From 60d1b2c29363f464f506e1cf7ca492812fcaed44 Mon Sep 17 00:00:00 2001 From: Pablo Lezaeta Reyes Date: Wed, 27 Sep 2017 00:10:03 -0300 Subject: [PATCH] Initial import from https://www.mars.org/home/rob/proj/hfs/ Signed-off-by: Pablo Lezaeta Reyes --- BLURB | 47 + CHANGES | 482 ++++++++ COPYING | 339 ++++++ COPYRIGHT | 21 + CREDITS | 49 + INSTALL | 183 +++ Makefile.in | 317 ++++++ README | 132 +++ TODO | 79 ++ acconfig.h | 53 + binhex.c | 616 ++++++++++ binhex.h | 32 + charset.c | 285 +++++ charset.h | 27 + config.h.in | 93 ++ configure | 2116 +++++++++++++++++++++++++++++++++++ configure.in | 301 +++++ copyin.c | 769 +++++++++++++ copyin.h | 29 + copyout.c | 652 +++++++++++ copyout.h | 29 + crc.c | 98 ++ crc.h | 25 + darray.c | 141 +++ darray.h | 30 + dlist.c | 119 ++ dlist.h | 33 + doc/charset.txt | 249 +++++ doc/libhfs.txt | 517 +++++++++ doc/librsrc.txt | 119 ++ doc/man/hattrib.1 | 46 + doc/man/hcd.1 | 31 + doc/man/hcopy.1 | 64 ++ doc/man/hdel.1 | 17 + doc/man/hdir.1 | 21 + doc/man/hformat.1 | 94 ++ doc/man/hfs.1 | 111 ++ doc/man/hfssh.1 | 424 +++++++ doc/man/hfsutils.1 | 109 ++ doc/man/hls.1 | 119 ++ doc/man/hmkdir.1 | 17 + doc/man/hmount.1 | 64 ++ doc/man/hpwd.1 | 15 + doc/man/hrename.1 | 27 + doc/man/hrmdir.1 | 17 + doc/man/humount.1 | 24 + doc/man/hvol.1 | 30 + doc/man/xhfs.1 | 47 + dstring.c | 124 ++ dstring.h | 34 + glob.c | 359 ++++++ glob.h | 22 + hattrib.c | 231 ++++ hattrib.h | 22 + hcd.c | 115 ++ hcd.h | 22 + hcopy.c | 318 ++++++ hcopy.h | 22 + hcwd.c | 371 ++++++ hcwd.h | 37 + hdel.c | 75 ++ hdel.h | 22 + hdisk.pl | 64 ++ hformat.c | 190 ++++ hformat.h | 22 + hfs.tcl | 491 ++++++++ hfsck/Makefile.in | 107 ++ hfsck/ck_btree.c | 96 ++ hfsck/ck_btree.h | 22 + hfsck/ck_mdb.c | 234 ++++ hfsck/ck_mdb.h | 22 + hfsck/ck_volume.c | 37 + hfsck/ck_volume.h | 22 + hfsck/hfsck.c | 34 + hfsck/hfsck.h | 42 + hfsck/main.c | 220 ++++ hfsck/util.c | 179 +++ hfsck/util.h | 26 + hfssh.c | 64 ++ hfsutil.c | 401 +++++++ hfsutil.h | 39 + hfswish.c | 136 +++ hls.c | 1029 +++++++++++++++++ hls.h | 22 + hmkdir.c | 74 ++ hmkdir.h | 22 + hmount.c | 112 ++ hmount.h | 22 + hpwd.c | 61 + hpwd.h | 22 + hrename.c | 119 ++ hrename.h | 22 + hrmdir.c | 74 ++ hrmdir.h | 22 + humount.c | 74 ++ humount.h | 22 + hvol.c | 134 +++ hvol.h | 22 + images.h | 54 + images/application.xbm | 14 + images/application_mask.xbm | 14 + images/caution.xbm | 14 + images/cdrom.xbm | 14 + images/cdrom_mask.xbm | 14 + images/document.xbm | 14 + images/document_mask.xbm | 14 + images/floppy.xbm | 14 + images/floppy_mask.xbm | 14 + images/folder.xbm | 12 + images/folder_mask.xbm | 12 + images/harddisk.xbm | 7 + images/harddisk_mask.xbm | 7 + images/help.xbm | 6 + images/macdaemon.xbm | 16 + images/macdaemon_mask.xbm | 16 + images/note.xbm | 14 + images/padlock.xbm | 4 + images/sm_application.xbm | 6 + images/sm_cdrom.xbm | 6 + images/sm_document.xbm | 6 + images/sm_floppy.xbm | 6 + images/sm_folder.xbm | 6 + images/sm_harddisk.xbm | 4 + images/stop.xbm | 14 + install-sh | 250 +++++ libhfs/.stamp/config.h | 0 libhfs/.stamp/config.h.in | 0 libhfs/.stamp/configure | 0 libhfs/Makefile.in | 143 +++ libhfs/acconfig.h | 38 + libhfs/apple.h | 272 +++++ libhfs/block.c | 807 +++++++++++++ libhfs/block.h | 40 + libhfs/btree.c | 700 ++++++++++++ libhfs/btree.h | 33 + libhfs/config.h.in | 57 + libhfs/configure | 1719 ++++++++++++++++++++++++++++ libhfs/configure.in | 63 ++ libhfs/data.c | 485 ++++++++ libhfs/data.h | 58 + libhfs/file.c | 520 +++++++++ libhfs/file.h | 45 + libhfs/hfs.c | 1848 ++++++++++++++++++++++++++++++ libhfs/hfs.h | 180 +++ libhfs/libhfs.h | 226 ++++ libhfs/low.c | 470 ++++++++ libhfs/low.h | 44 + libhfs/medium.c | 318 ++++++ libhfs/medium.h | 42 + libhfs/memcmp.c | 50 + libhfs/node.c | 473 ++++++++ libhfs/node.h | 34 + libhfs/os.h | 29 + libhfs/os/unix.c | 199 ++++ libhfs/record.c | 557 +++++++++ libhfs/record.h | 47 + libhfs/version.c | 29 + libhfs/version.h | 26 + libhfs/volume.c | 1202 ++++++++++++++++++++ libhfs/volume.h | 62 + librsrc/.stamp/config.h | 0 librsrc/.stamp/config.h.in | 0 librsrc/.stamp/configure | 0 librsrc/Makefile.in | 127 +++ librsrc/acconfig.h | 34 + librsrc/config.h.in | 53 + librsrc/configure | 1662 +++++++++++++++++++++++++++ librsrc/configure.in | 62 + librsrc/data.c | 467 ++++++++ librsrc/data.h | 58 + librsrc/librsrc.h | 101 ++ librsrc/main.c | 112 ++ librsrc/rsrc.c | 494 ++++++++ librsrc/rsrc.h | 58 + librsrc/version.c | 29 + librsrc/version.h | 26 + linux/Makefile.in | 107 ++ linux/fsck.c | 25 + linux/hdump.c | 147 +++ linux/kernel/Makefile | 65 ++ linux/kernel/module.c | 100 ++ linux/mkfs.c | 25 + strdup.c | 42 + strerror.c | 74 ++ strstr.c | 56 + strtol.c | 67 ++ suid.c | 100 ++ suid.h | 24 + tclhfs.c | 1485 ++++++++++++++++++++++++ tclhfs.h | 22 + test/Makefile | 36 + test/main.tcl | 43 + test/test1.tcl | 119 ++ test/test2.tcl | 74 ++ test/test3.tcl | 13 + version.c | 44 + version.h | 28 + xhfs.h | 22 + xhfs.tcl | 1926 +++++++++++++++++++++++++++++++ 199 files changed, 34649 insertions(+) create mode 100644 BLURB create mode 100644 CHANGES create mode 100644 COPYING create mode 100644 COPYRIGHT create mode 100644 CREDITS create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 README create mode 100644 TODO create mode 100644 acconfig.h create mode 100644 binhex.c create mode 100644 binhex.h create mode 100644 charset.c create mode 100644 charset.h create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 copyin.c create mode 100644 copyin.h create mode 100644 copyout.c create mode 100644 copyout.h create mode 100644 crc.c create mode 100644 crc.h create mode 100644 darray.c create mode 100644 darray.h create mode 100644 dlist.c create mode 100644 dlist.h create mode 100644 doc/charset.txt create mode 100644 doc/libhfs.txt create mode 100644 doc/librsrc.txt create mode 100644 doc/man/hattrib.1 create mode 100644 doc/man/hcd.1 create mode 100644 doc/man/hcopy.1 create mode 100644 doc/man/hdel.1 create mode 100644 doc/man/hdir.1 create mode 100644 doc/man/hformat.1 create mode 100644 doc/man/hfs.1 create mode 100644 doc/man/hfssh.1 create mode 100644 doc/man/hfsutils.1 create mode 100644 doc/man/hls.1 create mode 100644 doc/man/hmkdir.1 create mode 100644 doc/man/hmount.1 create mode 100644 doc/man/hpwd.1 create mode 100644 doc/man/hrename.1 create mode 100644 doc/man/hrmdir.1 create mode 100644 doc/man/humount.1 create mode 100644 doc/man/hvol.1 create mode 100644 doc/man/xhfs.1 create mode 100644 dstring.c create mode 100644 dstring.h create mode 100644 glob.c create mode 100644 glob.h create mode 100644 hattrib.c create mode 100644 hattrib.h create mode 100644 hcd.c create mode 100644 hcd.h create mode 100644 hcopy.c create mode 100644 hcopy.h create mode 100644 hcwd.c create mode 100644 hcwd.h create mode 100644 hdel.c create mode 100644 hdel.h create mode 100644 hdisk.pl create mode 100644 hformat.c create mode 100644 hformat.h create mode 100644 hfs.tcl create mode 100644 hfsck/Makefile.in create mode 100644 hfsck/ck_btree.c create mode 100644 hfsck/ck_btree.h create mode 100644 hfsck/ck_mdb.c create mode 100644 hfsck/ck_mdb.h create mode 100644 hfsck/ck_volume.c create mode 100644 hfsck/ck_volume.h create mode 100644 hfsck/hfsck.c create mode 100644 hfsck/hfsck.h create mode 100644 hfsck/main.c create mode 100644 hfsck/util.c create mode 100644 hfsck/util.h create mode 100644 hfssh.c create mode 100644 hfsutil.c create mode 100644 hfsutil.h create mode 100644 hfswish.c create mode 100644 hls.c create mode 100644 hls.h create mode 100644 hmkdir.c create mode 100644 hmkdir.h create mode 100644 hmount.c create mode 100644 hmount.h create mode 100644 hpwd.c create mode 100644 hpwd.h create mode 100644 hrename.c create mode 100644 hrename.h create mode 100644 hrmdir.c create mode 100644 hrmdir.h create mode 100644 humount.c create mode 100644 humount.h create mode 100644 hvol.c create mode 100644 hvol.h create mode 100644 images.h create mode 100644 images/application.xbm create mode 100644 images/application_mask.xbm create mode 100644 images/caution.xbm create mode 100644 images/cdrom.xbm create mode 100644 images/cdrom_mask.xbm create mode 100644 images/document.xbm create mode 100644 images/document_mask.xbm create mode 100644 images/floppy.xbm create mode 100644 images/floppy_mask.xbm create mode 100644 images/folder.xbm create mode 100644 images/folder_mask.xbm create mode 100644 images/harddisk.xbm create mode 100644 images/harddisk_mask.xbm create mode 100644 images/help.xbm create mode 100644 images/macdaemon.xbm create mode 100644 images/macdaemon_mask.xbm create mode 100644 images/note.xbm create mode 100644 images/padlock.xbm create mode 100644 images/sm_application.xbm create mode 100644 images/sm_cdrom.xbm create mode 100644 images/sm_document.xbm create mode 100644 images/sm_floppy.xbm create mode 100644 images/sm_folder.xbm create mode 100644 images/sm_harddisk.xbm create mode 100644 images/stop.xbm create mode 100755 install-sh create mode 100644 libhfs/.stamp/config.h create mode 100644 libhfs/.stamp/config.h.in create mode 100644 libhfs/.stamp/configure create mode 100644 libhfs/Makefile.in create mode 100644 libhfs/acconfig.h create mode 100644 libhfs/apple.h create mode 100644 libhfs/block.c create mode 100644 libhfs/block.h create mode 100644 libhfs/btree.c create mode 100644 libhfs/btree.h create mode 100644 libhfs/config.h.in create mode 100755 libhfs/configure create mode 100644 libhfs/configure.in create mode 100644 libhfs/data.c create mode 100644 libhfs/data.h create mode 100644 libhfs/file.c create mode 100644 libhfs/file.h create mode 100644 libhfs/hfs.c create mode 100644 libhfs/hfs.h create mode 100644 libhfs/libhfs.h create mode 100644 libhfs/low.c create mode 100644 libhfs/low.h create mode 100644 libhfs/medium.c create mode 100644 libhfs/medium.h create mode 100644 libhfs/memcmp.c create mode 100644 libhfs/node.c create mode 100644 libhfs/node.h create mode 100644 libhfs/os.h create mode 100644 libhfs/os/unix.c create mode 100644 libhfs/record.c create mode 100644 libhfs/record.h create mode 100644 libhfs/version.c create mode 100644 libhfs/version.h create mode 100644 libhfs/volume.c create mode 100644 libhfs/volume.h create mode 100644 librsrc/.stamp/config.h create mode 100644 librsrc/.stamp/config.h.in create mode 100644 librsrc/.stamp/configure create mode 100644 librsrc/Makefile.in create mode 100644 librsrc/acconfig.h create mode 100644 librsrc/config.h.in create mode 100755 librsrc/configure create mode 100644 librsrc/configure.in create mode 100644 librsrc/data.c create mode 100644 librsrc/data.h create mode 100644 librsrc/librsrc.h create mode 100644 librsrc/main.c create mode 100644 librsrc/rsrc.c create mode 100644 librsrc/rsrc.h create mode 100644 librsrc/version.c create mode 100644 librsrc/version.h create mode 100644 linux/Makefile.in create mode 100644 linux/fsck.c create mode 100644 linux/hdump.c create mode 100644 linux/kernel/Makefile create mode 100644 linux/kernel/module.c create mode 100644 linux/mkfs.c create mode 100644 strdup.c create mode 100644 strerror.c create mode 100644 strstr.c create mode 100644 strtol.c create mode 100644 suid.c create mode 100644 suid.h create mode 100644 tclhfs.c create mode 100644 tclhfs.h create mode 100644 test/Makefile create mode 100644 test/main.tcl create mode 100644 test/test1.tcl create mode 100644 test/test2.tcl create mode 100644 test/test3.tcl create mode 100644 version.c create mode 100644 version.h create mode 100644 xhfs.h create mode 100644 xhfs.tcl diff --git a/BLURB b/BLURB new file mode 100644 index 0000000..6a9b701 --- /dev/null +++ b/BLURB @@ -0,0 +1,47 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: BLURB,v 1.1 1998/04/11 08:26:52 rob Exp $ + +=============================================================================== + +What is this? + + HFS is the "Hierarchical File System" used on modern Macintosh computers. + With this package, you can read and write Macintosh-formatted media such as + floppy disks, CD-ROMs, and SCSI hard disks on most UNIX platforms. You can + also format raw media into an HFS volume. + + This package contains a number of different tools: + + * Several command-line programs (hmount, hls, hcopy, et al.) + * A Tk-based front-end for browsing and copying files through a + variety of transfer modes (MacBinary, BinHex, text, etc.) + * A Tcl package and interface for scriptable access to volumes + * A C library for low-level access to volumes + +How is this different from other HFS packages? + + This package attempts to provide a portable, free implementation of + routines for accessing HFS volumes. It is implemented as a set of user-level + tools to provide maximum portability. + + The command-line programs are intended to be used in the same vein as the + Mtools command-line programs used to access MS-DOS filesystems. + + The graphical front-end provides point-and-click access to Macintosh + volumes, which is often more convenient than the command line. + + The Tcl interface offers a scriptable HFS "shell" that is more efficient + than the external command-line programs and allows for greater + extensibility. + + The C library can be linked with other programs wanting to manipulate + Macintosh files in their native format. For example, an implementation of + the Macintosh Resource Manager could be built on top of this library to + provide seamless access to data objects contained within the resource forks + of Macintosh files. + +=============================================================================== + diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..5b2623f --- /dev/null +++ b/CHANGES @@ -0,0 +1,482 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: CHANGES,v 1.14 1998/11/02 22:08:20 rob Exp $ + +=============================================================================== + +Version 3.2.6 + + * Added casts and made other changes to satisfy fussy signed/unsigned + compilers. + + * Fixed a problem in which volumes were not marked as "cleanly unmounted," + causing unnecessary scavenging to occur when the volume is mounted again. + + * Fixed a problem where scavenging failed to mark all extents from the final + B*-tree node. + + * Added a simple next-CNID verification to scavenging. + + * Introduced a flags parameter to hfs_format(); new HFS_OPT_2048 format + option ensures 2048-byte physical-block file boundaries, compatible with + hybrid ISO 9660 schemes. + + * New HFS_OPT_NOCACHE mount option inhibits use of libhfs' internal block + cache, useful for RAM file systems or systems/circumstances where the + cache may decrease performance. + + * New HFS_OPT_ZERO format/mount option forces all allocated blocks to be + zero-initialized before use, for systems/circumstances in which blocks + may otherwise contain random data. (This is primarily a security feature + with a performance cost.) + +Version 3.2.5 + + * Miscellaneous internal `const' and other minor changes. + +Version 3.2.4 + + * Fixed a problem with the handling of catalog and extents clump sizes + (again; first fixed in 1.15b but re-introduced in 3.0b1, alas.) + + * Modified `configure' once again to search for tcl.h/tk.h header files. + + * Changed the OS interface to operate on full block rather than byte + offsets. + + * Moved the 800K size check from hfs_format() into v_geometry(). + +Version 3.2.3 + + * Fixed a problem with the B*-tree node splitting code. This could have + caused the libhfs routines to crash under some rare circumstances. + +Version 3.2.2 + + * Added support for "blessing" the MacOS System Folder by means of a new + field in the hfsvolent struct. A new option to `hattrib' can be used to + set this field, as can a new "bless" Tcl volume command. + + * Modified `configure' to use existing Tcl/Tk configuration information + rather than reconstruct it. + +Version 3.2.1 + + * Fixed a minor potential problem where BinHex translation might fail to + recognize the hqx header. + +Version 3.2 + + * Fixed a problem determining medium sizes under unusual conditions. + + * Modified MDB `drVCSize', `drVBMCSize', and `drCtlCSize' to new + Sequoia-supporting `drEmbedSigWord' and `drEmbedExtent'. + +Version 3.1.1 + + * Fixed a problem related to partition locations on large media. + + * Fixed a problem with suid.c on some systems. + + * Fixed a problem with font selection in `xhfs'. + + * Changed copyouts not to append `.txt' to text files if the filename + already contains a period. + + * Fixed a few other minor problems. + +Version 3.1 + + * `hmount' and friends now properly handle relative pathnames, even if + the current directory changes. + + * Enhanced API for managing partitions: hfs_zero() will create a new, + empty partition map on any medium. hfs_mkpart() will create a new HFS + partition (if space allows) in the map. hfs_nparts() will return the + number of HFS partitions currently existing on the medium. + + * hfs_mount() and hfs_format() are now more strict with the partition + number passed to them; previously this number was ignored if the medium + was not partitioned. Now the partition _must_ exist, or else 0 can be + passed to ignore any partitions and select the entire medium. + + * Changed the API for hfs_create(); this routine now returns an open file + reference upon successful creation. + + * Added bad block sparing to hfs_format(). This routine now requires two + additional arguments specifying a list of block numbers which are + defective on the physical medium and should be mapped out of use. + + * Character set translation is now performed as well as end-of-line + translation in the "text" transfer mode. The routines convert MacOS + Standard Roman to equivalent (or near-equivalent) Latin-1 (ISO 8859-1) + sequences and vice versa. It should be noted however that these + translations are NOT reversible for all characters. + + * Fixed a bug in xhfs.tcl which failed to include the helper `ctime' + procedure (fixed in hfs.tcl) after it was removed from tclhfs.c. + + * Further librsrc development. + +Version 3.0b2 + + * Changed the block cache mechanism so that consecutive blocks are read + and written in single chunks, significantly improving performance. + + * Improved the efficiency of the internal routines for searching B*-trees + by reducing the number of times the record keys are unpacked from their + external format. + +Version 3.0b1 + + * Package is now configured automatically via GNU `configure' script. + + * Numerous internal changes to support automatic configuration and improve + portability, as well as reduce code size. + + * Bug fix: the alternate MDB was sometimes written to the wrong location + on non-partitioned media. + + * Bug fix: the Extents Overflow file could become corrupted under some + circumstances in which a heavily fragmented file is removed. + + * Bug fix: it was possible for the volume allocation routine to become + caught in an infinite loop. + + * Significant librsrc development. The library can now read and return + resources to an application, but cannot create or modify them. The + library can be used with or without libhfs. + + * hfs_mount() now honors the software volume lock bit and makes the volume + read-only accordingly. + + * Added a new hfs_vsetattr() routine to allow some volume attributes to + be modified. + + * Added support for partition data blocks that do not start at the + beginning of a partition. + +Version 2.1 + + * Further hfsck development. + + * Physical blocks from the volume are now cached by the library. This + should dramatically improve performance of the programs on some systems. + + * Modified the hfsvolent and hfsdirent structures to include several new + fields. The hfs_vstat(), hfs_stat(), and hfs_setattr() functions now + manipulate the new fields. Note that hfsdirent now includes a separate + union structure for files and directories. + + * Fixed a problem on some systems with `xhfs' that caused it to terminate + with the message `Error: can't read "data": no such variable'. The + problem was caused by the inability to parse the system's `df' command + output. + +Version 2.0 + + * Renamed hfs_fork() to hfs_setfork(), and added hfs_getfork(). + + * Modified all programs to be setuid-aware. Setuid privileges are only + engaged when opening devices as HFS volumes; this can be useful to limit + access to and/or help protect the structural integrity of HFS volumes, + for example by installing the hfsutils programs setgid. N.B. This + implementation may not be completely portable, and is isolated to the + new source file `./suid.c'. + + * First non-beta release. + +Version 1.19b + + * Fixed a problem which prevented large volumes from being properly + formatted by hfs_format(). This should also allow the library to + correctly manipulate large volumes in certain other cases. + + * Adapted the code to work with Tcl 7.6 and Tk 4.2. + + * Added an HFS globbing interface to Tcl, and extended the `hfs' shell + to use it. + + * Modified globbing routine to ignore Finder-invisible files. + + * Rewrote `hls' and `hdir'; many UNIX-like options are now available. + + * Updated the general documentation. + + * Continued development of `hfsck'. + +Version 1.18b + + * Resolved many signed/unsigned argument passing conflicts throughout the + code. It should now compile cleanly even on very strict systems. + + * Changed the interface for hfs_read() and hfs_write() to accept a void * + buffer pointer rather than demanding char *. + + * Made volume file locking optional for systems that don't support it. + + * Included a few other portability fixes for AIX and BeOS. + +Version 1.17b + + * Completed the implementation for `hattrib'; HFS file attributes (type, + creator, invisible/locked) can now be changed from the command line. + + * Fixed an off-by-one error in computing catalog record key lengths. + + * Fixed a problem with backslash-quoted braces during globbing. + + * Improved Makefile handling. + + * Regularized the handling of the volume-unmounted flag. + + * Streamlined the internal and external interface header files. + + * Added a `parid' field to the hfsdirent structure. + + * Updated libhfs documentation. + + * Implemented a better error message reporting format involving pathnames. + +Version 1.16b + + * Improved the robustness of the HFS path resolution routine. + + * Fixed hfs_rename() to better detect the validity of destination paths. + + * Changed mount-time scavenging only to mark bits in the volume bitmap, + not clear them. This reduces the risk of causing further damage to a + corrupt disk, leaving serious recovery to more advanced tools. + + * Added file locking to hfs_mount() to prevent concurrent processes from + potentially creating inconsistencies in a volume. + + * Added a new argument to hfs_mount() for specifying mount flags. This now + allows a volume to be explicitly mounted read-only, read-write, or don't + care (whichever is available). + + * Improved detection and handling of multiple same-device mounts by a single + process. + + * Improved error message reporting for all programs. + + * Fixed a problem where hrenaming a volume caused it to become inaccessible + until it was hmounted again. + +Version 1.15b + + * Increased buffer size for native HFS-to-HFS copies to increase + performance. + + * Fixed a problem verifying the existence of directory threads. + + * Fixed a problem with the handling of catalog and extents clump sizes. + +Version 1.14b + + * The command-line programs now support filename globbing; see hfsutils(1) + for syntax details. + + * File threads are now managed properly when the associated file is deleted, + moved, or renamed. However, there is no mechanism yet to explicitly create + or remove threads, or to use them in catalog searches. + + * The volume bitmap is now reconstructed from the catalog and extents files + when a volume is mounted that was not previously cleanly unmounted. + + * Directories may now be deleted recursively using `xhfs'. + + * BinHex and MacBinary header fork size validation limitations have been + relaxed. (MacBinary spec suggested 0x007fffff ~= 8MB as maximum length?) + + * Volumes are now flushed automatically by `xhfs' every 30 seconds to + reduce risk of accidental data loss. + + * Begun work on `hfsck', a program to verify and correct HFS volume + consistency. + + * Begun work on `librsrc', a library for manipulating the resource forks + of HFS files. + +Version 1.13b + + * Fixed two bad extent record key sorting bugs; these could have caused + serious problems on certain fragmented volumes. + + * Fixed a problem related to MacOS default file clump size semantics. + + * Implemented recursive directory copies in `xhfs'. + + * Files and directories may now be renamed with a mere change of case. + + * Multiple files may now be moved with `hrename'. + + * A UNIX pathname of "-" in `hcopy' may now be used to mean stdin or stdout + when used as the source or destination target, respectively. + +Version 1.12b + + * Fix to allow mounting of some miscreant partitioned media. + + * Increased copyin/copyout buffer sizes for better performance. + + * Implemented -a for `hcopy' (automatic transfer mode selection.) + + * HFS timestamps are now relative to the current time zone. + +Version 1.11b + + * UNIX-to-HFS BinHex transfers have been implemented. All transfer modes + are now fully implemented. + + * The overall BinHex implementation now uses the stdio library for + somewhat better performance. + + * New libhfs routines: hfs_setattr(), hfs_fsetattr() for changing file + and directory attributes (timestamps, type/creator, flags). + + * The copyin routines now update the relevant file information (timestamps, + flags) for MacBinary and BinHex transfers. + + * The `hvol' command now provides better output. + + * Modified the installation procedure; separate targets now build and + install the separate components of the package. + + * Fixed a bug in HFS filename sorting which affects the search routines; + filenames are now sorted precisely according to MacOS idiosyncrasies. + + * Begun work on (but still unimplemented): hattrib, filename globbing. + +Version 1.10b + + * Fixed a serious bug in directory record updates; directories with names + longer than 21 characters could get corrupted. + + * Implemented hfs_rename() and eliminated hfs_move(); functionality for + the latter has been included in the former. + + * Added an `hrename' command. Minor changes to hcwd.c to cope with + volume name or cwd path changes. + + * Minor changes to `xhfs'. Files can now be renamed with the interface. + +Version 1.9b + + * Modularized the libhfs code. + + * Resolved more portability issues. The code should now work regardless of + the size or endianness of the host's datatypes. + +Version 1.8.1b + + * Fixed some portability bugs. + +Version 1.8b + + * More UI enhancements to `xhfs'. + + * UNIX-to-HFS MacBinary II transfers have been implemented; HFS-to-HFS + copies have been implemented. Only one transfer mode (UNIX-to-HFS BinHex) + remains unimplemented. + + * Replaced `hcat' with a generic `hcopy'. All transfer modes are supported. + (But note: The -a option is not yet supported.) + + * Fixed a bug in the MacBinary II encoding CRC algorithm. + + * Tcl hash tables are now used to maintain open files and volumes. + + * Cleaned up some of the Tcl/Tk interface to aid compatibility with + various versions. + +Version 1.7b + + * Many more UI enhancements to `xhfs'. + + * Files can now be copied from UNIX to HFS volumes with `xhfs' using either + Text or Raw mode translation. + +Version 1.6b + + * Fixed a bug in the catalog manipulation routines which would allow files + and directories to be created at the same level as the root folder. + + * Many UI enhancements to `xhfs'. Some minor enhancements to `hfs'. + + * hfs_islocked() now returns 1 if a volume is locked (read-only). + + * Extended file attributes to recognize invisible files. + +Version 1.5b + + * All cases of B*-tree record insertion have been completed. + + * B*-tree record deletion has been implemented. Files and folders may now + be created and deleted with no limitations (other than the size of the + volume.) + + * New commands: hrmdir, hdel + + * File writing and truncation have been implemented. + + * The library now maintains its own concept of "current volume". The last + volume to be mounted is by default current; the new functions + hfs_getvol() and hfs_setvol() can be used to get and set the current + volume, respectively. All other functions will now accept a null volume + pointer to represent the current volume. + + * Absolute pathnames may now refer to (mounted) volumes other than the one + specified in the call; in this event the specified volume is ignored. + +Version 1.4b + + * Updated the installation procedure. The Tcl and Tk programs can now be + built and installed optionally. + + * Fixed a minor bug which would cause the header node to be read just + before completing a directory read. + + * Mounting the same volume more than once now returns a reference to the + same volume, and does the right thing when each reference is + independently unmounted. + + * Cleaned up some of the Tcl interface to deal with multiply-referenced + volumes. `xhfs' can now deal with the same volume on both sides. + + * Renamed hfs_cwdid() to hfs_getcwd(), and added hfs_setcwd(). + + * hfs_umount() now closes all open files and directories on the volume + before unmounting it. All external references to the volume and any + open files/directories will become invalid. + + * Enabled hfs_umountall() which will effectively unmount all volumes, + invalidating all external volume and file references. This is a + catch-all that should be called before a program exits. N.B.: Errors + during volume flushing and unmounting are not reported. + +Version 1.3b + + * Enhanced writability: B*-tree file growth has been implemented. New + directories can be created until the disk gets full. + + * Added an option to `xhfs' for creating new directories. + +Version 1.2b + + * Enhanced writability: B*-tree node splitting has been implemented. New + directories can be created until the catalog file gets full. + + * Fixed some minor bugs in version 1.1b. + +Version 1.1b + + * Full read-only capability. + + * Limited write capability: new directories can be created only until + a catalog node gets full. + + * New volumes can be created with hfs_format(). + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..e77696a --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..ab83ace --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,21 @@ + + hfsutils - tools for reading and writing Macintosh HFS volumes + Copyright (C) 1996-1998 Robert Leslie + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + If you would like to negotiate alternate licensing terms, you may do + so by contacting the author: Robert Leslie + diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..9b40b1c --- /dev/null +++ b/CREDITS @@ -0,0 +1,49 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: CREDITS,v 1.11 1998/09/17 00:11:44 rob Exp $ + +=============================================================================== + +Author: Robert Leslie + +The HFS globbing code was inspired by similar code to perform filename +globbing in Tcl by John Ousterhout. + +The code to perform BinHex encoding/decoding is based on the definition of +BinHex 4.0 as written by Peter N Lewis. + +The code for `hls' is loosely based on the GNU implementation of `ls'. + +Many thanks to the following people for their support and various +contributions to this project: + + Howard Bergstrom + Marcus Better + Cees de Groot + Pat Dirks + Andy Fyfe + Paul H. Hargrove + George Hoffman + Jack Howarth + Geoff Hulten + Richard C.S. Kinne + Marc Lebas + Zach Leber + Clifford T. Matthews + Markus Mayer + Allen Pouratian + Steve Revilak + Andrew Ross + Nick Stephen + Ray Van Tassle + Thomas B. White + John Witford + +And thanks of course to the engineers of the truly fascinating HFS filesystem +for giving me a challenge. May your extents overflow file never become so +fragmented that you cannot locate its own extents. + +=============================================================================== + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..50dbe43 --- /dev/null +++ b/INSTALL @@ -0,0 +1,183 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..27c2bfd --- /dev/null +++ b/Makefile.in @@ -0,0 +1,317 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile.in,v 1.12 1998/08/31 18:40:14 rob Exp $ +# + +@SET_MAKE@ +src = @srcdir@ +VPATH = @srcdir@ + +### USER CUSTOMIZATIONS FOLLOW ################################################ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +BINDEST = @bindir@ +MANDEST = @mandir@ + +LIBDEST = @libdir@ +INCDEST = @includedir@ + +MANEXT = 1 + +INSTALL = @INSTALL@ +BININSTALL = @INSTALL_PROGRAM@ -m 755 +LIBINSTALL = @INSTALL_DATA@ +HARDLINK = ln -f + +CC = @CC@ +INCLUDES = @CPPFLAGS@ -Ilibhfs @TCL_INCLUDES@ @TK_INCLUDES@ +DEFINES = @DEFS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ + +TCLLIBS = @TCL_LIB_SPEC@ @TCL_LIBS@ +TKLIBS = @TK_LIB_SPEC@ @TCL_LIB_SPEC@ @TK_LIBS@ + +COPTS = @CFLAGS@ +LDOPTS = @LDFLAGS@ -Llibhfs + +### END OF USER CUSTOMIZATION ################################################# + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(CLITARGETS) $(TCLTARGETS) $(TKTARGETS) + +CLITARGETS = hattrib hcd hcopy hdel hdir hformat hls hmkdir hmount hpwd \ + hrename hrmdir humount hvol +TCLTARGETS = hfssh hfs +TKTARGETS = xhfs + +LIBHFS = libhfs/libhfs.a +INCHFS = libhfs/hfs.h + +LIBRSRC = librsrc/librsrc.a +INCRSRC = librsrc/rsrc.h + +HFSUTIL = hfsutil +HFSCK = hfsck/hfsck +LINUX = linux/mkfs.hfs linux/fsck.hfs linux/kernel/hfs.o + +SUBDIRS = hfsck linux test +ACSUBDIRS = @subdirs@ + +GENERALDOCS = hfsutils.1 +CLIDOCS = hattrib.1 hcd.1 hcopy.1 hdel.1 hdir.1 hformat.1 hls.1 \ + hmkdir.1 hmount.1 hpwd.1 hrename.1 hrmdir.1 humount.1 hvol.1 +TCLDOCS = hfssh.1 hfs.1 +TKDOCS = xhfs.1 + +CLIOBJS = hattrib.o hcd.o hcopy.o hdel.o hformat.o hls.o hmkdir.o \ + hmount.o hpwd.o hrename.o hrmdir.o humount.o hvol.o +UTILOBJS = crc.o binhex.o copyin.o copyout.o charset.o \ + darray.o dlist.o dstring.o glob.o suid.o version.o + +############################################################################### + +all :: @MAKE_TARGETS@ + +all_cli :: $(CLITARGETS) +all_tcl :: $(TCLTARGETS) +all_tk :: $(TKTARGETS) +all_lib :: $(LIBHFS) $(LIBRSRC) + +check :: all + @if [ -f hfs ]; then \ + cd test && $(MAKE) && \ + echo "Self-tests passed."; \ + else \ + echo "Self-tests only available if configured --with-tcl."; \ + fi + +install :: @INSTALL_TARGETS@ + +install_cli :: all_cli + $(BININSTALL) $(HFSUTIL) "$(BINDEST)/." + + for file in $(CLITARGETS); do \ + $(HARDLINK) "$(BINDEST)/$(HFSUTIL)" "$(BINDEST)/$$file"; \ + done + + rm -f "$(BINDEST)/$(HFSUTIL)" + + for file in $(GENERALDOCS) $(CLIDOCS); do \ + $(LIBINSTALL) doc/man/$$file \ + "$(MANDEST)/man$(MANEXT)/`basename $$file .1`.$(MANEXT)"; \ + done + +install_tcl :: all_tcl + for file in $(TCLTARGETS); do \ + $(BININSTALL) $$file "$(BINDEST)/."; \ + done + + if [ -f "$(BINDEST)/hfs" ]; then \ + sed -e '1d' "$(BINDEST)/hfs" > "$(BINDEST)/hfs.new"; \ + $(BININSTALL) "$(BINDEST)/hfs.new" "$(BINDEST)/hfs"; \ + rm -f "$(BINDEST)/hfs.new"; \ + fi + + for file in $(TCLDOCS); do \ + $(LIBINSTALL) doc/man/$$file \ + "$(MANDEST)/man$(MANEXT)/`basename $$file .1`.$(MANEXT)"; \ + done + +install_tk :: all_tk + for file in $(TKTARGETS); do \ + $(BININSTALL) $$file "$(BINDEST)/."; \ + done + + for file in $(TKDOCS); do \ + $(LIBINSTALL) doc/man/$$file \ + "$(MANDEST)/man$(MANEXT)/`basename $$file .1`.$(MANEXT)"; \ + done + +install_lib :: all_lib + cd libhfs && $(MAKE) install + cd librsrc && $(MAKE) install + +again :: clean all + +depend :: + for dir in $(ACSUBDIRS) $(SUBDIRS); do \ + (cd $$dir && $(MAKE) depend); \ + done + + ( sed -n '1,/^### DEPEND/p' Makefile.in; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.in.new + mv -f Makefile.in.new Makefile.in + +clean :: + for dir in $(ACSUBDIRS) $(SUBDIRS); do \ + (cd $$dir && $(MAKE) clean); \ + done + + rm -f $(TARGETS) *.o gmon.* core + rm -f $(HFSUTIL) xhfs.c + +distclean :: clean + for dir in $(ACSUBDIRS); do \ + (cd $$dir && $(MAKE) distclean); \ + done + + rm -f config.status config.cache config.log config.h + rm -f hfsck/Makefile linux/Makefile Makefile + +maintainer-clean :: distclean + for dir in . $(ACSUBDIRS); do \ + rm -f $$dir/config.h.in $$dir/configure; \ + done + +dist :: + for dir in . $(ACSUBDIRS); do \ + (cd $$dir && $(MAKE) config.h.in configure); \ + done + + $(MAKE) distclean + +############################################################################### + +Makefile: config.status + ./config.status && touch .stamp/config.h + +.stamp/config.h: config.status + ./config.status && touch .stamp/config.h + +config.status: .stamp/configure .stamp/config.h.in Makefile.in + ./config.status --recheck + +.stamp/configure: configure.in + autoconf && touch .stamp/configure + +.stamp/config.h.in: configure.in acconfig.h + autoheader && touch .stamp/config.h.in + +############################################################################### + +$(LIBHFS) :: + cd libhfs && $(MAKE) + +$(LIBRSRC) :: + cd librsrc && $(MAKE) + +$(HFSCK) :: $(LIBHFS) + cd hfsck && $(MAKE) + +$(LINUX) :: $(LIBHFS) + cd linux && $(MAKE) + +$(HFSUTIL): $(LIBHFS) hfsutil.o hcwd.o $(CLIOBJS) $(UTILOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) hfsutil.o hcwd.o \ + $(CLIOBJS) $(UTILOBJS) \ + -lhfs $(LIBS) $(LIBOBJS) -o $@ + +$(CLITARGETS): $(HFSUTIL) + -$(HARDLINK) $(HFSUTIL) $@ + +hfssh: $(LIBHFS) hfssh.o tclhfs.o $(UTILOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) hfssh.o tclhfs.o $(UTILOBJS) \ + -lhfs $(LIBS) $(TCLLIBS) $(LIBOBJS) -o $@ + +hfs: hfs.tcl hfssh + ( echo "#!./hfssh"; \ + echo "#! /bin/sh"; \ + echo "# Start hfssh ... \\"; \ + echo 'exec hfssh "$$0" "$$@"'; \ + echo; \ + cat hfs.tcl; \ + ) > $@ + chmod a+x $@ + +xhfs.c: xhfs.tcl + ( echo '# include "xhfs.h"'; \ + echo; \ + echo "char xhfs[] ="; \ + sed -e 's|^[ ]*||' -e 's|^#.*||' -e '/^$$/d' \ + -e 's| *| |g' \ + -e 's|[\\"]|\\&|g' -e 's|.*|"&\\n"|' xhfs.tcl; \ + echo ";"; \ + ) > $@ + +xhfs: $(LIBHFS) hfswish.o xhfs.o tclhfs.o $(UTILOBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) hfswish.o xhfs.o tclhfs.o $(UTILOBJS) \ + -lhfs $(LIBS) $(TKLIBS) $(LIBOBJS) -o $@ + +### DEPENDENCIES FOLLOW ####################################################### + +binhex.o: binhex.c config.h binhex.h crc.h +charset.o: charset.c config.h charset.h +copyin.o: copyin.c config.h libhfs/hfs.h libhfs/data.h copyin.h \ + charset.h binhex.h crc.h +copyout.o: copyout.c config.h libhfs/hfs.h libhfs/data.h copyout.h \ + charset.h binhex.h crc.h +crc.o: crc.c config.h crc.h +darray.o: darray.c config.h darray.h +dlist.o: dlist.c config.h dlist.h +dstring.o: dstring.c config.h dstring.h +glob.o: glob.c config.h dlist.h dstring.h libhfs/hfs.h glob.h +hattrib.o: hattrib.c config.h libhfs/hfs.h hcwd.h hfsutil.h hattrib.h +hcd.o: hcd.c config.h libhfs/hfs.h hcwd.h hfsutil.h hcd.h +hcopy.o: hcopy.c config.h libhfs/hfs.h hcwd.h hfsutil.h hcopy.h \ + copyin.h copyout.h +hcwd.o: hcwd.c config.h libhfs/hfs.h hcwd.h +hdel.o: hdel.c config.h libhfs/hfs.h hcwd.h hfsutil.h hdel.h +hformat.o: hformat.c config.h libhfs/hfs.h hcwd.h hfsutil.h suid.h \ + hformat.h +hfssh.o: hfssh.c config.h tclhfs.h suid.h +hfsutil.o: hfsutil.c config.h libhfs/hfs.h hcwd.h hfsutil.h suid.h \ + glob.h version.h hattrib.h hcd.h hcopy.h hdel.h hformat.h hls.h \ + hmkdir.h hmount.h hpwd.h hrename.h hrmdir.h humount.h hvol.h +hfswish.o: hfswish.c config.h tclhfs.h xhfs.h suid.h images.h \ + images/macdaemon.xbm images/macdaemon_mask.xbm images/stop.xbm \ + images/caution.xbm images/note.xbm images/floppy.xbm \ + images/harddisk.xbm images/cdrom.xbm images/floppy_mask.xbm \ + images/harddisk_mask.xbm images/cdrom_mask.xbm images/sm_floppy.xbm \ + images/sm_harddisk.xbm images/sm_cdrom.xbm images/folder.xbm \ + images/document.xbm images/application.xbm images/folder_mask.xbm \ + images/document_mask.xbm images/application_mask.xbm \ + images/sm_folder.xbm images/sm_document.xbm images/sm_application.xbm \ + images/help.xbm images/padlock.xbm +hls.o: hls.c config.h libhfs/hfs.h hcwd.h hfsutil.h darray.h dlist.h \ + dstring.h hls.h +hmkdir.o: hmkdir.c config.h libhfs/hfs.h hcwd.h hfsutil.h hmkdir.h +hmount.o: hmount.c config.h libhfs/hfs.h hcwd.h hfsutil.h suid.h \ + hmount.h +hpwd.o: hpwd.c config.h libhfs/hfs.h hcwd.h hfsutil.h hpwd.h +hrename.o: hrename.c config.h libhfs/hfs.h hcwd.h hfsutil.h hrename.h +hrmdir.o: hrmdir.c config.h libhfs/hfs.h hcwd.h hfsutil.h hrmdir.h +humount.o: humount.c config.h libhfs/hfs.h hcwd.h hfsutil.h humount.h +hvol.o: hvol.c config.h libhfs/hfs.h hcwd.h hfsutil.h hvol.h +strdup.o: strdup.c config.h +strerror.o: strerror.c config.h +strstr.o: strstr.c config.h +strtol.o: strtol.c config.h +suid.o: suid.c config.h suid.h +tclhfs.o: tclhfs.c config.h tclhfs.h libhfs/hfs.h glob.h copyin.h \ + copyout.h charset.h suid.h version.h +version.o: version.c version.h diff --git a/README b/README new file mode 100644 index 0000000..5af6098 --- /dev/null +++ b/README @@ -0,0 +1,132 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: README,v 1.10 1998/09/08 18:51:51 rob Exp $ + +=============================================================================== + +NOTES ON THIS RELEASE + + This release uses GNU autoconf to automatically configure the software + for installation and use on your system. This means you should not have + to modify any files by hand to get the software to compile -- if you do, + please let the author know so it can be fixed. + + For general installation instructions, please read the `INSTALL' file. + + There are a few special options you can give the `configure' program that + you should know about: + + --with-tcl build the Tcl/Tk components + --with-tk build the Tk-based X interface `xhfs' + --disable-cli do not build or install the command-line utils + --enable-devlibs enable installation of the developer libraries + + To build the Tcl tools `hfssh' and `hfs', you must use `--with-tcl'. + To build the X interface `xhfs', you must use `--with-tcl --with-tk'. + + In order to build the Tcl tools you must have Tcl 7.6 or newer already + installed on your system. To build the X interface, you must have Tk 4.2 + or newer installed as well. + + If `configure' is unable to locate your Tcl or Tk installation, you will + have to give it a hint. It looks for the files `tclConfig.sh' and + `tkConfig.sh' (respectively) which contain all the details about your + configuration; these files were created when Tcl/Tk was installed on your + system. + + To help `configure' find these files, you can specify a directory name on + the command line. For example: + + ./configure --with-tcl=/usr/share/tcl --with-tk=/usr/share/tk + + When in doubt, wait and see if `configure' complains. + + By default, the command-line utilities (hmount, hls, hcopy, etc.) are + always built and installed. If you don't want them, use `--disable-cli'. + + If you want to install the developer libraries (libhfs.a, librsrc.a, and + associated header files) as well as the programs, use `--enable-devlibs'. + The libraries are not installed by default. + + The `INSTALL' file contains the rest of the details you will need to + compile and install the software. Please read it! + +=============================================================================== + +NOTES ON "SETUID" INSTALLATION + + Although the author has made some effort to make the programs in this + package somewhat setuid-safe, none are intended to be installed in such a + manner, and you do so at your own risk! + + Specifically, installing the programs setuid root is a bad idea, as it will + allow anyone to attempt to open (or format) any file as an HFS volume, + disregarding the normal UNIX file permission checks. + + A better idea is to modify the permissions on the necessary device files to + allow access to appropriate users. For example, if you make your floppy + device group-writable to a special group of floppy users, then only those + users will be able to access an inserted floppy disk and nothing needs to + be made setuid. + + Here is an example: + + % ls -l /dev/fd0 /usr/local/bin/xhfs + brw-rw---- 1 root floppy 2, 0 Sep 17 13:51 /dev/fd0 + -rwxr-xr-x 1 root staff 339874 Jan 17 20:18 /usr/local/bin/xhfs + + % grep floppy /etc/group + floppy:*:25:rob,thigpen + + This configuration permits users `rob' and `thigpen' read/write access to + the floppy device, without needing to make any programs setuid. + + Another possibility is to make some of the programs in this package setgid + (NOT setuid) to a similar group which has access to the necessary devices. + This avoids the need to add specific users to a group, since it allows + anyone to access the devices using the utilities, while still maintaining + the integrity of the data on the devices since they are not world-writable. + (A world-writable device lends itself to the possibility that someone may + corrupt some portion of it by other means and render an HFS volume + unusable, whereas limiting access only through the utilities means the + volume is unlikely to be corrupted under normal usage -- although note that + since HFS itself does not provide any file permission structure, anyone may + still modify the HFS volume in any way. The only protection here is that + the modifications must be made in an HFS-compatible way, so that the + possibility of corruption is reduced.) + + Here is another example: + + % ls -l /dev/fd0 /usr/local/bin/xhfs + brw-rw---- 1 root floppy 2, 0 Sep 17 13:51 /dev/fd0 + -rwxr-sr-x 1 root floppy 339874 Jan 17 20:18 /usr/local/bin/xhfs + + % grep floppy /etc/group + floppy:*:25: + + In this case, only setgid `floppy' programs (such as `/usr/local/bin/xhfs') + are permitted access to the floppy device. + + To summarize: it is suggested that the utilities NOT be installed setuid or + setgid. It is possible to install the utilities setgid as described above, + but it is NOT recommended that the utilities be installed setuid root under + any circumstances. + +=============================================================================== + +CONTACTING THE AUTHOR + + Please see the `COPYRIGHT' file for copyright and warranty information. + + Send comments, bug reports, suggestions, patches, etc. to: + + * Robert Leslie + + See also the HFS Utilities home page on the Web: + + * http://www.mars.org/home/rob/proj/hfs/ + +=============================================================================== + diff --git a/TODO b/TODO new file mode 100644 index 0000000..a780b12 --- /dev/null +++ b/TODO @@ -0,0 +1,79 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: TODO,v 1.14 1998/11/02 22:08:22 rob Exp $ + +=============================================================================== + +General to-do items (aka bugs): + + libhfs + + * support HFS+ (aka Sequoia, Extended Format) + * don't allow reads from spared blocks + * further improve efficiency of b*-tree searches? + * concurrency considerations: opening same file multiple times; + performing directory manipulations on open files + * b*-tree record balancing? + * smarter block allocation algorithm + * volume full: hfs_write() should return bytes written < len, return -1 + on next call? + * seek beyond end of file? + * touch directory timestamps on file updates? + * improve efficiency of bitmap operations + * improve efficiency of search operations + * CNID wrapping? + * bad block sparing by physical block + + librsrc + + * determine resource sizes without reading them + * support writability + + hfsck + + * modify libhfs to support low-level grubbing + + command-line tools + + * preserve timestamps (-p) for hcopy + * recursive hcopy (-r) + * allow hformat to create files and/or set size? + * write Linux utilities mkfs.hfs, fsck.hfs + * eliminate ftruncate() + * more flags for hdel (-i, -r, -f) + * glob :*:foo correctly + * glob hcopy destination HFS path? + * formalize hdisk.pl into the distribution + + xhfs + + * open arbitrary partitions + * rename volumes + * change file type/creator + * more support for other platforms (correct device defaults; df output) + * graphical partition map editor? + * recursive copy: map illegal directory name chars + + copyin/copyout + + * finish/fix timestamp preservation + * revisit buffer sizes + * remember src/dst filenames (for preserve/delete) + * allow name mangling to be optional + * don't unconditionally clobber + * delete incomplete files after failed transfers + * AppleSingle, Netatalk-/CAP-style AppleDouble? + +Future wish-list: + + * raw SCSI access for hideous platforms + * partitioning utility + * defragmenting utility + +Acknowledged limitations: + + * null characters (ASCII 0x00) not supported in filenames + * partition maps not managed with A/UX support + diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..e627c98 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,53 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:26:53 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ +@TOP@ + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +/* Define if your system has sys_errlist[] and sys_nerr. */ +#undef HAVE_SYS_ERRLIST + +@BOTTOM@ + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ + +# ifndef HAVE_STRDUP +char *strdup(const char *); +# endif + +# ifndef HAVE_STRERROR +char *strerror(int); +# endif + +# ifndef HAVE_STRSTR +char *strstr(const char *, const char *); +# endif + +# ifndef HAVE_STRTOL +long strtol(const char *, char **, int); +# endif diff --git a/binhex.c b/binhex.c new file mode 100644 index 0000000..50a9eeb --- /dev/null +++ b/binhex.c @@ -0,0 +1,616 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: binhex.c,v 1.11 1998/09/18 22:56:22 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# else +int dup(int); +# endif + +# include +# include +# include + +# include "binhex.h" +# include "crc.h" + +const char *bh_error = "no error"; + +extern int errno; + +# define ERROR(code, str) (bh_error = (str), errno = (code)) + +static FILE *file; /* input/output file */ +static char line[67]; /* ASCII line buffer */ +static int lptr; /* next char in line buffer */ +static int state86; /* 8->6 encoding state */ +static unsigned char lastch; /* last encoded data byte */ +static int runlen; /* runlength of last data byte */ +static unsigned short crc; /* incremental CRC word */ + +static const +unsigned char zero[2] = { 0, 0 }; + +static const +char hqxheader[] = "(This file must be converted with BinHex 4.0)\n"; + +static const +char enmap[] = "!\"#$%&'()*+,-012345689@ABCDEFGHI" + "JKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; + +static const +signed char demap[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, -1, 0, 0, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -1, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 0, 0, + 14, 15, 16, 17, 18, 19, 20, 0, + 21, 22, 0, 0, 0, 0, 0, 0, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 0, + 38, 39, 40, 41, 42, 43, 44, 0, + 45, 46, 47, 48, 0, 0, 0, 0, + 49, 50, 51, 52, 53, 54, 55, 0, + 56, 57, 58, 59, 60, 61, 0, 0, + 62, 63, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +# define HEADERMATCH 40 +# define MAXLINELEN 64 +# define ISRETURN(c) (demap[(unsigned char) (c)] == -1) + +/* BinHex Encoding ========================================================= */ + +/* + * NAME: bh->start() + * DESCRIPTION: begin BinHex encoding + */ +int bh_start(int fd) +{ + int dupfd; + + dupfd = dup(fd); + if (dupfd == -1) + { + ERROR(errno, "error duplicating output stream"); + return -1; + } + + file = fdopen(dupfd, "ab"); + if (file == 0) + { + ERROR(errno, "error creating output buffer"); + + close(dupfd); + return -1; + } + + line[0] = ':'; + lptr = 1; + + state86 = 0; + runlen = 0; + + crc = 0x0000; + + if (fputs(hqxheader, file) == EOF) + { + ERROR(EIO, "error writing hqx header"); + + fclose(file); + return -1; + } + + return 0; +} + +/* + * NAME: flushline() + * DESCRIPTION: flush a line to the output file + */ +static +int flushline(void) +{ + line[lptr++] = '\n'; + line[lptr++] = 0; + + if (fputs(line, file) == EOF) + { + ERROR(EIO, "error writing output data"); + return -1; + } + + lptr = 0; + + return 0; +} + +/* + * NAME: addchars() + * DESCRIPTION: insert bytes of data to the output stream + */ +static +int addchars(const unsigned char *data, register int len) +{ + register unsigned char c; + + while (len--) + { + c = *data++; + + if (lptr == MAXLINELEN && + flushline() == -1) + return -1; + + switch (state86 & 0xff00) + { + case 0x0000: + line[lptr++] = enmap[c >> 2]; + state86 = 0x0100 | (c & 0x03); + break; + + case 0x0100: + line[lptr++] = enmap[((state86 & 0x03) << 4) | (c >> 4)]; + state86 = 0x0200 | (c & 0x0f); + break; + + case 0x0200: + line[lptr++] = enmap[((state86 & 0x0f) << 2) | (c >> 6)]; + + if (lptr == MAXLINELEN && + flushline() == -1) + return -1; + + line[lptr++] = enmap[c & 0x3f]; + state86 = 0; + break; + } + } + + return 0; +} + +/* + * NAME: rleflush() + * DESCRIPTION: run-length encode data + */ +static +int rleflush(void) +{ + unsigned char rle[] = { 0x90, 0x00, 0x90, 0x00 }; + + if ((lastch != 0x90 && runlen < 4) || + (lastch == 0x90 && runlen < 3)) + { + /* self representation */ + + if (lastch == 0x90) + { + while (runlen--) + if (addchars(rle, 2) == -1) + return -1; + } + else + { + while (runlen--) + if (addchars(&lastch, 1) == -1) + return -1; + } + } + else + { + /* run-length encoded */ + + if (lastch == 0x90) + { + rle[3] = runlen; + + if (addchars(rle, 4) == -1) + return -1; + } + else + { + rle[1] = lastch; + rle[3] = runlen; + + if (addchars(&rle[1], 3) == -1) + return -1; + } + } + + runlen = 0; + + return 0; +} + +/* + * NAME: bh->insert() + * DESCRIPTION: encode bytes of data, buffering lines and flushing + */ +int bh_insert(const void *buf, register int len) +{ + register const unsigned char *data = buf; + + crc = crc_binh(data, len, crc); + + for ( ; len--; ++data) + { + if (runlen) + { + if (runlen == 0xff || lastch != *data) + { + if (rleflush() == -1) + return -1; + } + + if (lastch == *data) + { + ++runlen; + continue; + } + } + + lastch = *data; + runlen = 1; + } + + return 0; +} + +/* + * NAME: bh->insertcrc() + * DESCRIPTION: insert a two-byte CRC checksum + */ +int bh_insertcrc(void) +{ + unsigned char word[2]; + + crc = crc_binh(zero, 2, crc); + + word[0] = (crc & 0xff00) >> 8; + word[1] = (crc & 0x00ff) >> 0; + + if (bh_insert(word, 2) == -1) + return -1; + + crc = 0x0000; + + return 0; +} + +/* + * NAME: bh->end() + * DESCRIPTION: finish BinHex encoding + */ +int bh_end(void) +{ + int result = 0; + + if (runlen && + rleflush() == -1) + result = -1; + + if (state86 && result == 0 && + addchars(zero, 1) == -1) + result = -1; + + line[lptr++] = ':'; + + if (result == 0 && + flushline() == -1) + result = -1; + + if (fclose(file) == EOF && result == 0) + { + ERROR(errno, "error flushing output data"); + result = -1; + } + + return result; +} + +/* BinHex Decoding ========================================================= */ + +/* + * NAME: bh->open() + * DESCRIPTION: begin BinHex decoding + */ +int bh_open(int fd) +{ + int dupfd, c; + const char *ptr; + + dupfd = dup(fd); + if (dupfd == -1) + { + ERROR(errno, "error duplicating input stream"); + return -1; + } + + file = fdopen(dupfd, "rb"); + if (file == 0) + { + ERROR(errno, "error creating input buffer"); + + close(dupfd); + return -1; + } + + state86 = 0; + runlen = 0; + + crc = 0x0000; + + /* find hqx header */ + + ptr = hqxheader; + while (ptr == 0 || ptr - hqxheader < HEADERMATCH) + { + c = getc(file); + if (c == EOF) + { + ERROR(EINVAL, "hqx file header not found"); + + fclose(file); + return -1; + } + + if (c == '\n' || c == '\r') + { + ptr = hqxheader; + continue; + } + + if (ptr && c != *ptr++) + ptr = 0; + } + + /* skip to CR/LF */ + + do + { + c = getc(file); + if (c == EOF) + { + ERROR(EINVAL, "corrupt hqx file"); + + fclose(file); + return -1; + } + } + while (c != '\n' && c != '\r'); + + /* skip whitespace */ + + do + { + c = getc(file); + if (c == EOF) + { + ERROR(EINVAL, "corrupt hqx file"); + + fclose(file); + return -1; + } + } + while (ISRETURN(c)); + + if (c != ':') + { + ERROR(EINVAL, "corrupt hqx file"); + + fclose(file); + return -1; + } + + return 0; +} + +/* + * NAME: hqxchar() + * DESCRIPTION: return the next hqx character from the input stream + */ +static +int hqxchar(void) +{ + int c; + + do + c = getc(file); + while (c != EOF && ISRETURN(c)); + + if (c == EOF) + { + if (feof(file)) + ERROR(EINVAL, "unexpected end of file"); + else + ERROR(EIO, "error reading input file"); + + return -1; + } + + c = demap[(unsigned char) c]; + if (c == 0) + { + ERROR(EINVAL, "illegal character in hqx file"); + return -1; + } + + return c - 1; +} + +/* + * NAME: nextchar() + * DESCRIPTION: decode one character from the hqx stream + */ +static +int nextchar(void) +{ + int c, c2, ch; + + c = hqxchar(); + if (c == -1) + return -1; + + switch (state86 & 0xff00) + { + case 0x0000: + c2 = hqxchar(); + if (c2 == -1) + return -1; + + ch = (c << 2) | (c2 >> 4); + state86 = 0x0100 | (c2 & 0x0f); + break; + + case 0x0100: + ch = ((state86 & 0x0f) << 4) | (c >> 2); + state86 = 0x0200 | (c & 0x03); + break; + + case 0x0200: + ch = ((state86 & 0x03) << 6) | c; + state86 = 0; + break; + } + + return ch; +} + +/* + * NAME: bh->read() + * DESCRIPTION: decode and return bytes from the hqx stream + */ +int bh_read(void *buf, register int len) +{ + register unsigned char *data = buf; + const unsigned char *ptr = data; + int c, rl, count = len; + + while (len--) + { + if (runlen) + { + *data++ = lastch; + --runlen; + continue; + } + + c = nextchar(); + if (c == -1) + return -1; + + if (c == 0x90) + { + rl = nextchar(); + if (rl == -1) + return -1; + + if (rl > 0) + { + runlen = rl - 1; + ++len; + continue; + } + } + + *data++ = lastch = c; + } + + crc = crc_binh(ptr, count, crc); + + return count; +} + +/* + * NAME: bh->readcrc() + * DESCRIPTION: read and compare CRC bytes + */ +int bh_readcrc(void) +{ + unsigned short check; + unsigned char word[2]; + + check = crc_binh(zero, 2, crc); + + if (bh_read(word, 2) < 2) + return -1; + + crc = (word[0] << 8) | + (word[1] << 0); + + if (crc != check) + { + ERROR(EINVAL, "CRC checksum error"); + return -1; + } + + crc = 0x0000; + + return 0; +} + +/* + * NAME: bh->close() + * DESCRIPTION: finish BinHex decoding + */ +int bh_close(void) +{ + int c, result = 0; + + /* skip whitespace */ + + do + c = getc(file); + while (c != EOF && ISRETURN(c)); + + /* skip optional exclamation */ + + if (c == '!') + { + do + c = getc(file); + while (c != EOF && ISRETURN(c)); + } + + /* verify trailing colon */ + + if (c != ':') + { + ERROR(EINVAL, "corrupt end of hqx file"); + result = -1; + } + + if (fclose(file) == EOF && result == 0) + { + ERROR(errno, "error closing input file"); + result = -1; + } + + return result; +} diff --git a/binhex.h b/binhex.h new file mode 100644 index 0000000..a75e5e9 --- /dev/null +++ b/binhex.h @@ -0,0 +1,32 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: binhex.h,v 1.7 1998/04/11 08:26:53 rob Exp $ + */ + +extern const char *bh_error; + +int bh_start(int); +int bh_insert(const void *, register int); +int bh_insertcrc(void); +int bh_end(void); + +int bh_open(int); +int bh_read(void *, register int); +int bh_readcrc(void); +int bh_close(void); diff --git a/charset.c b/charset.c new file mode 100644 index 0000000..d4e3c44 --- /dev/null +++ b/charset.c @@ -0,0 +1,285 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: charset.c,v 1.5 1998/11/02 22:08:23 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "charset.h" + +static const +UCS2 macroman[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + + 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, + 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, + 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, + 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, + + 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, + 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, + 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, + 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x2126, 0x00e6, 0x00f8, + + 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, + 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, + 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, + 0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0xfb01, 0xfb02, + + 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, + 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, + 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, + 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 +}; + +static const +char *macroman_subst[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + + "+", 0, 0, 0, 0, "*", 0, 0, /* 0xa0 - 0xa7 */ + 0, 0, "[tm]", 0, 0, "!=", 0, 0, /* 0xa8 - 0xaf */ + + "[inf]", 0, "<=", ">=", 0, 0, "[partial]", "[Sum]", /* 0xb0 - 0xb7 */ + "[Prod]", "[pi]", "[Integral]", 0, 0, "[ohm]", 0, 0, /* 0xb8 - 0xbf */ + + 0, 0, 0, "[Sqrt]", "[f]", "~=", "[delta]", 0, /* 0xc0 - 0xc7 */ + 0, "...", 0, 0, 0, 0, "OE", "oe", /* 0xc8 - 0xcf */ + + "-", "--", "``", "''", "`", "'", 0, "#", /* 0xd0 - 0xd7 */ + 0, "Y", "/", 0, "<", ">", "fi", "fl", /* 0xd8 - 0xdf */ + + "++", 0, ",", ",,", "o/oo", 0, 0, 0, /* 0xe0 - 0xe7 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe8 - 0xef */ + + "[Apple]", 0, 0, 0, 0, "i", "^", "~", /* 0xf0 - 0xf7 */ + 0, "-", "\267", "\260", 0, "\"", "-", "^" /* 0xf8 - 0xff */ +}; + +static +unsigned char latin1[256]; + +static const +char *latin1_subst[128] = { + "", "", "", "", "", "", "", "", /* 0x80 - 0x87 */ + "", "", "", "", "", "", "", "", /* 0x88 - 0x8f */ + + "", "", "", "", "", "", "", "", /* 0x90 - 0x97 */ + "", "", "", "", "", "", "", "", /* 0x98 - 0x9f */ + + 0, 0, 0, 0, 0, 0, "|", 0, /* 0xa0 - 0xa7 */ + 0, 0, 0, 0, 0, "-", 0, 0, /* 0xa8 - 0xaf */ + + 0, 0, "[^2]", "[^3]", 0, 0, 0, 0, /* 0xb0 - 0xb7 */ + 0, "[^1]", 0, 0, "[1/4]", "[1/2]", "[3/4]", 0, /* 0xb8 - 0xbf */ + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + + "D", 0, 0, 0, 0, 0, 0, "x", /* 0xd0 - 0xd7 */ + 0, 0, 0, 0, 0, "Y", "P", 0, /* 0xd8 - 0xdf */ + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + + "d", 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xf7 */ + 0, 0, 0, 0, 0, "y", "p", 0 /* 0xf8 - 0xff */ +}; + +/* + * NAME: charset->unicode() + * DESCRIPTION: return a Unicode string for MacOS Standard Roman + */ +UCS2 *cs_unicode(char *mstr, int *lenptr) +{ + int len, i; + UCS2 *unicode, *ptr; + + len = lenptr ? *lenptr : strlen(mstr); + + unicode = malloc((len + 1) * sizeof(UCS2)); + if (unicode == 0) + return 0; + + ptr = unicode; + for (i = 0; i < len; ++i) + *ptr++ = macroman[*(unsigned char *) mstr++]; + + *ptr = 0; + + return unicode; +} + +/* + * NAME: charset->latin1() + * DESCRIPTION: return a Latin-1 (ISO 8859-1) string for MacOS Standard Roman + */ +char *cs_latin1(char *mstr, int *lenptr) +{ + int ilen, olen, i; + char *latin1, *ptr; + const char *subst; + unsigned char ch; + UCS2 unicode; + + ilen = lenptr ? *lenptr : strlen(mstr); + + olen = 0; + for (i = 0; i < ilen; ++i) + { + ch = *((unsigned char *) mstr + i); + unicode = macroman[ch]; + + if (unicode & 0xff00) + olen += strlen(macroman_subst[ch & 0x7f]); + else + ++olen; + } + + latin1 = malloc(olen + 1); + if (latin1 == 0) + return 0; + + ptr = latin1; + for (i = 0; i < ilen; ++i) + { + ch = *(unsigned char *) mstr++; + unicode = macroman[ch]; + + if (unicode & 0xff00) + { + /* substitute similar Latin-1 character(s) */ + + subst = macroman_subst[ch & 0x7f]; + + strcpy(ptr, subst); + ptr += strlen(subst); + } + else + *ptr++ = unicode; + } + + *ptr = 0; + + if (lenptr) + *lenptr = olen; + + return latin1; +} + +/* + * NAME: charset->mktable() + * DESCRIPTION: construct latin1[] table from macroman[] + */ +static +void mktable(void) +{ + int i; + + for (i = 0x00; i <= 0xff; ++i) + latin1[i] = 0xff; + + for (i = 0x00; i <= 0xff; ++i) + { + UCS2 unicode = macroman[i]; + + if (! (unicode & 0xff00)) + latin1[unicode] = i; + } +} + +/* + * NAME: charset->macroman() + * DESCRIPTION: return a MacOS Standard Roman string for Latin-1 (ISO 8859-1) + */ +char *cs_macroman(char *lstr, int *lenptr) +{ + int ilen, olen, i; + char *macroman, *ptr; + const char *subst; + unsigned char ch, msr; + + if (latin1[0x80] != 0xff) + mktable(); + + ilen = lenptr ? *lenptr : strlen(lstr); + + olen = 0; + for (i = 0; i < ilen; ++i) + { + ch = *((unsigned char *) lstr + i); + msr = latin1[ch]; + + if (msr == 0xff) + olen += strlen(latin1_subst[ch & 0x7f]); + else + ++olen; + } + + macroman = malloc(olen + 1); + if (macroman == 0) + return 0; + + ptr = macroman; + for (i = 0; i < ilen; ++i) + { + ch = *(unsigned char *) lstr++; + msr = latin1[ch]; + + if (msr == 0xff) + { + /* substitute similar MacOS Standard Roman character(s) */ + + subst = latin1_subst[ch & 0x7f]; + + strcpy(ptr, subst); + ptr += strlen(subst); + } + else + *ptr++ = msr; + } + + *ptr = 0; + + if (lenptr) + *lenptr = olen; + + return macroman; +} diff --git a/charset.h b/charset.h new file mode 100644 index 0000000..5d600bd --- /dev/null +++ b/charset.h @@ -0,0 +1,27 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: charset.h,v 1.3 1998/04/11 08:26:53 rob Exp $ + */ + +typedef unsigned short UCS2; + +UCS2 *cs_unicode(char *, int *); + +char *cs_latin1(char *, int *); +char *cs_macroman(char *, int *); diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..232e731 --- /dev/null +++ b/config.h.in @@ -0,0 +1,93 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:26:53 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +/* Define if your system has sys_errlist[] and sys_nerr. */ +#undef HAVE_SYS_ERRLIST + +/* Define if you have the setreuid function. */ +#undef HAVE_SETREUID + +/* Define if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the strtol function. */ +#undef HAVE_STRTOL + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ + +# ifndef HAVE_STRDUP +char *strdup(const char *); +# endif + +# ifndef HAVE_STRERROR +char *strerror(int); +# endif + +# ifndef HAVE_STRSTR +char *strstr(const char *, const char *); +# endif + +# ifndef HAVE_STRTOL +long strtol(const char *, char **, int); +# endif diff --git a/configure b/configure new file mode 100755 index 0000000..e6e752e --- /dev/null +++ b/configure @@ -0,0 +1,2116 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-tcl[=DIR] build the Tcl components" +ac_help="$ac_help + --with-tk[=DIR] build the Tk components" +ac_help="$ac_help + --disable-cli do not build or install the command-line utils" +ac_help="$ac_help + --enable-devlibs enable installation of the developer libraries" +ac_help="$ac_help + --enable-debug enable diagnostic debugging support" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=libhfs/libhfs.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +subdirs="libhfs librsrc" + + +# Check whether --with-tcl or --without-tcl was given. +if test "${with_tcl+set}" = set; then + withval="$with_tcl" + + case "$with_tcl" in + yes|no) + tcl_dir="DEFAULT" + ;; + *) + tcl_dir="$with_tcl" + with_tcl=yes + ;; + esac + +fi + + +# Check whether --with-tk or --without-tk was given. +if test "${with_tk+set}" = set; then + withval="$with_tk" + + case "$with_tk" in + yes|no) + tk_dir="DEFAULT" + ;; + *) + tk_dir="$with_tk" + with_tk=yes + ;; + esac + +fi + + +if test "$with_tk" = yes && test "$with_tcl" != yes +then + { echo "configure: error: --with-tk requires --with-tcl" 1>&2; exit 1; } +fi + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:597: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:626: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:655: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:703: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:737: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:742: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:766: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:804: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:856: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:884: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:905: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:922: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:946: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:994: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1007: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1074: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in unistd.h fcntl.h termios.h sys/ioctl.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1101: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1111: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + + +echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 +echo "configure:1140: checking for uid_t in sys/types.h" >&5 +if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "uid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_type_uid_t" 1>&6 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + + cat >> confdefs.h <<\EOF +#define gid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1174: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1208: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1262: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + + + +for ac_func in strdup strerror strstr strtol +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1287: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1315: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +LIBOBJS="$LIBOBJS ${ac_func}.o" +fi +done + + +for ac_func in setreuid +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1344: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1372: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for sys_errlist""... $ac_c" 1>&6 +echo "configure:1398: checking for sys_errlist" >&5 +if eval "test \"`echo '$''{'hfs_cv_decl_syserrlist'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + hfs_cv_decl_syserrlist=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + hfs_cv_decl_syserrlist=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$hfs_cv_decl_syserrlist" 1>&6 + +if test "$hfs_cv_decl_syserrlist" = yes +then + cat >> confdefs.h <<\EOF +#define HAVE_SYS_ERRLIST 1 +EOF + +fi + + +MAKE_TARGETS="" +INSTALL_TARGETS="" + + + + + +TCL_INCLUDES="" +TCL_LIB_SPEC="" +TCL_LIBS="" + + + + + +if test "$with_tcl" = yes +then + echo $ac_n "checking for Tcl""... $ac_c" 1>&6 +echo "configure:1458: checking for Tcl" >&5 + + found="" + for dir in \ + "$tcl_dir" \ + "$tcl_dir/lib" \ + "$tcl_dir/../lib" \ + /usr/local/lib \ + /usr/local/lib/tcl* \ + /usr/local/tcl*/lib \ + /usr/lib \ + /usr/lib/tcl* \ + /usr/tcl*/lib \ + ; \ + do + if test -r "$dir/tclConfig.sh" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + echo "$ac_t""cannot find installation" 1>&6 + { echo "configure: error: use --without-tcl or --with-tcl=DIR" 1>&2; exit 1; } + fi + + tcl_dir="$found" + . "$tcl_dir/tclConfig.sh" + + echo "$ac_t""version $TCL_VERSION in $tcl_dir" 1>&6 + + echo $ac_n "checking for tcl.h""... $ac_c" 1>&6 +echo "configure:1492: checking for tcl.h" >&5 + + found="" + for dir in \ + $TCL_PREFIX/include \ + $TCL_PREFIX/include/tcl* \ + $TCL_PREFIX/tcl*/include \ + "$tcl_dir" \ + "$tcl_dir/include" \ + "$tcl_dir/../include" \ + `echo "$tcl_dir" | sed -e 's|/lib|/include|'` \ + ; \ + do + if test -r "$dir/tcl.h" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + echo "$ac_t""not found" 1>&6 + echo "configure: warning: compilation of Tcl components may fail" 1>&2 + echo "configure: warning: edit Makefile if necessary and contact author" 1>&2 + else + echo "$ac_t""$found" 1>&6 + TCL_INCLUDES="-I$found" + fi + + MAKE_TARGETS="$MAKE_TARGETS all_tcl" + INSTALL_TARGETS="$INSTALL_TARGETS install_tcl" +fi + + +TK_INCLUDES="" +TK_LIB_SPEC="" +TK_LIBS="" + + + + + +if test "$with_tk" = yes +then + echo $ac_n "checking for Tk""... $ac_c" 1>&6 +echo "configure:1538: checking for Tk" >&5 + + found="" + for dir in \ + "$tk_dir" \ + "$tk_dir/lib" \ + "$tk_dir/../lib" \ + /usr/local/lib \ + /usr/local/lib/tk* \ + /usr/local/tk*/lib \ + /usr/lib \ + /usr/lib/tk* \ + /usr/tk*/lib \ + ; \ + do + if test -r "$dir/tkConfig.sh" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + echo "$ac_t""cannot find installation" 1>&6 + { echo "configure: error: use --without-tk or --with-tk=DIR" 1>&2; exit 1; } + fi + + tk_dir="$found" + . "$tk_dir/tkConfig.sh" + + echo "$ac_t""version $TK_VERSION in $tk_dir" 1>&6 + + echo $ac_n "checking for tk.h""... $ac_c" 1>&6 +echo "configure:1572: checking for tk.h" >&5 + + found="" + for dir in \ + $TK_PREFIX/include \ + $TK_PREFIX/include/tk* \ + $TK_PREFIX/tk*/include \ + "$tk_dir" \ + "$tk_dir/include" \ + "$tk_dir/../include" \ + `echo "$tk_dir" | sed -e 's|/lib|/include|'` \ + ; \ + do + if test -r "$dir/tk.h" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + echo "$ac_t""not found" 1>&6 + echo "configure: warning: compilation of Tk components may fail" 1>&2 + echo "configure: warning: edit Makefile if necessary and contact author" 1>&2 + else + echo "$ac_t""$found" 1>&6 + + if test "x-I$found" != "x$TCL_INCLUDES" + then + TK_INCLUDES="-I$found" + fi + fi + + TK_INCLUDES="$TK_INCLUDES $TK_XINCLUDES" + + MAKE_TARGETS="$MAKE_TARGETS all_tk" + INSTALL_TARGETS="$INSTALL_TARGETS install_tk" +fi + + +# Check whether --enable-cli or --disable-cli was given. +if test "${enable_cli+set}" = set; then + enableval="$enable_cli" + : +fi + + +if test "x$enable_cli" != xno +then + MAKE_TARGETS="all_cli $MAKE_TARGETS" + INSTALL_TARGETS="install_cli $INSTALL_TARGETS" +fi + +# Check whether --enable-devlibs or --disable-devlibs was given. +if test "${enable_devlibs+set}" = set; then + enableval="$enable_devlibs" + : +fi + + +if test "x$enable_devlibs" = xyes +then + MAKE_TARGETS="$MAKE_TARGETS all_lib" + INSTALL_TARGETS="$INSTALL_TARGETS install_lib" +fi + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + : +fi + + +if test "x$enable_debug" = xyes +then + cat >> confdefs.h <<\EOF +#define DEBUG 1 +EOF + +fi + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "hfsck/Makefile linux/Makefile Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@subdirs@%$subdirs%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@CPP@%$CPP%g +s%@LIBOBJS@%$LIBOBJS%g +s%@MAKE_TARGETS@%$MAKE_TARGETS%g +s%@INSTALL_TARGETS@%$INSTALL_TARGETS%g +s%@TCL_INCLUDES@%$TCL_INCLUDES%g +s%@TCL_LIB_SPEC@%$TCL_LIB_SPEC%g +s%@TCL_LIBS@%$TCL_LIBS%g +s%@TK_INCLUDES@%$TK_INCLUDES%g +s%@TK_LIB_SPEC@%$TK_LIB_SPEC%g +s%@TK_LIBS@%$TK_LIBS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + +if test "$no_recursion" != yes; then + + # Remove --cache-file and --srcdir arguments so they do not pile up. + ac_sub_configure_args= + ac_prev= + for ac_arg in $ac_configure_args; do + if test -n "$ac_prev"; then + ac_prev= + continue + fi + case "$ac_arg" in + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + ;; + *) ac_sub_configure_args="$ac_sub_configure_args $ac_arg" ;; + esac + done + + for ac_config_dir in libhfs librsrc; do + + # Do not complain, so a configure script can configure whichever + # parts of a large source tree are present. + if test ! -d $srcdir/$ac_config_dir; then + continue + fi + + echo configuring in $ac_config_dir + + case "$srcdir" in + .) ;; + *) + if test -d ./$ac_config_dir || mkdir ./$ac_config_dir; then :; + else + { echo "configure: error: can not create `pwd`/$ac_config_dir" 1>&2; exit 1; } + fi + ;; + esac + + ac_popdir=`pwd` + cd $ac_config_dir + + # A "../" for each directory in /$ac_config_dir. + ac_dots=`echo $ac_config_dir|sed -e 's%^\./%%' -e 's%[^/]$%&/%' -e 's%[^/]*/%../%g'` + + case "$srcdir" in + .) # No --srcdir option. We are building in place. + ac_sub_srcdir=$srcdir ;; + /*) # Absolute path. + ac_sub_srcdir=$srcdir/$ac_config_dir ;; + *) # Relative path. + ac_sub_srcdir=$ac_dots$srcdir/$ac_config_dir ;; + esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_sub_srcdir/configure; then + ac_sub_configure=$ac_sub_srcdir/configure + elif test -f $ac_sub_srcdir/configure.in; then + ac_sub_configure=$ac_configure + else + echo "configure: warning: no configuration information is in $ac_config_dir" 1>&2 + ac_sub_configure= + fi + + # The recursion is here. + if test -n "$ac_sub_configure"; then + + # Make the cache file name correct relative to the subdirectory. + case "$cache_file" in + /*) ac_sub_cache_file=$cache_file ;; + *) # Relative path. + ac_sub_cache_file="$ac_dots$cache_file" ;; + esac + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo "running ${CONFIG_SHELL-/bin/sh} $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_sub_srcdir" + # The eval makes quoting arguments work. + if eval ${CONFIG_SHELL-/bin/sh} $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_sub_srcdir + then : + else + { echo "configure: error: $ac_sub_configure failed for $ac_config_dir" 1>&2; exit 1; } + fi + fi + + cd $ac_popdir + done +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..90910dc --- /dev/null +++ b/configure.in @@ -0,0 +1,301 @@ +dnl -*- shell-script -*- +dnl +dnl hfsutils - tools for reading and writing Macintosh HFS volumes +dnl Copyright (C) 1996-1998 Robert Leslie +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +dnl +dnl $Id: configure.in,v 1.10 1998/09/15 19:19:42 rob Exp $ +dnl + +AC_INIT(libhfs/libhfs.h) +AC_CONFIG_HEADER(config.h) + +AC_CONFIG_SUBDIRS(libhfs librsrc) + +AC_ARG_WITH(tcl, + [ --with-tcl[=DIR] build the Tcl components], [ + case "$with_tcl" in + yes|no) + tcl_dir="DEFAULT" + ;; + *) + tcl_dir="$with_tcl" + with_tcl=yes + ;; + esac +]) + +AC_ARG_WITH(tk, + [ --with-tk[=DIR] build the Tk components], [ + case "$with_tk" in + yes|no) + tk_dir="DEFAULT" + ;; + *) + tk_dir="$with_tk" + with_tk=yes + ;; + esac +]) + +if test "$with_tk" = yes && test "$with_tcl" != yes +then + AC_MSG_ERROR(--with-tk requires --with-tcl) +fi + +dnl Checks for programs. + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +AC_PROG_GCC_TRADITIONAL + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h fcntl.h termios.h sys/ioctl.h) + +dnl Checks for typedefs, structures, and compiler characteristics. + +AC_TYPE_UID_T +AC_TYPE_SIZE_T + +AC_C_CONST + +dnl Checks for library functions. + +AC_REPLACE_FUNCS(strdup strerror strstr strtol) +AC_CHECK_FUNCS(setreuid) + +AC_CACHE_CHECK(for sys_errlist, hfs_cv_decl_syserrlist, [ + AC_TRY_LINK([], [ + extern char *sys_errlist[]; + extern int sys_nerr; + + sys_errlist[sys_nerr - 1][0] = 0; + ], hfs_cv_decl_syserrlist=yes, hfs_cv_decl_syserrlist=no) +]) + +if test "$hfs_cv_decl_syserrlist" = yes +then + AC_DEFINE(HAVE_SYS_ERRLIST) +fi + +dnl Prepare target variables. + +MAKE_TARGETS="" +INSTALL_TARGETS="" + +AC_SUBST(MAKE_TARGETS) +AC_SUBST(INSTALL_TARGETS) + +dnl Tcl configuration. + +TCL_INCLUDES="" +TCL_LIB_SPEC="" +TCL_LIBS="" + +AC_SUBST(TCL_INCLUDES) +AC_SUBST(TCL_LIB_SPEC) +AC_SUBST(TCL_LIBS) + +if test "$with_tcl" = yes +then + AC_MSG_CHECKING(for Tcl) + + found="" + for dir in \ + "$tcl_dir" \ + "$tcl_dir/lib" \ + "$tcl_dir/../lib" \ + /usr/local/lib \ + /usr/local/lib/tcl* \ + /usr/local/tcl*/lib \ + /usr/lib \ + /usr/lib/tcl* \ + /usr/tcl*/lib \ + ; \ + do + if test -r "$dir/tclConfig.sh" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + AC_MSG_RESULT(cannot find installation) + AC_MSG_ERROR(use --without-tcl or --with-tcl=DIR) + fi + + tcl_dir="$found" + . "$tcl_dir/tclConfig.sh" + + AC_MSG_RESULT(version $TCL_VERSION in $tcl_dir) + + AC_MSG_CHECKING(for tcl.h) + + found="" + for dir in \ + $TCL_PREFIX/include \ + $TCL_PREFIX/include/tcl* \ + $TCL_PREFIX/tcl*/include \ + "$tcl_dir" \ + "$tcl_dir/include" \ + "$tcl_dir/../include" \ + `echo "$tcl_dir" | sed -e 's|/lib|/include|'` \ + ; \ + do + if test -r "$dir/tcl.h" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + AC_MSG_RESULT(not found) + AC_MSG_WARN(compilation of Tcl components may fail) + AC_MSG_WARN(edit Makefile if necessary and contact author) + else + AC_MSG_RESULT($found) + TCL_INCLUDES="-I$found" + fi + + MAKE_TARGETS="$MAKE_TARGETS all_tcl" + INSTALL_TARGETS="$INSTALL_TARGETS install_tcl" +fi + +dnl Tk configuration. + +TK_INCLUDES="" +TK_LIB_SPEC="" +TK_LIBS="" + +AC_SUBST(TK_INCLUDES) +AC_SUBST(TK_LIB_SPEC) +AC_SUBST(TK_LIBS) + +if test "$with_tk" = yes +then + AC_MSG_CHECKING(for Tk) + + found="" + for dir in \ + "$tk_dir" \ + "$tk_dir/lib" \ + "$tk_dir/../lib" \ + /usr/local/lib \ + /usr/local/lib/tk* \ + /usr/local/tk*/lib \ + /usr/lib \ + /usr/lib/tk* \ + /usr/tk*/lib \ + ; \ + do + if test -r "$dir/tkConfig.sh" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + AC_MSG_RESULT(cannot find installation) + AC_MSG_ERROR(use --without-tk or --with-tk=DIR) + fi + + tk_dir="$found" + . "$tk_dir/tkConfig.sh" + + AC_MSG_RESULT(version $TK_VERSION in $tk_dir) + + AC_MSG_CHECKING(for tk.h) + + found="" + for dir in \ + $TK_PREFIX/include \ + $TK_PREFIX/include/tk* \ + $TK_PREFIX/tk*/include \ + "$tk_dir" \ + "$tk_dir/include" \ + "$tk_dir/../include" \ + `echo "$tk_dir" | sed -e 's|/lib|/include|'` \ + ; \ + do + if test -r "$dir/tk.h" + then + found="$dir" + break + fi + done + + if test -z "$found" + then + AC_MSG_RESULT(not found) + AC_MSG_WARN(compilation of Tk components may fail) + AC_MSG_WARN(edit Makefile if necessary and contact author) + else + AC_MSG_RESULT($found) + + if test "x-I$found" != "x$TCL_INCLUDES" + then + TK_INCLUDES="-I$found" + fi + fi + + TK_INCLUDES="$TK_INCLUDES $TK_XINCLUDES" + + MAKE_TARGETS="$MAKE_TARGETS all_tk" + INSTALL_TARGETS="$INSTALL_TARGETS install_tk" +fi + +dnl Other options. + +AC_ARG_ENABLE(cli, + [ --disable-cli do not build or install the command-line utils]) + +if test "x$enable_cli" != xno +then + MAKE_TARGETS="all_cli $MAKE_TARGETS" + INSTALL_TARGETS="install_cli $INSTALL_TARGETS" +fi + +AC_ARG_ENABLE(devlibs, + [ --enable-devlibs enable installation of the developer libraries]) + +if test "x$enable_devlibs" = xyes +then + MAKE_TARGETS="$MAKE_TARGETS all_lib" + INSTALL_TARGETS="$INSTALL_TARGETS install_lib" +fi + +AC_ARG_ENABLE(debug, + [ --enable-debug enable diagnostic debugging support]) + +if test "x$enable_debug" = xyes +then + AC_DEFINE(DEBUG) +fi + +dnl Create output files. + +AC_OUTPUT(hfsck/Makefile linux/Makefile Makefile) diff --git a/copyin.c b/copyin.c new file mode 100644 index 0000000..061f042 --- /dev/null +++ b/copyin.c @@ -0,0 +1,769 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: copyin.c,v 1.8 1998/11/02 22:08:25 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_FCNTL_H +# include +# else +int open(const char *, int, ...); +# endif + +# ifdef HAVE_UNISTD_H +# include +# else +int dup(int); +# endif + +# include +# include +# include + +# include "hfs.h" +# include "data.h" +# include "copyin.h" +# include "charset.h" +# include "binhex.h" +# include "crc.h" + +const char *cpi_error = "no error"; + +extern int errno; + +# define ERROR(code, str) (cpi_error = (str), errno = (code)) + +# define MACB_BLOCKSZ 128 + +# define TEXT_TYPE "TEXT" +# define TEXT_CREA "UNIX" + +# define RAW_TYPE "????" +# define RAW_CREA "UNIX" + +/* Copy routines =========================================================== */ + +/* + * NAME: fork->macb() + * DESCRIPTION: copy a single fork for MacBinary II + */ +static +int fork_macb(int ifile, hfsfile *ofile, unsigned long size) +{ + char buf[HFS_BLOCKSZ * 4]; + unsigned long chunk, bytes; + + while (size) + { + chunk = (size < sizeof(buf)) ? + (size + (MACB_BLOCKSZ - 1)) & ~(MACB_BLOCKSZ - 1) : sizeof(buf); + + bytes = read(ifile, buf, chunk); + if (bytes == (unsigned long) -1) + { + ERROR(errno, "error reading data"); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "read incomplete chunk"); + return -1; + } + + chunk = (size > bytes) ? bytes : size; + + bytes = hfs_write(ofile, buf, chunk); + if (bytes == (unsigned long) -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + + size -= chunk; + } + + return 0; +} + +/* + * NAME: do_macb() + * DESCRIPTION: perform copy using MacBinary II translation + */ +static +int do_macb(int ifile, hfsfile *ofile, + unsigned long dsize, unsigned long rsize) +{ + if (hfs_setfork(ofile, 0) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_macb(ifile, ofile, dsize) == -1) + return -1; + + if (hfs_setfork(ofile, 1) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_macb(ifile, ofile, rsize) == -1) + return -1; + + return 0; +} + +/* + * NAME: fork->binh() + * DESCRIPTION: copy a single fork for BinHex + */ +static +int fork_binh(hfsfile *ofile, unsigned long size) +{ + char buf[HFS_BLOCKSZ * 4]; + long chunk, bytes; + + while (size) + { + chunk = (size > sizeof(buf)) ? sizeof(buf) : size; + + bytes = bh_read(buf, chunk); + if (bytes == -1) + { + ERROR(errno, bh_error); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "read incomplete chunk"); + return -1; + } + + bytes = hfs_write(ofile, buf, chunk); + if (bytes == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + + size -= chunk; + } + + if (bh_readcrc() == -1) + { + ERROR(errno, bh_error); + return -1; + } + + return 0; +} + +/* + * NAME: do_binh() + * DESCRIPTION: perform copy using BinHex translation + */ +static +int do_binh(hfsfile *ofile, unsigned long dsize, unsigned long rsize) +{ + if (hfs_setfork(ofile, 0) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_binh(ofile, dsize) == -1) + return -1; + + if (hfs_setfork(ofile, 1) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_binh(ofile, rsize) == -1) + return -1; + + return 0; +} + +/* + * NAME: do_text() + * DESCRIPTION: perform copy using text translation + */ +static +int do_text(int ifile, hfsfile *ofile) +{ + char buf[HFS_BLOCKSZ * 4], *ptr; + long chunk, bytes; + int len; + + while (1) + { + chunk = read(ifile, buf, sizeof(buf)); + if (chunk == -1) + { + ERROR(errno, "error reading source file"); + return -1; + } + else if (chunk == 0) + break; + + for (ptr = buf; ptr < buf + chunk; ++ptr) + { + if (*ptr == '\n') + *ptr = '\r'; + } + + len = chunk; + ptr = cs_macroman(buf, &len); + if (ptr == 0) + { + ERROR(ENOMEM, 0); + return -1; + } + + bytes = hfs_write(ofile, ptr, len); + free(ptr); + + if (bytes == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (bytes != len) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + } + + return 0; +} + +/* + * NAME: do_raw() + * DESCRIPTION: perform copy using no translation + */ +static +int do_raw(int ifile, hfsfile *ofile) +{ + char buf[HFS_BLOCKSZ * 4]; + long chunk, bytes; + + while (1) + { + chunk = read(ifile, buf, sizeof(buf)); + if (chunk == -1) + { + ERROR(errno, "error reading source file"); + return -1; + } + else if (chunk == 0) + break; + + bytes = hfs_write(ofile, buf, chunk); + if (bytes == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + } + + return 0; +} + +/* Utility Routines ======================================================== */ + +/* + * NAME: opensrc() + * DESCRIPTION: open the source file; set hint for destination filename + */ +static +int opensrc(const char *srcname, const char **dsthint, const char *ext, + int binary) +{ + int fd, len; + static char name[HFS_MAX_FLEN + 1]; + const char *cptr; + char *ptr; + + if (strcmp(srcname, "-") == 0) + { + fd = dup(STDIN_FILENO); + srcname = ""; + } + else + fd = open(srcname, O_RDONLY); + + if (fd == -1) + { + ERROR(errno, "error opening source file"); + return -1; + } + + cptr = strrchr(srcname, '/'); + if (cptr == 0) + cptr = srcname; + else + ++cptr; + + if (ext == 0) + len = strlen(cptr); + else + { + ext = strstr(cptr, ext); + if (ext == 0) + len = strlen(cptr); + else + len = ext - cptr; + } + + if (len > HFS_MAX_FLEN) + len = HFS_MAX_FLEN; + + memcpy(name, cptr, len); + name[len] = 0; + + for (ptr = name; *ptr; ++ptr) + { + switch (*ptr) + { + case ':': + *ptr = '-'; + break; + + case '_': + *ptr = ' '; + break; + } + } + + *dsthint = name; + + return fd; +} + +/* + * NAME: opendst() + * DESCRIPTION: open the destination file + */ +static +hfsfile *opendst(hfsvol *vol, const char *dstname, const char *hint, + const char *type, const char *creator) +{ + hfsdirent ent; + hfsfile *file; + unsigned long cwd; + + if (hfs_stat(vol, dstname, &ent) != -1 && + (ent.flags & HFS_ISDIR)) + { + cwd = hfs_getcwd(vol); + + if (hfs_setcwd(vol, ent.cnid) == -1) + { + ERROR(errno, hfs_error); + return 0; + } + + dstname = hint; + } + + hfs_delete(vol, dstname); + + file = hfs_create(vol, dstname, type, creator); + if (file == 0) + { + ERROR(errno, hfs_error); + + if (dstname == hint) + hfs_setcwd(vol, cwd); + + return 0; + } + + if (dstname == hint) + { + if (hfs_setcwd(vol, cwd) == -1) + { + ERROR(errno, hfs_error); + + hfs_close(file); + return 0; + } + } + + return file; +} + +/* + * NAME: closefiles() + * DESCRIPTION: close source and destination files + */ +static +void closefiles(int ifile, hfsfile *ofile, int *result) +{ + if (ofile && hfs_close(ofile) == -1 && *result == 0) + { + ERROR(errno, hfs_error); + *result = -1; + } + + if (close(ifile) == -1 && *result == 0) + { + ERROR(errno, "error closing source file"); + *result = -1; + } +} + +/* Interface Routines ====================================================== */ + +/* + * NAME: cpi->macb() + * DESCRIPTION: copy a UNIX file to an HFS file using MacBinary II translation + */ +int cpi_macb(const char *srcname, hfsvol *vol, const char *dstname) +{ + int ifile, result = 0; + hfsfile *ofile; + hfsdirent ent; + const char *dsthint; + char type[5], creator[5]; + unsigned char buf[MACB_BLOCKSZ]; + unsigned short crc; + unsigned long dsize, rsize; + + ifile = opensrc(srcname, &dsthint, ".bin", 1); + if (ifile == -1) + return -1; + + if (read(ifile, buf, MACB_BLOCKSZ) < MACB_BLOCKSZ) + { + ERROR(errno, "error reading MacBinary file header"); + + close(ifile); + return -1; + } + + if (buf[0] != 0 || buf[74] != 0) + { + ERROR(EINVAL, "invalid MacBinary file header"); + + close(ifile); + return -1; + } + + crc = d_getuw(&buf[124]); + + if (crc_macb(buf, 124, 0x0000) != crc) + { + /* (buf[82] == 0) => MacBinary I? */ + + ERROR(EINVAL, "unknown, unsupported, or corrupt MacBinary file"); + + close(ifile); + return -1; + } + + if (buf[123] > 129) + { + ERROR(EINVAL, "unsupported MacBinary file version"); + + close(ifile); + return -1; + } + + if (buf[1] < 1 || buf[1] > 63 || + buf[2 + buf[1]] != 0) + { + ERROR(EINVAL, "invalid MacBinary file header (bad file name)"); + + close(ifile); + return -1; + } + + dsize = d_getul(&buf[83]); + rsize = d_getul(&buf[87]); + + if (dsize > 0x7fffffff || rsize > 0x7fffffff) + { + ERROR(EINVAL, "invalid MacBinary file header (bad file length)"); + + close(ifile); + return -1; + } + + dsthint = (char *) &buf[2]; + + memcpy(type, &buf[65], 4); + memcpy(creator, &buf[69], 4); + type[4] = creator[4] = 0; + + ofile = opendst(vol, dstname, dsthint, type, creator); + if (ofile == 0) + { + close(ifile); + return -1; + } + + result = do_macb(ifile, ofile, dsize, rsize); + + if (result == 0 && hfs_fstat(ofile, &ent) == -1) + { + ERROR(errno, hfs_error); + result = -1; + } + + ent.fdflags = (buf[73] << 8 | buf[101]) & + ~(HFS_FNDR_ISONDESK | HFS_FNDR_HASBEENINITED | HFS_FNDR_RESERVED); + + ent.crdate = d_ltime(d_getul(&buf[91])); + ent.mddate = d_ltime(d_getul(&buf[95])); + + if (result == 0 && hfs_fsetattr(ofile, &ent) == -1) + { + ERROR(errno, hfs_error); + result = -1; + } + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: binhx() + * DESCRIPTION: auxiliary BinHex routine + */ +static +int binhx(char *fname, char *type, char *creator, short *fdflags, + unsigned long *dsize, unsigned long *rsize) +{ + int len; + unsigned char byte, word[2], lword[4]; + + if (bh_read(&byte, 1) < 1) + { + ERROR(errno, bh_error); + return -1; + } + + len = (unsigned char) byte; + + if (len < 1 || len > HFS_MAX_FLEN) + { + ERROR(EINVAL, "invalid BinHex file header (bad file name)"); + return -1; + } + + if (bh_read(fname, len + 1) < len + 1) + { + ERROR(errno, bh_error); + return -1; + } + + if (fname[len] != 0) + { + ERROR(EINVAL, "invalid BinHex file header (bad file name)"); + return -1; + } + + if (bh_read(type, 4) < 4 || + bh_read(creator, 4) < 4 || + bh_read(word, 2) < 2) + { + ERROR(errno, bh_error); + return -1; + } + *fdflags = d_getsw(word); + + if (bh_read(lword, 4) < 4) + { + ERROR(errno, bh_error); + return -1; + } + *dsize = d_getul(lword); + + if (bh_read(lword, 4) < 4) + { + ERROR(errno, bh_error); + return -1; + } + *rsize = d_getul(lword); + + if (*dsize > 0x7fffffff || *rsize > 0x7fffffff) + { + ERROR(EINVAL, "invalid BinHex file header (bad file length)"); + return -1; + } + + if (bh_readcrc() == -1) + { + ERROR(errno, bh_error); + return -1; + } + + return 0; +} + +/* + * NAME: cpi->binh() + * DESCRIPTION: copy a UNIX file to an HFS file using BinHex translation + */ +int cpi_binh(const char *srcname, hfsvol *vol, const char *dstname) +{ + int ifile, result; + hfsfile *ofile; + hfsdirent ent; + const char *dsthint; + char fname[HFS_MAX_FLEN + 1], type[5], creator[5]; + short fdflags; + unsigned long dsize, rsize; + + ifile = opensrc(srcname, &dsthint, ".hqx", 0); + if (ifile == -1) + return -1; + + if (bh_open(ifile) == -1) + { + ERROR(errno, bh_error); + + close(ifile); + return -1; + } + + if (binhx(fname, type, creator, &fdflags, &dsize, &rsize) == -1) + { + bh_close(); + close(ifile); + return -1; + } + + dsthint = fname; + + ofile = opendst(vol, dstname, dsthint, type, creator); + if (ofile == 0) + { + bh_close(); + close(ifile); + return -1; + } + + result = do_binh(ofile, dsize, rsize); + + if (bh_close() == -1 && result == 0) + { + ERROR(errno, bh_error); + result = -1; + } + + if (result == 0 && hfs_fstat(ofile, &ent) == -1) + { + ERROR(errno, hfs_error); + result = -1; + } + + ent.fdflags = fdflags & + ~(HFS_FNDR_ISONDESK | HFS_FNDR_HASBEENINITED | HFS_FNDR_ISINVISIBLE); + + if (result == 0 && hfs_fsetattr(ofile, &ent) == -1) + { + ERROR(errno, hfs_error); + result = -1; + } + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: cpi->text() + * DESCRIPTION: copy a UNIX file to an HFS file using text translation + */ +int cpi_text(const char *srcname, hfsvol *vol, const char *dstname) +{ + int ifile, result = 0; + hfsfile *ofile; + const char *dsthint; + + ifile = opensrc(srcname, &dsthint, ".txt", 0); + if (ifile == -1) + return -1; + + ofile = opendst(vol, dstname, dsthint, TEXT_TYPE, TEXT_CREA); + if (ofile == 0) + { + close(ifile); + return -1; + } + + result = do_text(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: cpi->raw() + * DESCRIPTION: copy a UNIX file to the data fork of an HFS file + */ +int cpi_raw(const char *srcname, hfsvol *vol, const char *dstname) +{ + int ifile, result = 0; + hfsfile *ofile; + const char *dsthint; + + ifile = opensrc(srcname, &dsthint, 0, 1); + if (ifile == -1) + return -1; + + ofile = opendst(vol, dstname, dsthint, RAW_TYPE, RAW_CREA); + if (ofile == 0) + { + close(ifile); + return -1; + } + + result = do_raw(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} diff --git a/copyin.h b/copyin.h new file mode 100644 index 0000000..430a7c2 --- /dev/null +++ b/copyin.h @@ -0,0 +1,29 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: copyin.h,v 1.6 1998/04/11 08:26:54 rob Exp $ + */ + +extern const char *cpi_error; + +typedef int (*cpifunc)(const char *, hfsvol *, const char *); + +int cpi_macb(const char *, hfsvol *, const char *); +int cpi_binh(const char *, hfsvol *, const char *); +int cpi_text(const char *, hfsvol *, const char *); +int cpi_raw(const char *, hfsvol *, const char *); diff --git a/copyout.c b/copyout.c new file mode 100644 index 0000000..e2f6b4e --- /dev/null +++ b/copyout.c @@ -0,0 +1,652 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: copyout.c,v 1.9 1998/04/11 08:26:54 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_FCNTL_H +# include +# else +int open(const char *, int, ...); +# endif + +# ifdef HAVE_UNISTD_H +# include +# else +int dup(int); +# endif + +# include +# include +# include +# include + +# include "hfs.h" +# include "data.h" +# include "copyout.h" +# include "charset.h" +# include "binhex.h" +# include "crc.h" + +const char *cpo_error = "no error"; + +extern int errno; + +# define ERROR(code, str) (cpo_error = (str), errno = (code)) + +# define MACB_BLOCKSZ 128 + +/* Copy Routines =========================================================== */ + +/* + * NAME: fork->macb() + * DESCRIPTION: copy a single fork for MacBinary II + */ +static +int fork_macb(hfsfile *ifile, int ofile, unsigned long size) +{ + char buf[HFS_BLOCKSZ * 4]; + long chunk, bytes; + unsigned long total = 0; + + while (1) + { + chunk = hfs_read(ifile, buf, sizeof(buf)); + if (chunk == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (chunk == 0) + break; + + bytes = write(ofile, buf, chunk); + if (bytes == -1) + { + ERROR(errno, "error writing data"); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + + total += bytes; + } + + if (total != size) + { + ERROR(EIO, "inconsistent fork length"); + return -1; + } + + chunk = total % MACB_BLOCKSZ; + if (chunk) + { + memset(buf, 0, MACB_BLOCKSZ); + bytes = write(ofile, buf, MACB_BLOCKSZ - chunk); + if (bytes == -1) + { + ERROR(errno, "error writing data"); + return -1; + } + else if (bytes != MACB_BLOCKSZ - chunk) + { + ERROR(EIO, "wrong incomplete chunk"); + return -1; + } + } + + return 0; +} + +/* + * NAME: do_macb() + * DESCRIPTION: perform copy using MacBinary II translation + */ +static +int do_macb(hfsfile *ifile, int ofile) +{ + hfsdirent ent; + unsigned char buf[MACB_BLOCKSZ]; + long bytes; + + if (hfs_fstat(ifile, &ent) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + memset(buf, 0, MACB_BLOCKSZ); + + buf[1] = strlen(ent.name); + strcpy((char *) &buf[2], ent.name); + + memcpy(&buf[65], ent.u.file.type, 4); + memcpy(&buf[69], ent.u.file.creator, 4); + + buf[73] = ent.fdflags >> 8; + + d_putul(&buf[83], ent.u.file.dsize); + d_putul(&buf[87], ent.u.file.rsize); + + d_putul(&buf[91], d_mtime(ent.crdate)); + d_putul(&buf[95], d_mtime(ent.mddate)); + + buf[101] = ent.fdflags & 0xff; + buf[122] = buf[123] = 129; + + d_putuw(&buf[124], crc_macb(buf, 124, 0x0000)); + + bytes = write(ofile, buf, MACB_BLOCKSZ); + if (bytes == -1) + { + ERROR(errno, "error writing data"); + return -1; + } + else if (bytes != MACB_BLOCKSZ) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + + if (hfs_setfork(ifile, 0) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_macb(ifile, ofile, ent.u.file.dsize) == -1) + return -1; + + if (hfs_setfork(ifile, 1) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_macb(ifile, ofile, ent.u.file.rsize) == -1) + return -1; + + return 0; +} + +/* + * NAME: fork->binh() + * DESCRIPTION: copy a single fork for BinHex + */ +static +int fork_binh(hfsfile *ifile, unsigned long size) +{ + char buf[HFS_BLOCKSZ * 4]; + long bytes; + unsigned long total = 0; + + while (1) + { + bytes = hfs_read(ifile, buf, sizeof(buf)); + if (bytes == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (bytes == 0) + break; + + if (bh_insert(buf, bytes) == -1) + { + ERROR(errno, bh_error); + return -1; + } + + total += bytes; + } + + if (total != size) + { + ERROR(EIO, "inconsistent fork length"); + return -1; + } + + if (bh_insertcrc() == -1) + { + ERROR(errno, bh_error); + return -1; + } + + return 0; +} + +/* + * NAME: binhx() + * DESCRIPTION: auxiliary BinHex routine + */ +static +int binhx(hfsfile *ifile) +{ + hfsdirent ent; + unsigned char byte, word[2], lword[4]; + + if (hfs_fstat(ifile, &ent) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + byte = strlen(ent.name); + if (bh_insert(&byte, 1) == -1 || + bh_insert(ent.name, byte + 1) == -1 || + bh_insert(ent.u.file.type, 4) == -1 || + bh_insert(ent.u.file.creator, 4) == -1) + { + ERROR(errno, bh_error); + return -1; + } + + d_putsw(word, ent.fdflags); + if (bh_insert(word, 2) == -1) + { + ERROR(errno, bh_error); + return -1; + } + + d_putul(lword, ent.u.file.dsize); + if (bh_insert(lword, 4) == -1) + { + ERROR(errno, bh_error); + return -1; + } + + d_putul(lword, ent.u.file.rsize); + if (bh_insert(lword, 4) == -1 || + bh_insertcrc() == -1) + { + ERROR(errno, bh_error); + return -1; + } + + if (hfs_setfork(ifile, 0) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_binh(ifile, ent.u.file.dsize) == -1) + return -1; + + if (hfs_setfork(ifile, 1) == -1) + { + ERROR(errno, hfs_error); + return -1; + } + + if (fork_binh(ifile, ent.u.file.rsize) == -1) + return -1; + + return 0; +} + +/* + * NAME: do_binh() + * DESCRIPTION: perform copy using BinHex translation + */ +static +int do_binh(hfsfile *ifile, int ofile) +{ + int result; + + if (bh_start(ofile) == -1) + { + ERROR(errno, bh_error); + return -1; + } + + result = binhx(ifile); + + if (bh_end() == -1 && result == 0) + { + ERROR(errno, bh_error); + result = -1; + } + + return result; +} + +/* + * NAME: do_text() + * DESCRIPTION: perform copy using text translation + */ +static +int do_text(hfsfile *ifile, int ofile) +{ + char buf[HFS_BLOCKSZ * 4], *ptr; + long chunk, bytes; + int len; + + while (1) + { + chunk = hfs_read(ifile, buf, sizeof(buf)); + if (chunk == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (chunk == 0) + break; + + for (ptr = buf; ptr < buf + chunk; ++ptr) + { + if (*ptr == '\r') + *ptr = '\n'; + } + + len = chunk; + ptr = cs_latin1(buf, &len); + if (ptr == 0) + { + ERROR(ENOMEM, 0); + return -1; + } + + bytes = write(ofile, ptr, len); + free(ptr); + + if (bytes == -1) + { + ERROR(errno, "error writing data"); + return -1; + } + else if (bytes != len) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + } + + return 0; +} + +/* + * NAME: do_raw() + * DESCRIPTION: perform copy using no translation + */ +static +int do_raw(hfsfile *ifile, int ofile) +{ + char buf[HFS_BLOCKSZ * 4]; + long chunk, bytes; + + while (1) + { + chunk = hfs_read(ifile, buf, sizeof(buf)); + if (chunk == -1) + { + ERROR(errno, hfs_error); + return -1; + } + else if (chunk == 0) + break; + + bytes = write(ofile, buf, chunk); + if (bytes == -1) + { + ERROR(errno, "error writing data"); + return -1; + } + else if (bytes != chunk) + { + ERROR(EIO, "wrote incomplete chunk"); + return -1; + } + } + + return 0; +} + +/* Utility Routines ======================================================== */ + +/* + * NAME: opensrc() + * DESCRIPTION: open the source file; set hint for destination filename + */ +static +hfsfile *opensrc(hfsvol *vol, const char *srcname, + const char **dsthint, const char *ext) +{ + hfsfile *file; + hfsdirent ent; + static char name[HFS_MAX_FLEN + 4 + 1]; + char *ptr; + + file = hfs_open(vol, srcname); + if (file == 0) + { + ERROR(errno, hfs_error); + return 0; + } + + if (hfs_fstat(file, &ent) == -1) + { + ERROR(errno, hfs_error); + + hfs_close(file); + return 0; + } + + strcpy(name, ent.name); + + for (ptr = name; *ptr; ++ptr) + { + switch (*ptr) + { + case '/': + *ptr = '-'; + break; + + case ' ': + *ptr = '_'; + break; + } + } + + if (ext) + strcat(name, ext); + + *dsthint = name; + + return file; +} + +/* + * NAME: opendst() + * DESCRIPTION: open the destination file + */ +static +int opendst(const char *dstname, const char *hint) +{ + int fd; + + if (strcmp(dstname, "-") == 0) + fd = dup(STDOUT_FILENO); + else + { + struct stat sbuf; + char *path = 0; + + if (stat(dstname, &sbuf) != -1 && + S_ISDIR(sbuf.st_mode)) + { + path = malloc(strlen(dstname) + 1 + strlen(hint) + 1); + if (path == 0) + { + ERROR(ENOMEM, 0); + return -1; + } + + strcpy(path, dstname); + strcat(path, "/"); + strcat(path, hint); + + dstname = path; + } + + fd = open(dstname, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if (path) + free(path); + } + + if (fd == -1) + { + ERROR(errno, "error opening destination file"); + return -1; + } + + return fd; +} + +/* + * NAME: openfiles() + * DESCRIPTION: open source and destination files + */ +static +int openfiles(hfsvol *vol, const char *srcname, const char *dstname, + const char *ext, hfsfile **ifile, int *ofile) +{ + const char *dsthint; + + *ifile = opensrc(vol, srcname, &dsthint, ext); + if (*ifile == 0) + return -1; + + *ofile = opendst(dstname, dsthint); + if (*ofile == -1) + { + hfs_close(*ifile); + return -1; + } + + return 0; +} + +/* + * NAME: closefiles() + * DESCRIPTION: close source and destination files + */ +static +void closefiles(hfsfile *ifile, int ofile, int *result) +{ + if (close(ofile) == -1 && *result == 0) + { + ERROR(errno, "error closing destination file"); + *result = -1; + } + + if (hfs_close(ifile) == -1 && *result == 0) + { + ERROR(errno, hfs_error); + *result = -1; + } +} + +/* Interface Routines ====================================================== */ + +/* + * NAME: cpo->macb() + * DESCRIPTION: copy an HFS file to a UNIX file using MacBinary II translation + */ +int cpo_macb(hfsvol *vol, const char *srcname, const char *dstname) +{ + hfsfile *ifile; + int ofile, result = 0; + + if (openfiles(vol, srcname, dstname, ".bin", &ifile, &ofile) == -1) + return -1; + + result = do_macb(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: cpo->binh() + * DESCRIPTION: copy an HFS file to a UNIX file using BinHex translation + */ +int cpo_binh(hfsvol *vol, const char *srcname, const char *dstname) +{ + hfsfile *ifile; + int ofile, result; + + if (openfiles(vol, srcname, dstname, ".hqx", &ifile, &ofile) == -1) + return -1; + + result = do_binh(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: cpo->text() + * DESCRIPTION: copy an HFS file to a UNIX file using text translation + */ +int cpo_text(hfsvol *vol, const char *srcname, const char *dstname) +{ + const char *ext = 0; + hfsfile *ifile; + int ofile, result = 0; + + if (strchr(srcname, '.') == 0) + ext = ".txt"; + + if (openfiles(vol, srcname, dstname, ext, &ifile, &ofile) == -1) + return -1; + + result = do_text(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} + +/* + * NAME: cpo->raw() + * DESCRIPTION: copy the data fork of an HFS file to a UNIX file + */ +int cpo_raw(hfsvol *vol, const char *srcname, const char *dstname) +{ + hfsfile *ifile; + int ofile, result = 0; + + if (openfiles(vol, srcname, dstname, 0, &ifile, &ofile) == -1) + return -1; + + result = do_raw(ifile, ofile); + + closefiles(ifile, ofile, &result); + + return result; +} diff --git a/copyout.h b/copyout.h new file mode 100644 index 0000000..74d6f2c --- /dev/null +++ b/copyout.h @@ -0,0 +1,29 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: copyout.h,v 1.7 1998/04/11 08:26:54 rob Exp $ + */ + +extern const char *cpo_error; + +typedef int (*cpofunc)(hfsvol *, const char *, const char *); + +int cpo_macb(hfsvol *, const char *, const char *); +int cpo_binh(hfsvol *, const char *, const char *); +int cpo_text(hfsvol *, const char *, const char *); +int cpo_raw(hfsvol *, const char *, const char *); diff --git a/crc.c b/crc.c new file mode 100644 index 0000000..5ff8ae3 --- /dev/null +++ b/crc.c @@ -0,0 +1,98 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: crc.c,v 1.8 1998/09/18 22:56:24 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include "crc.h" + +static const +unsigned short magic[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/* + * NAME: crc->binh() + * DESCRIPTION: compute BinHex-style CRC value for a block of data + */ +unsigned short crc_binh(register const unsigned char *ptr, register int count, + register unsigned short crc) +{ + while (count--) + crc = ((crc << 8) | *ptr++) ^ magic[crc >> 8]; + + return crc; +} + +/* + * NAME: crc->macb() + * DESCRIPTION: compute MacBinary II-style CRC value for a block of data + */ +unsigned short crc_macb(register const unsigned char *ptr, register int count, + register unsigned short crc) +{ + while (count--) + { + crc ^= *ptr++ << 8; + crc = (crc << 8) ^ magic[crc >> 8]; + } + + return crc; +} diff --git a/crc.h b/crc.h new file mode 100644 index 0000000..4aaa716 --- /dev/null +++ b/crc.h @@ -0,0 +1,25 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: crc.h,v 1.7 1998/04/11 08:26:55 rob Exp $ + */ + +unsigned short crc_binh(register const unsigned char *, register int, + register unsigned short); +unsigned short crc_macb(register const unsigned char *, register int, + register unsigned short); diff --git a/darray.c b/darray.c new file mode 100644 index 0000000..11b8204 --- /dev/null +++ b/darray.c @@ -0,0 +1,141 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: darray.c,v 1.7 1998/11/02 22:08:26 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "darray.h" + +struct _darray_ { + char *mem; + size_t memsz; + char *eltend; + size_t elemsz; +}; + +/* + * NAME: darray->new() + * DESCRIPTION: allocate and return a new dynamic array + */ +darray *darr_new(size_t elemsz) +{ + darray *array; + + array = malloc(sizeof(darray)); + if (array == 0) + return 0; + + array->memsz = 8 * elemsz; + array->mem = malloc(array->memsz); + if (array->mem == 0) + { + free(array); + return 0; + } + + array->eltend = array->mem; + array->elemsz = elemsz; + + return array; +} + +/* + * NAME: darray->free() + * DESCRIPTION: dispose of a dynamic array + */ +void darr_free(darray *array) +{ + free(array->mem); + free(array); +} + +/* + * NAME: darray->size() + * DESCRIPTION: return the number of elements in a dynamic array + */ +unsigned int darr_size(darray *array) +{ + return (array->eltend - array->mem) / array->elemsz; +} + +/* + * NAME: darray->array() + * DESCRIPTION: return the array as an indexable block + */ +void *darr_array(darray *array) +{ + return (void *) array->mem; +} + +/* + * NAME: darray->append() + * DESCRIPTION: add an element to the end of a dynamic array + */ +void *darr_append(darray *array, void *elem) +{ + char *eltend = array->eltend; + size_t elemsz = array->elemsz; + + if ((size_t) (eltend - array->mem) == array->memsz) + { + char *newmem; + size_t newsz; + + newsz = array->memsz * 2; + newmem = realloc(array->mem, newsz); + if (newmem == 0) + return 0; + + eltend = newmem + array->memsz; + + array->mem = newmem; + array->memsz = newsz; + } + + memcpy(eltend, elem, elemsz); + array->eltend = eltend + elemsz; + + return eltend; +} + +/* + * NAME: darray->shrink() + * DESCRIPTION: truncate elements from the end of a dynamic array + */ +void darr_shrink(darray *array, unsigned int nelts) +{ + if (nelts < (array->eltend - array->mem) / array->elemsz) + array->eltend = array->mem + nelts * array->elemsz; +} + +/* + * NAME: darray->sort() + * DESCRIPTION: arrange items according to sorting criteria + */ +void darr_sort(darray *array, int (*compare)(const void *, const void *)) +{ + qsort(array->mem, (array->eltend - array->mem) / array->elemsz, + array->elemsz, compare); +} diff --git a/darray.h b/darray.h new file mode 100644 index 0000000..bf81620 --- /dev/null +++ b/darray.h @@ -0,0 +1,30 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: darray.h,v 1.6 1998/04/11 08:26:55 rob Exp $ + */ + +typedef struct _darray_ darray; + +darray *darr_new(size_t); +void darr_free(darray *); +unsigned int darr_size(darray *); +void *darr_array(darray *); +void *darr_append(darray *, void *); +void darr_shrink(darray *, unsigned int); +void darr_sort(darray *, int (*)(const void *, const void *)); diff --git a/dlist.c b/dlist.c new file mode 100644 index 0000000..c1d2e19 --- /dev/null +++ b/dlist.c @@ -0,0 +1,119 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: dlist.c,v 1.7 1998/11/02 22:08:27 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "dlist.h" + +/* + * NAME: dlist->init() + * DESCRIPTION: initialize a new dynamic list + */ +int dl_init(dlist *list) +{ + list->memsz = 100; + list->mem = malloc(list->memsz); + if (list->mem == 0) + return -1; + + list->eltend = (char **) list->mem; + list->strs = list->mem + list->memsz; + + return 0; +} + +/* + * NAME: dlist->free() + * DESCRIPTION: dispose of a dynamic list + */ +void dl_free(dlist *list) +{ + free(list->mem); +} + +/* + * NAME: dlist->array() + * DESCRIPTION: return the array of strings in a list; can dispose with free() + */ +char **dl_array(dlist *list) +{ + return (char **) list->mem; +} + +/* + * NAME: dlist->size() + * DESCRIPTION: return the number of strings in a list + */ +int dl_size(dlist *list) +{ + return list->eltend - (char **) list->mem; +} + +/* + * NAME: dlist->append() + * DESCRIPTION: insert a string to the end of a list + */ +int dl_append(dlist *list, const char *str) +{ + size_t len; + + len = strlen(str) + 1; + + /* make sure there is room */ + + if (sizeof(char *) + len > (size_t) (list->strs - (char *) list->eltend)) + { + dlist newlist; + size_t strsz; + char **elt; + + strsz = (list->mem + list->memsz) - list->strs; + + newlist.memsz = list->memsz * 2 + sizeof(char *) + len; + newlist.mem = malloc(newlist.memsz); + if (newlist.mem == 0) + return -1; + + newlist.eltend = (char **) newlist.mem; + newlist.strs = newlist.mem + newlist.memsz - strsz; + + memcpy(newlist.strs, list->strs, strsz); + + for (elt = (char **) list->mem; elt < list->eltend; ++elt) + *newlist.eltend++ = newlist.strs + (*elt - list->strs); + + free(list->mem); + + *list = newlist; + } + + list->strs -= len; + strcpy(list->strs, str); + + *list->eltend++ = list->strs; + + return 0; +} diff --git a/dlist.h b/dlist.h new file mode 100644 index 0000000..191701d --- /dev/null +++ b/dlist.h @@ -0,0 +1,33 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: dlist.h,v 1.6 1998/04/11 08:26:55 rob Exp $ + */ + +typedef struct { + char *mem; + size_t memsz; + char **eltend; + char *strs; +} dlist; + +int dl_init(dlist *); +void dl_free(dlist *); +char **dl_array(dlist *); +int dl_size(dlist *); +int dl_append(dlist *, const char *); diff --git a/doc/charset.txt b/doc/charset.txt new file mode 100644 index 0000000..3d8002b --- /dev/null +++ b/doc/charset.txt @@ -0,0 +1,249 @@ + +hfsutils - tools for reading and writing Macintosh HFS volumes +Copyright (C) 1996-1998 Robert Leslie + +$Id: charset.txt,v 1.4 1998/11/02 22:08:46 rob Exp $ + +=============================================================================== + +HFS uses the MacOS Standard Roman character set for volume, file, and path +names, as well as potentially for the data contained within some files. + +Although MacOS Standard Roman shares the same characters as US ASCII for +values 0x00 through 0x7F, the remaining characters have values which are +primarily unique to MacOS Standard Roman. + +The following is a translation table from MacOS Standard Roman to Unicode. + +Note that ISO 8859-1 is a subset of Unicode, directly mapped from U+0000 +through U+00FF. Since the Unicode values for MacOS Standard Roman characters +do not all fall within this range, it is not possible to map MacOS Standard +Roman completely and directly to ISO 8859-1 (and, incidentally, neither is it +possible to map ISO 8859-1 completely and directly to MacOS Standard Roman.) + +(All numeric values in this table are hexadecimal.) + +Part I: Control Characters (00 - 1F) + ++----+--------+-----------------------+----+--------+-------------------------- +|ASR |Unicode | Character |ASR |Unicode | Character ++----+--------+-----------------------+----+--------+-------------------------- +| 00 | U+0000 | NULL | 10 | U+0010 | DATA LINK ESCAPE +| 01 | U+0001 | START OF HEADING | 11 | U+0011 | DEVICE CONTROL ONE +| 02 | U+0002 | START OF TEXT | 12 | U+0012 | DEVICE CONTROL TWO +| 03 | U+0003 | END OF TEXT | 13 | U+0013 | DEVICE CONTROL THREE +| 04 | U+0004 | END OF TRANSMISSION | 14 | U+0014 | DEVICE CONTROL FOUR +| 05 | U+0005 | ENQUIRY | 15 | U+0015 | NEGATIVE ACKNOWLEDGE +| 06 | U+0006 | ACKNOWLEDGE | 16 | U+0016 | SYNCHRONOUS IDLE +| 07 | U+0007 | BELL | 17 | U+0017 | END OF TRANSMISSION BLOCK +| 08 | U+0008 | BACKSPACE | 18 | U+0018 | CANCEL +| 09 | U+0009 | HORIZONTAL TABULATION | 19 | U+0019 | END OF MEDIUM +| 0A | U+000A | LINE FEED | 1A | U+001A | SUBSTITUTE +| 0B | U+000B | VERTICAL TABULATION | 1B | U+001B | ESCAPE +| 0C | U+000C | FORM FEED | 1C | U+001C | FILE SEPARATOR +| 0D | U+000D | CARRIAGE RETURN | 1D | U+001D | GROUP SEPARATOR +| 0E | U+000E | SHIFT OUT | 1E | U+001E | RECORD SEPARATOR +| 0F | U+000F | SHIFT IN | 1F | U+001F | UNIT SEPARATOR ++----+--------+-----------------------+----+--------+-------------------------- + +Part II: Printable ASCII Characters and Delete (20 - 7E, and 7F) + ++----+--------+-------------------------+----+--------+------------------------ +|ASR |Unicode | Character |ASR |Unicode | Character ++----+--------+-------------------------+----+--------+------------------------ +| 20 | U+0020 | SPACE | 30 | U+0030 | DIGIT ZERO +| 21 | U+0021 | EXCLAMATION MARK | 31 | U+0031 | DIGIT ONE +| 22 | U+0022 | QUOTATION MARK | 32 | U+0032 | DIGIT TWO +| 23 | U+0023 | NUMBER SIGN | 33 | U+0033 | DIGIT THREE +| 24 | U+0024 | DOLLAR SIGN | 34 | U+0034 | DIGIT FOUR +| 25 | U+0025 | PERCENT SIGN | 35 | U+0035 | DIGIT FIVE +| 26 | U+0026 | AMPERSAND | 36 | U+0036 | DIGIT SIX +| 27 | U+0027 | APOSTROPHE | 37 | U+0037 | DIGIT SEVEN +| 28 | U+0028 | LEFT PARENTHESIS | 38 | U+0038 | DIGIT EIGHT +| 29 | U+0029 | RIGHT PARENTHESIS | 39 | U+0039 | DIGIT NINE +| 2A | U+002A | ASTERISK | 3A | U+003A | COLON +| 2B | U+002B | PLUS SIGN | 3B | U+003B | SEMICOLON +| 2C | U+002C | COMMA | 3C | U+003C | LESS-THAN SIGN +| 2D | U+002D | HYPHEN-MINUS | 3D | U+003D | EQUALS SIGN +| 2E | U+002E | FULL STOP | 3E | U+003E | GREATER-THAN SIGN +| 2F | U+002F | SOLIDUS | 3F | U+003F | QUESTION MARK ++----+--------+-------------------------+----+--------+------------------------ +| 40 | U+0040 | COMMERCIAL AT | 50 | U+0050 | LATIN CAPITAL LETTER P +| 41 | U+0041 | LATIN CAPITAL LETTER A | 51 | U+0051 | LATIN CAPITAL LETTER Q +| 42 | U+0042 | LATIN CAPITAL LETTER B | 52 | U+0052 | LATIN CAPITAL LETTER R +| 43 | U+0043 | LATIN CAPITAL LETTER C | 53 | U+0053 | LATIN CAPITAL LETTER S +| 44 | U+0044 | LATIN CAPITAL LETTER D | 54 | U+0054 | LATIN CAPITAL LETTER T +| 45 | U+0045 | LATIN CAPITAL LETTER E | 55 | U+0055 | LATIN CAPITAL LETTER U +| 46 | U+0046 | LATIN CAPITAL LETTER F | 56 | U+0056 | LATIN CAPITAL LETTER V +| 47 | U+0047 | LATIN CAPITAL LETTER G | 57 | U+0057 | LATIN CAPITAL LETTER W +| 48 | U+0048 | LATIN CAPITAL LETTER H | 58 | U+0058 | LATIN CAPITAL LETTER X +| 49 | U+0049 | LATIN CAPITAL LETTER I | 59 | U+0059 | LATIN CAPITAL LETTER Y +| 4A | U+004A | LATIN CAPITAL LETTER J | 5A | U+005A | LATIN CAPITAL LETTER Z +| 4B | U+004B | LATIN CAPITAL LETTER K | 5B | U+005B | LEFT SQUARE BRACKET +| 4C | U+004C | LATIN CAPITAL LETTER L | 5C | U+005C | REVERSE SOLIDUS +| 4D | U+004D | LATIN CAPITAL LETTER M | 5D | U+005D | RIGHT SQUARE BRACKET +| 4E | U+004E | LATIN CAPITAL LETTER N | 5E | U+005E | CIRCUMFLEX ACCENT +| 4F | U+004F | LATIN CAPITAL LETTER O | 5F | U+005F | LOW LINE ++----+--------+-------------------------+----+--------+------------------------ +| 60 | U+0060 | GRAVE ACCENT | 70 | U+0070 | LATIN SMALL LETTER P +| 61 | U+0061 | LATIN SMALL LETTER A | 71 | U+0071 | LATIN SMALL LETTER Q +| 62 | U+0062 | LATIN SMALL LETTER B | 72 | U+0072 | LATIN SMALL LETTER R +| 63 | U+0063 | LATIN SMALL LETTER C | 73 | U+0073 | LATIN SMALL LETTER S +| 64 | U+0064 | LATIN SMALL LETTER D | 74 | U+0074 | LATIN SMALL LETTER T +| 65 | U+0065 | LATIN SMALL LETTER E | 75 | U+0075 | LATIN SMALL LETTER U +| 66 | U+0066 | LATIN SMALL LETTER F | 76 | U+0076 | LATIN SMALL LETTER V +| 67 | U+0067 | LATIN SMALL LETTER G | 77 | U+0077 | LATIN SMALL LETTER W +| 68 | U+0068 | LATIN SMALL LETTER H | 78 | U+0078 | LATIN SMALL LETTER X +| 69 | U+0069 | LATIN SMALL LETTER I | 79 | U+0079 | LATIN SMALL LETTER Y +| 6A | U+006A | LATIN SMALL LETTER J | 7A | U+007A | LATIN SMALL LETTER Z +| 6B | U+006B | LATIN SMALL LETTER K | 7B | U+007B | LEFT CURLY BRACKET +| 6C | U+006C | LATIN SMALL LETTER L | 7C | U+007C | VERTICAL LINE +| 6D | U+006D | LATIN SMALL LETTER M | 7D | U+007D | RIGHT CURLY BRACKET +| 6E | U+006E | LATIN SMALL LETTER N | 7E | U+007E | TILDE +| 6F | U+006F | LATIN SMALL LETTER O | 7F | U+007F | DELETE ++----+--------+-------------------------+----+--------+------------------------ + +Part III: Extended Characters (80 - FF) + ++----+--------+---------------------------------------------------------------- +|ASR |Unicode | Character ++----+--------+---------------------------------------------------------------- +| 80 | U+00C4 | LATIN CAPITAL LETTER A WITH DIAERESIS +| 81 | U+00C5 | LATIN CAPITAL LETTER A WITH RING ABOVE +| 82 | U+00C7 | LATIN CAPITAL LETTER C WITH CEDILLA +| 83 | U+00C9 | LATIN CAPITAL LETTER E WITH ACUTE +| 84 | U+00D1 | LATIN CAPITAL LETTER N WITH TILDE +| 85 | U+00D6 | LATIN CAPITAL LETTER O WITH DIAERESIS +| 86 | U+00DC | LATIN CAPITAL LETTER U WITH DIAERESIS +| 87 | U+00E1 | LATIN SMALL LETTER A WITH ACUTE +| 88 | U+00E0 | LATIN SMALL LETTER A WITH GRAVE +| 89 | U+00E2 | LATIN SMALL LETTER A WITH CIRCUMFLEX +| 8A | U+00E4 | LATIN SMALL LETTER A WITH DIAERESIS +| 8B | U+00E3 | LATIN SMALL LETTER A WITH TILDE +| 8C | U+00E5 | LATIN SMALL LETTER A WITH RING ABOVE +| 8D | U+00E7 | LATIN SMALL LETTER C WITH CEDILLA +| 8E | U+00E9 | LATIN SMALL LETTER E WITH ACUTE +| 8F | U+00E8 | LATIN SMALL LETTER E WITH GRAVE ++----+--------+---------------------------------------------------------------- +| 90 | U+00EA | LATIN SMALL LETTER E WITH CIRCUMFLEX +| 91 | U+00EB | LATIN SMALL LETTER E WITH DIAERESIS +| 92 | U+00ED | LATIN SMALL LETTER I WITH ACUTE +| 93 | U+00EC | LATIN SMALL LETTER I WITH GRAVE +| 94 | U+00EE | LATIN SMALL LETTER I WITH CIRCUMFLEX +| 95 | U+00EF | LATIN SMALL LETTER I WITH DIAERESIS +| 96 | U+00F1 | LATIN SMALL LETTER N WITH TILDE +| 97 | U+00F3 | LATIN SMALL LETTER O WITH ACUTE +| 98 | U+00F2 | LATIN SMALL LETTER O WITH GRAVE +| 99 | U+00F4 | LATIN SMALL LETTER O WITH CIRCUMFLEX +| 9A | U+00F6 | LATIN SMALL LETTER O WITH DIAERESIS +| 9B | U+00F5 | LATIN SMALL LETTER O WITH TILDE +| 9C | U+00FA | LATIN SMALL LETTER U WITH ACUTE +| 9D | U+00F9 | LATIN SMALL LETTER U WITH GRAVE +| 9E | U+00FB | LATIN SMALL LETTER U WITH CIRCUMFLEX +| 9F | U+00FC | LATIN SMALL LETTER U WITH DIAERESIS ++----+--------+---------------------------------------------------------------- +| A0 | U+2020 | DAGGER +| A1 | U+00B0 | DEGREE SIGN +| A2 | U+00A2 | CENT SIGN +| A3 | U+00A3 | POUND SIGN +| A4 | U+00A7 | SECTION SIGN +| A5 | U+2022 | BULLET +| A6 | U+00B6 | PILCROW SIGN +| A7 | U+00DF | LATIN SMALL LETTER SHARP S +| A8 | U+00AE | REGISTERED SIGN +| A9 | U+00A9 | COPYRIGHT SIGN +| AA | U+2122 | TRADE MARK SIGN +| AB | U+00B4 | ACUTE ACCENT +| AC | U+00A8 | DIAERESIS +| AD | U+2260 | NOT EQUAL TO +| AE | U+00C6 | LATIN CAPITAL LIGATURE AE +| AF | U+00D8 | LATIN CAPITAL LETTER O WITH STROKE ++----+--------+---------------------------------------------------------------- +| B0 | U+221E | INFINITY +| B1 | U+00B1 | PLUS-MINUS SIGN +| B2 | U+2264 | LESS-THAN OR EQUAL TO +| B3 | U+2265 | GREATER-THAN OR EQUAL TO +| B4 | U+00A5 | YEN SIGN +| B5 | U+00B5 | MICRO SIGN +| B6 | U+2202 | PARTIAL DIFFERENTIAL +| B7 | U+2211 | N-ARY SUMMATION +| B8 | U+220F | N-ARY PRODUCT +| B9 | U+03C0 | GREEK SMALL LETTER PI +| BA | U+222B | INTEGRAL +| BB | U+00AA | FEMININE ORDINAL INDICATOR +| BC | U+00BA | MASCULINE ORDINAL INDICATOR +| BD | U+2126 | OHM SIGN +| BE | U+00E6 | LATIN SMALL LIGATURE AE +| BF | U+00F8 | LATIN SMALL LETTER O WITH STROKE ++----+--------+---------------------------------------------------------------- +| C0 | U+00BF | INVERTED QUESTION MARK +| C1 | U+00A1 | INVERTED EXCLAMATION MARK +| C2 | U+00AC | NOT SIGN +| C3 | U+221A | SQUARE ROOT +| C4 | U+0192 | LATIN SMALL LETTER F WITH HOOK +| C5 | U+2248 | ALMOST EQUAL TO +| C6 | U+2206 | INCREMENT +| C7 | U+00AB | LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +| C8 | U+00BB | RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +| C9 | U+2026 | HORIZONTAL ELLIPSIS +| CA | U+00A0 | NO-BREAK SPACE +| CB | U+00C0 | LATIN CAPITAL LETTER A WITH GRAVE +| CC | U+00C3 | LATIN CAPITAL LETTER A WITH TILDE +| CD | U+00D5 | LATIN CAPITAL LETTER O WITH TILDE +| CE | U+0152 | LATIN CAPITAL LIGATURE OE +| CF | U+0153 | LATIN SMALL LIGATURE OE ++----+--------+---------------------------------------------------------------- +| D0 | U+2013 | EN DASH +| D1 | U+2014 | EM DASH +| D2 | U+201C | LEFT DOUBLE QUOTATION MARK +| D3 | U+201D | RIGHT DOUBLE QUOTATION MARK +| D4 | U+2018 | LEFT SINGLE QUOTATION MARK +| D5 | U+2019 | RIGHT SINGLE QUOTATION MARK +| D6 | U+00F7 | DIVISION SIGN +| D7 | U+25CA | LOZENGE +| D8 | U+00FF | LATIN SMALL LETTER Y WITH DIAERESIS +| D9 | U+0178 | LATIN CAPITAL LETTER Y WITH DIAERESIS +| DA | U+2044 | FRACTION SLASH +| DB | U+00A4 | CURRENCY SIGN +| DC | U+2039 | SINGLE LEFT-POINTING ANGLE QUOTATION MARK +| DD | U+203A | SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +| DE | U+FB01 | LATIN SMALL LIGATURE FI +| DF | U+FB02 | LATIN SMALL LIGATURE FL ++----+--------+---------------------------------------------------------------- +| E0 | U+2021 | DOUBLE DAGGER +| E1 | U+00B7 | MIDDLE DOT +| E2 | U+201A | SINGLE LOW-9 QUOTATION MARK +| E3 | U+201E | DOUBLE LOW-9 QUOTATION MARK +| E4 | U+2030 | PER MILLE SIGN +| E5 | U+00C2 | LATIN CAPITAL LETTER A WITH CIRCUMFLEX +| E6 | U+00CA | LATIN CAPITAL LETTER E WITH CIRCUMFLEX +| E7 | U+00C1 | LATIN CAPITAL LETTER A WITH ACUTE +| E8 | U+00CB | LATIN CAPITAL LETTER E WITH DIAERESIS +| E9 | U+00C8 | LATIN CAPITAL LETTER E WITH GRAVE +| EA | U+00CD | LATIN CAPITAL LETTER I WITH ACUTE +| EB | U+00CE | LATIN CAPITAL LETTER I WITH CIRCUMFLEX +| EC | U+00CF | LATIN CAPITAL LETTER I WITH DIAERESIS +| ED | U+00CC | LATIN CAPITAL LETTER I WITH GRAVE +| EE | U+00D3 | LATIN CAPITAL LETTER O WITH ACUTE +| EF | U+00D4 | LATIN CAPITAL LETTER O WITH CIRCUMFLEX ++----+--------+---------------------------------------------------------------- +| F0 | U+F8FF | Apple logo +| F1 | U+00D2 | LATIN CAPITAL LETTER O WITH GRAVE +| F2 | U+00DA | LATIN CAPITAL LETTER U WITH ACUTE +| F3 | U+00DB | LATIN CAPITAL LETTER U WITH CIRCUMFLEX +| F4 | U+00D9 | LATIN CAPITAL LETTER U WITH GRAVE +| F5 | U+0131 | LATIN SMALL LETTER DOTLESS I +| F6 | U+02C6 | MODIFIER LETTER CIRCUMFLEX ACCENT +| F7 | U+02DC | SMALL TILDE +| F8 | U+00AF | MACRON +| F9 | U+02D8 | BREVE +| FA | U+02D9 | DOT ABOVE +| FB | U+02DA | RING ABOVE +| FC | U+00B8 | CEDILLA +| FD | U+02DD | DOUBLE ACUTE ACCENT +| FE | U+02DB | OGONEK +| FF | U+02C7 | CARON ++----+--------+---------------------------------------------------------------- + +=============================================================================== + diff --git a/doc/libhfs.txt b/doc/libhfs.txt new file mode 100644 index 0000000..2da801a --- /dev/null +++ b/doc/libhfs.txt @@ -0,0 +1,517 @@ + +This file documents the libhfs.a library for accessing HFS volumes. +Copyright (C) 1996-1998 Robert Leslie + +$Id: libhfs.txt,v 1.11 1998/11/02 22:08:47 rob Exp $ + +=============================================================================== + +Exported Data + + const char *hfs_error; + + This contains a pointer to a C string describing the last HFS error. + It is generally only valid after an HFS routine has returned an error + code (-1 or a NULL pointer). + + This string is encoded using ISO 8859-1. + + In all cases when an error occurs, the global variable `errno' is also + set to an appropriate value. + + unsigned char hfs_charorder[]; + + This array contains the relative sorting order of characters in HFS + filenames according to the semantics of the Macintosh character set + and the MacOS string comparison routines as used by HFS. The array can + be indexed by unsigned character quantities; the resulting value can be + compared to other array values to determine the relative sorting order + of the corresponding character indices. + +Public Routines + + ----- Volume Routines ----- + + hfsvol *hfs_mount(const char *path, int pnum, int flags); + + This routine attempts to open an HFS volume from a source pathname. The + given `pnum' indicates which ordinal HFS partition is to be mounted, + or can be 0 to indicate the entire medium should be mounted (ignoring + any partition structure). If this value is not 0, the requested + partition must exist. + + The `flags' argument specifies how the volume should be mounted. + HFS_MODE_RDONLY means the volume should be mounted read-only. + HFS_MODE_RDWR means the volume must be opened read/write. HFS_MODE_ANY + means the volume can be mounted either read-only or read/write, with + preference for the latter. + + The `flags' argument may also specify volume options. HFS_OPT_NOCACHE + means not to perform any internal block caching, such as would be + unnecessary for a volume residing in RAM, or if the associated overhead + is not desired. HFS_OPT_ZERO means that newly-allocated blocks should be + zero-initialized before use, primarily as a security feature for systems + on which blocks may otherwise contain random data. Neither of these + options should normally be necessary, and both may affect performance. + + If an error occurs, this function returns NULL. Otherwise a pointer to a + volume structure is returned. This pointer is used to access the volume + and must eventually be passed to hfs_umount() to flush and close the + volume and free all associated memory. + + int hfs_flush(hfsvol *vol); + + This routine causes all pending changes to be flushed to an HFS volume. + If a volume is kept open for a long period of time, it would be wise + to call this periodically to avoid corrupting the volume due to + unforeseen circumstances (power failure, floppy eject, etc.) + + If an error occurs, this function returns -1. Otherwise it returns 0. + + void hfs_flushall(void); + + This routine is similar to hfs_flush() except that all mounted volumes + are flushed, and errors are not reported. + + int hfs_umount(hfsvol *vol); + + The specified HFS volume is unmounted; all open files and directories + on the volume are closed, all pending changes to the volume are + flushed, and all memory allocated for the volume is freed. + + All volumes opened with hfs_mount() must eventually be closed with + hfs_umount(), or they will risk corruption. + + If an error occurs, this function returns -1. Otherwise it returns 0. + In either case, the volume structure pointer will become invalid, as + will all pointers to open file or directory structures associated with + the volume. + + void hfs_umountall(void); + + This routine is similar to hfs_umount() except that all mounted volumes + are closed, and errors are not reported. + + This routine may be useful to call just before a process terminates to + make sure any remaining open volumes are properly closed. + + hfsvol *hfs_getvol(const char *name); + + This routines searches all mounted volumes for one having the given + `name', and returns its volume structure pointer. If more than one + volume have the same name, the most recently mounted one is returned. If + no volume matches the given name, a NULL pointer is returned. + + The given `name' is assumed to be encoded using MacOS Standard Roman. + + If a NULL pointer is passed to this routine, the current volume is + returned, if any. + + void hfs_setvol(hfsvol *vol); + + The routine changes the "current" volume. Most HFS routines will accept + a NULL volume pointer to mean the current volume; by default, the + current volume is the last one which was mounted. + + int hfs_vstat(hfsvol *vol, hfsvolent *ent); + + This routine fills the volume entity structure `*ent' with information + about a mounted volume. The fields of the structure are defined in + the hfs.h header file. + + This routine returns 0 unless a NULL pointer is passed for the volume + and no volume is current, in which case it returns -1. + + int hfs_vsetattr(hfsvol *vol, hfsvolent *ent); + + This routine allows some attributes of a volume to be changed. The + attributes which may be changed are: ent->clumpsz, ent->crdate, + ent->mddate, ent->bkdate, and ent->blessed. Note that the default file + clump size may only be changed to be a multiple of the volume's + allocation block size, and the "blessed" folder must either be 0 or a + valid folder CNID. + + To change the volume's name, use hfs_rename(). + + If an error occurs, this function returns -1. Otherwise it returns 0. + + ----- Directory Routines ----- + + int hfs_chdir(hfsvol *vol, const char *path); + + The "current working directory" for the given volume is changed. + `path' can be either a relative or absolute HFS path. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + long hfs_getcwd(hfsvol *vol); + + The internal directory ID of the current working directory for the + given volume is returned. This value is typically only useful for + passing to hfs_setcwd() or hfs_dirinfo(). + + int hfs_setcwd(hfsvol *vol, long id); + + This routine changes the current working directory for the given + volume. A directory must exist with the given id. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_dirinfo(hfsvol *vol, long *id, char *name); + + This function looks up the given directory ID `*id' and stores in its + place the directory ID of its parent. If `name' is not NULL, the name + of the (child) directory is also stored in the buffer pointed to by it, + which must be at least HFS_MAX_FLEN + 1 (32) bytes long. + + The string `name' will be encoded using MacOS Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + This function can be called repeatedly to construct a full pathname + to the current working directory. The root directory of a volume + always has a directory ID of HFS_CNID_ROOTDIR, and a pseudo-parent ID + of HFS_CNID_ROOTPAR. + + hfsdir *hfs_opendir(hfsvol *vol, const char *path); + + This function prepares to read the contents of a directory. `path' + must be either an absolute or relative pathname to the desired HFS + directory. As a special case, if `path' is an empty string, a + "meta-directory" will be opened containing the root directories from + all of the currently mounted volumes. + + The string `path' is assumed to be encoded using MacOS Standard Roman. + + This function returns a pointer which must be passed to the other + directory-related routines to read the directory. + + If an error occurs, this function returns a NULL pointer. + + int hfs_readdir(hfsdir *dir, hfsdirent *ent); + + This routine fills the directory entity structure `*ent' with + information about the next item in the given open directory. The + fields of the structure are defined in the hfs.h header file. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + When no more items occur in the directory, this function returns -1 + and sets `errno' to ENOENT. + + int hfs_closedir(hfsdir *dir); + + This function closes an open directory and frees all associated + memory. + + If an error occurs, this function returns -1. Otherwise it returns 0. + In either case, the directory structure pointer will no longer be valid. + + ----- File Routines ----- + + hfsfile *hfs_create(hfsvol *vol, const char *path, + const char *type, const char *creator); + + This routine creates a new, empty file with the given path, type, and + creator. The type and creator must be strings of length 4, and have + particular meaning under MacOS. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If the creation is successful, the file is opened and a pointer to a + file structure is returned, the same as if hfs_open() had been called. + + If an error occurs, this function returns a NULL pointer. + + hfsfile *hfs_open(hfsvol *vol, const char *path); + + This function opens an HFS file in preparation for I/O. Both forks of + the file may be manipulated once the file is opened; hfs_setfork() is + used to select the current fork. By default, the data fork is current. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + A pointer to a file structure is returned. This pointer should be + passed to other routines to manipulate the file. + + If an error occurs, this function returns a NULL pointer. + + int hfs_setfork(hfsfile *file, int fork); + + This routine selects the current fork in an open file for I/O. HFS + files have two forks, data and resource. Resource forks normally contain + structured data, although these HFS routines make no distinction + between forks when reading or writing. It is up to higher-level + applications to make sense of the information read or written from + either fork. + + If 0 is passed to this routine, the data fork is selected. Otherwise + the resource fork is selected. The seek pointer for the file is + automatically reset to the beginning of the newly selected fork. + + As a side effect, this routine causes any excess disk blocks allocated + for the fork which was current before the call to be freed; normally + extra blocks are allocated during file writes to promote contiguity. + This routine will return -1 if an error occurs in this process; + otherwise it will return 0. The current fork will have been changed + regardless. + + int hfs_getfork(hfsfile *file); + + This routine returns an indication of which fork is currently active + for I/O operations on the given file. If 0 is returned, the data fork + is selected. Otherwise the resource fork is selected. + + long hfs_read(hfsfile *file, void *ptr, unsigned long len); + + This routine reads up to `len' bytes from the current fork of an HFS + file and places them into the buffer pointed to by `ptr' (which must be + at least `len' bytes long.) The number of bytes actually read is + returned, and may be less than `len' if the end of the file is reached. + + If this routine returns 0, there is no more data to be read from the + file. If an error occurs, this routine will return -1. + + It is most efficient to read data in multiples of HFS_BLOCKSZ byte + blocks at a time. + + long hfs_write(hfsfile *file, const void *ptr, unsigned long len); + + This routine writes up to `len' bytes of data to the current fork of an + HFS file from the buffer pointed to by `ptr'. The number of bytes + actually written is returned. If an error occurs, this routine will + return -1. + + If the end of the file is reached before all bytes have been written, + the file is automatically extended. + + It is most efficient to write data in multiples of HFS_BLOCKSZ byte + blocks at a time. + + int hfs_truncate(hfsfile *file, unsigned long len); + + This routine causes the current fork of the specified open file to be + truncated to at most `len' bytes. + + The disk blocks associated with the freed portion of the file are not + actually deallocated until either the current fork is changed or the + file is closed. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + long hfs_seek(hfsfile *file, long offset, int from); + + This routine changes the current seek pointer for the specified open + file. This pointer determines where the next call to hfs_read() or + hfs_write() will read or write data within the current fork. + + If `from' is HFS_SEEK_SET, the pointer is set to the absolute position + given by `offset'. + + If `from' is HFS_SEEK_CUR, the pointer is offset from its current + position by the amount `offset'. Positive offsets seek forward; negative + offsets seek backward. + + If `from' is HFS_SEEK_END, the pointer is offset from the end of the + file by the amount `offset', which ought not be positive. + + It is not presently possible to set the seek pointer beyond the logical + end of the file. + + The new absolute position of the seek pointer is returned, unless an + invalid argument was specified, in which case -1 is returned. + + int hfs_close(hfsfile *file); + + This routine causes all pending changes to the specified file to be + flushed, and all storage associated with the file structure to be + freed. Any excess disk blocks associated with the file are also + deallocated at this time. + + If an error occurs, this routine returns -1. Otherwise it returns 0. + In either case, the file structure pointer will no longer be valid. + + ----- Catalog Routines ----- + + int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent); + + This routine fills the directory entity structure `*ent' with + information about the file or directory specified by `path' on the + given volume. The fields of the structure are defined in the hfs.h + header file. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If there is no such path, or if another error occurs, this routine + returns -1. Otherwise it returns 0. + + int hfs_fstat(hfsfile *file, hfsdirent *ent); + + This routine is similar to hfs_stat() except it returns information + about a file that is already open. + + If an error occurs, this routine returns -1. Otherwise it returns 0. + + int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent); + + This routine changes various attributes of an existing file or + directory. The attributes which may be changed are: ent->crdate, + ent->mddate, ent->bkdate, ent->fdflags, ent->fdlocation, + ent->u.file.type, ent->u.file.creator, and ent->u.dir.rect. Also, the + locked status of a file may be changed with ent->flags & HFS_ISLOCKED. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If an error occurs, this routine returns -1. Otherwise it returns 0. + + int hfs_fsetattr(hfsfile *file, const hfsdirent *ent); + + This routine is similar to hfs_setattr() except it manipulates a file + that is already open. + + If an error occurs, this routine returns -1. Otherwise it returns 0. + + int hfs_mkdir(hfsvol *vol, const char *path); + + This routine creates a new, empty directory with the given path. + All parent directories must already exist, but there must not already + be a file or directory with the complete given path. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_rmdir(hfsvol *vol, const char *path); + + This routine deletes the directory with the given path. The directory + must be empty. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_delete(hfsvol *vol, const char *path); + + This routine deletes both forks of the file with the given path. + + The given `path' is assumed to be encoded using MacOS Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath); + + This routine moves and/or renames the given `srcpath' to `dstpath'. + The source must exist; the destination must not exist, unless it is a + directory, in which case an attempt will be made to move the source + into the destination directory without changing its name. + + If both `srcpath' and `dstpath' refer to root directories, the volume + specified by `srcpath' will be renamed. Note that volume names may + only have 1-27 (HFS_MAX_VLEN) characters, while all other names may + have 1-31 (HFS_MAX_FLEN) characters. + + The given `srcpath' and `dstpath' are assumed to be encoded using MacOS + Standard Roman. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + ----- Media Routines ----- + + int hfs_zero(const char *path, unsigned int maxparts, + unsigned long *blocks); + + This routine initializes a medium with a new, empty driver descriptor + record and partition map. This is only necessary if it is desired to + partition the medium; the medium can be used as a whole without + partitions by specifying 0 to the routines which require a partition + number. + + The partition map will be empty, with the exception of an entry for the + partition map itself, plus an entry for the rest of the medium as free + space. To be useful, one or more HFS partitions should be created with + hfs_mkpart(). + + The partition map will be created just large enough to allow `maxparts' + individual partitions to be created, not counting the partitions created + automatically by this routine. This number should be conservative, as + it may be impossible to create more than this many partitions for the + lifetime of the medium without re-initializing. + + If `blocks' is not NULL, the total number of blocks available for + partitioning (after the partition map structures have been created) will + be stored at this location. + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_mkpart(const char *path, unsigned long len); + + This routine creates a new HFS partition having `len' blocks on the + given medium. Space for the partition will be taken from the available + free space as indicated in the existing partition map. + + It may not be possible to create the requested partition if there are + not enough free contiguous blocks on the medium, or if there is only + one slot left in the partition map and the request does not specify + all the remaining blocks in the free space. (The partition map cannot + leave any blocks in the medium unaccounted for.) + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int hfs_nparts(const char *path); + + This routine determines the number of HFS partitions present on the + given medium, if any. If the medium specified by `path' is not + partitioned, -1 will be returned. Otherwise, a number denoting the total + number of HFS partitions is returned, including (possibly) 0. + + The number returned by this routine can help determine if a particular + medium is partitioned, and if so, the allowable range of partition + numbers which can be passed to the routines which require one. However, + passing 0 as a partition number always refers to the entire medium, + ignoring all partitions. + + If an error occurs, this function returns -1. + + int hfs_format(const char *path, int pnum, int mode, const char *vname, + int nbadblocks, const unsigned long badblocks[]); + + This routine writes a new HFS file system to the specified `path', which + should be a block device or a writable file. The size of the volume is + determined either by the maximum size of the device or size of the file, + or by the size of the indicated partition within the medium. + + If `pnum' is > 0, it selects an ordinal HFS partition in the device + to receive the file system. The partition must already exist; an error + will result if it cannot be found. With `pnum' == 0, any partition + structure on the existing medium will be ignored, and the entire + device will be used for the new HFS volume. + + Volume options may be specified in the `mode' argument. In addition to + the options accepted by hfs_mount(), HFS_OPT_2048 may be specified to + request that the volume allocation blocks be aligned on physical + 2048-byte block boundaries. Such a constraint is necessary to support + some hybrid CD-ROM file system formats, but is otherwise unnecessary and + may result in fewer allocation blocks altogether. + + The volume is given the name `vname', which must be between 1 and + HFS_MAX_VLEN (27) characters in length inclusively, and cannot contain + any colons (':'). This string is assumed to be encoded using MacOS + Standard Roman. + + It is possible to map out or "spare" bad blocks on the device such that + the file system will be made aware of these blocks and will not attempt + to use them to store data. To perform this magic, hfs_format() may be + passed an array of block numbers to spare. These numbers must + correspond to logical 512-byte blocks on the device and should be + relative to the beginning of the volume's partition, if any. If no + blocks need to be spared, 0 should be passed for `nbadblocks', and + `badblocks' may be a NULL pointer. Note that an error can occur if a + bad block occurs in a critical disk structure, or if there are too + many bad blocks (more than 25%) in the volume. + + If an error occurs, this function returns -1. Otherwise it returns 0. + +=============================================================================== + diff --git a/doc/librsrc.txt b/doc/librsrc.txt new file mode 100644 index 0000000..6387430 --- /dev/null +++ b/doc/librsrc.txt @@ -0,0 +1,119 @@ + +This file documents the librsrc.a library for accessing resource forks. +Copyright (C) 1996-1998 Robert Leslie + +$Id: librsrc.txt,v 1.6 1998/09/19 05:06:32 rob Exp $ + +=============================================================================== + +Exported Data + + const char *rsrc_error; + + This contains a pointer to a C string describing the last resource + library error. It is generally only valid after a resource routine has + returned an error code (-1 or a NULL pointer). + + In all cases when an error occurs, the global variable `errno' is also + set to an appropriate value. + +Public Routines + + rsrcfile *rsrc_init(void *priv, const rsrcprocs *procs); + + This routine prepares an open file access path to be treated as a + Macintosh resource fork. `priv' is treated as private file access data, + and is passed transparently as the first argument to each of: + + procs->seek(priv, ...); + procs->read(priv, ...); + procs->write(priv, ...); + + Normally when using this library with libhfs, `priv' will be the + `hfsfile *' returned from hfs_open(), and `procs' will point to a + structure containing { hfs_seek, hfs_read, hfs_write }. + + However, any suitable routines may be substituted which accept + the appropriate arguments. + + Note that the file access path must already exist, and must be ready + to read and/or write resource data. When using libhfs, this means + hfs_setfork(..., 1) must be called prior to rsrc_init(). + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int rsrc_finish(rsrcfile *rfile); + + Calling this routine causes the library to flush all changes to the + given resource file `rfile', and release all storage associated with it. + + Note that this routine does not close the underlying file access path; + this must be done separately after calling rsrc_finish(). + + If an error occurs, this function returns -1. Otherwise it returns 0. + + int rsrc_counttypes(rsrcfile *rfile); + + This function returns the number of unique resource types in the given + resource file `rfile'. + + int rsrc_count(rsrcfile *rfile, const char *type); + + This function returns the number of actual resources in the given + resource file `rfile' of the given type `type', which must be a pointer + to a four-character string naming the type. + + void rsrc_gettype(rsrcfile *rfile, int index, char *type); + + This routine stores at `type' a four-character string (plus terminating + null) representing a resource type present in the given resource file + `rfile'. The ordinal `index' must be from 1 to the total number of + types present in the file, given by rsrc_counttypes(). + + If `index' is out of range, the effect of this function is undefined. + + void *rsrc_get(rsrcfile *rfile, const char *type, int id); + + This routine fetches an individual resource identified by the given + `type' and `id' from the resource file `rfile' and returns a pointer to + the beginning of a block containing the resource data. + + The pointer returned by this function can be passed to other routines + in this library to manipulate the resource, and must finally be passed + to rsrc_release() when the resource is no longer needed. + + If this function fails, a NULL pointer will be returned. + + void *rsrc_getnamed(rsrcfile *rfile, const char *type, const char *name); + + Like rsrc_get(), except the resource is identified by the given `name' + instead of by id. + + void *rsrc_getind(rsrcfile *rfile, const char *type, int index); + + Like rsrc_get(), except the resource is identified by the ordinal + `index' instead of by id. The `index' must be from 1 to the total number + of resources of type `type', given by rsrc_count(). + + unsigned long rsrc_size(void *rdata); + + This function returns the size (in bytes) of the given resource `rdata'. + + void *rsrc_resize(void *rdata, unsigned long len); + + This routine changes the size of the resource data `rdata' to be `len'. + The resource data will be unchanged up to the lesser of the original + size and the new size; newly allocated space will be uninitialized. + + If there is not enough memory to hold the new requested size, a NULL + pointer is returned and the original `rdata' pointer remains valid. + Otherwise, a new resource data pointer is returned and the original + becomes invalid. + + void rsrc_release(void *rdata); + + This function releases the resource data `rdata'. It should be called + once the data is no longer needed so the associated memory can be freed. + +=============================================================================== + diff --git a/doc/man/hattrib.1 b/doc/man/hattrib.1 new file mode 100644 index 0000000..1e86f2d --- /dev/null +++ b/doc/man/hattrib.1 @@ -0,0 +1,46 @@ +.TH HATTRIB 1 30-Aug-1998 HFSUTILS +.SH NAME +hattrib \- change HFS file or directory attributes +.SH SYNOPSIS +hattrib +[-t +.IR TYPE ] +[-c +.IR CREA ] +[-|+i] [-|+l] +.I hfs-path +[...] +.PP +hattrib -b +.I hfs-path +.SH DESCRIPTION +.B hattrib +permits the alteration of HFS file attributes. In the first form, the +MacOS-defined +.I type +and +.I creator +attributes can be set using the -t and -c flags, respectively. A file's +.I invisible +flag can be set or cleared with +i and -i, respectively. Finally, a file's +.I locked +flag can be set or cleared with +l and -l. +.PP +All files mentioned on the command line will receive the specified attributes, +regardless of the file's current attributes. Any attribute not mentioned in +the command line is left unchanged. +.PP +In the second form, a single HFS pathname refering to a folder is given with +the -b option, causing it to become "blessed" as the MacOS System Folder. For +this to be useful, the folder should contain valid Macintosh System and Finder +files. +.PP +Note that the +.I locked +and "blessed" attributes have little consequence to hfsutils. +.SH SEE ALSO +hfsutils(1), hls(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hcd.1 b/doc/man/hcd.1 new file mode 100644 index 0000000..458b799 --- /dev/null +++ b/doc/man/hcd.1 @@ -0,0 +1,31 @@ +.TH HCD 1 13-Jan-1997 HFSUTILS +.SH NAME +hcd \- change working HFS directory +.SH SYNOPSIS +hcd +.RI [ hfs-path ] +.SH DESCRIPTION +.B hcd +is used to change the notion of the "current working directory" for the +current HFS volume. All subsequent HFS commands will interpret filenames +relative to this directory, unless absolute pathnames are used. +.PP +If the argument pathname is omitted, +.B hcd +will change to the root of the current volume. +.SH SEE ALSO +hfsutils(1), hpwd(1), hls(1) +.SH FILES +$HOME/.hcwd +.SH BUGS +Although absolute pathnames can be given to +.BR hcd , +the full pathname must match the current volume; it cannot specify a path for +a different volume. Use +.B hvol +or +.B hmount +to change the current volume. (Each volume has its own independent current +working directory.) +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hcopy.1 b/doc/man/hcopy.1 new file mode 100644 index 0000000..51f7941 --- /dev/null +++ b/doc/man/hcopy.1 @@ -0,0 +1,64 @@ +.TH HCOPY 1 13-Jan-1997 HFSUTILS +.SH NAME +hcopy \- copy files from or to an HFS volume +.SH SYNOPSIS +hcopy [-m|-b|-t|-r|-a] +.I source-path +[...] +.I target-path +.SH DESCRIPTION +.B hcopy +transfers files from an HFS volume to UNIX or vice versa. The named source +files are copied to the named destination target, which must be a directory if +multiple files are to be copied. +.PP +Copies are performed using a translation mode, which must be one of: +.TP +-m +.BR "MacBinary II" : +A popular format for binary file transfer. Both forks of the Macintosh file +are preserved. This is the recommended mode for transferring arbitrary +Macintosh files. +.TP +-b +.BR BinHex : +An alternative format for ASCII file transfer. Both forks of the Macintosh +file are preserved. +.TP +-t +.BR Text : +Performs end-of-line translation. Only the data fork of the Macintosh file is +copied. +.TP +-r +.BR "Raw Data" : +Performs no translation. Only the data fork of the Macintosh file is copied. +.TP +-a +.BR Automatic : +A mode will be chosen automatically for each file based on a set of predefined +heuristics. +.PP +If no mode is specified, -a is assumed. +.PP +If a UNIX source pathname is specified as a single dash (-), +.B hcopy +will copy from standard input to the HFS destination. Likewise, a single dash +used as a UNIX destination pathname will cause +.B hcopy +to copy the HFS source to standard output. +.SH NOTES +Copied files may have their filenames altered during translation. For example, +an appropriate file extension may be added or removed, and certain other +characters may also be transliterated. +.PP +The destination target must not be ambiguous; that is, it must be obvious +whether the target is on the UNIX filesystem or on an HFS volume. As a rule, +HFS targets must contain at least one colon (:), usually as the beginning of a +relative pathname or by itself to represent the current working directory. To +make a UNIX target unambiguous, either use an absolute pathname or precede a +relative pathname with a dot and slash (./). +.SH SEE ALSO +hfsutils(1), hls(1), hattrib(1) +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hdel.1 b/doc/man/hdel.1 new file mode 100644 index 0000000..55f5fe3 --- /dev/null +++ b/doc/man/hdel.1 @@ -0,0 +1,17 @@ +.TH HDEL 1 05-Apr-1996 HFSUTILS +.SH NAME +hdel \- delete both forks of an HFS file +.SH SYNOPSIS +hdel +.I hfs-path +[...] +.SH DESCRIPTION +.B hdel +deletes files from the current HFS volume. Both forks (resource and data) of +each named file are removed, freeing space for other files. +.SH SEE ALSO +hfsutils(1), hrmdir(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hdir.1 b/doc/man/hdir.1 new file mode 100644 index 0000000..b878b72 --- /dev/null +++ b/doc/man/hdir.1 @@ -0,0 +1,21 @@ +.TH HDIR 1 14-Jan-1997 HFSUTILS +.SH NAME +hdir \- display an HFS directory in long format +.SH SYNOPSIS +hdir +.RI [ options ] +.RI [ hfs-path +.RI ...] +.SH DESCRIPTION +.B hdir +is an alternative interface to +.BR hls . +It is equivalent to supplying +.B hls +the -l flag. +.SH SEE ALSO +hfsutils(1), hls(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hformat.1 b/doc/man/hformat.1 new file mode 100644 index 0000000..470fc06 --- /dev/null +++ b/doc/man/hformat.1 @@ -0,0 +1,94 @@ +.TH HFORMAT 1 08-Nov-1997 HFSUTILS +.SH NAME +hformat \- create a new HFS filesystem and make it current +.SH SYNOPSIS +hformat +[-f] [-l +.IR label ] +.I destination-path +.RI [ partition-no ] +.SH DESCRIPTION +.B hformat +is used to write a new HFS filesystem to a volume. A UNIX pathname to the +volume's destination must be specified. The destination may be either a block +device or a regular file, but it must already exist and be writable. +.PP +An optional label can be specified to name the volume. The name must be +between 1-27 characters and cannot contain a colon (:). By default, the volume +will be named +.BR Untitled . +.PP +If the destination medium is partitioned, one partition must be selected to +receive the filesystem. If there is only one HFS partition on the medium, it +will be selected by default. Otherwise, the desired partition number must be +specified (as the ordinal +.IR n th +HFS partition) on the command-line. The size of the partition determines the +size of the resulting volume. +.PP +Partition number +.B 0 +can be specified to format the entire medium as a single filesystem without a +partition map, erasing any existing partition information. Since this will +destroy all the partitions, the +.B -f +option must be specified to force this operation if the medium currently +contains a partition map. +.PP +If the medium is not partitioned (or if partition 0 is specified), the size or +capacity of the medium determines the size of the resulting volume. +.PP +The new volume will be empty and will become "current" so subsequent commands +will refer to it. The current working directory for the volume is set to the +root of the volume. +.SH EXAMPLES +.SP +.TP +% hformat /dev/fd0 +If a floppy disk is available as +.BR /dev/fd0 , +this formats the disk as an HFS volume named +.BR Untitled . +(N.B. The floppy must already have received a low-level format by other +means.) +.TP +% dd if=/dev/zero of=disk.hfs bs=1k count=800 +.PD 0 +.TP +% hformat -l "Test Disk" disk.hfs +.PD 1 +This sequence creates an 800K HFS volume image in the file +.B +disk.hfs +in the current directory, and names it +.BR "Test Disk" . +.TP +% hformat -l "Loma Prieta" /dev/sd2 1 +If a SCSI disk is available as +.BR /dev/sd2 , +this initializes the first HFS partition on the disk (which must already +exist) with a new filesystem, naming the resulting volume +.BR "Loma Prieta" . +.TP +% hformat -f /dev/sd2 0 +This causes the medium accessible as +.B /dev/sd2 +to be reformatted as a single HFS volume, ignoring and erasing any existing +partition information on the medium. The +.B -f +option must be specified if the medium is currently partitioned; otherwise the +command will fail. +.SH NOTES +This command does not create or alter partition maps, although it can erase +them (as described above). Any partition number specified on the command line +must already exist. +.PP +The smallest volume size which can be formatted with +.B hformat +is 800K. +.SH SEE ALSO +hfsutils(1), hmount(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hfs.1 b/doc/man/hfs.1 new file mode 100644 index 0000000..00df0da --- /dev/null +++ b/doc/man/hfs.1 @@ -0,0 +1,111 @@ +.TH HFS 1 15-Jan-1997 HFSUTILS +.SH NAME +hfs \- shell for manipulating HFS volumes +.SH SYNOPSIS +hfs +.RI [ "hfs-path " [ partition-no ]] +.SH DESCRIPTION +.B hfs +is an interactive command-oriented tool for manipulating HFS volumes. +.B hfs +is based on the Tcl interpreter, so basic Tcl constructs can be used in +addition to the following commands: +.SP +.TP +.BR mount " path [partition-no]" +The specified UNIX path is opened as an HFS volume. If a partition number +.I n +is specified and the volume source is located on a partitioned medium, the +.IR n th +discovered HFS partition will be mounted. The default +.I partition-no +is 1. +.TP +.BR umount " [path]" +The volume previously mounted from the specified path (or the current volume, +if none specified) is unmounted. +.TP +.BR vol " path" +The volume previously mounted from the specified path is made current. +.TP +.B info +General information about the currently mounted volume is displayed. This +information is also displayed automatically when the volume is mounted. +.TP +.B pwd +The full path to the current working HFS directory is displayed. +.TP +.BR cd " [hfs-path]" +The current working directory is changed to the given HFS path. If no path is +given, the working directory is changed to the root of the volume. +.TP +.BR dir " [hfs-path]" +A directory listing of the specified HFS directory is displayed. If no path is +given, the contents of the current working directory are shown. +.TP +.BR mkdir " hfs-path" +A new, empty directory is created with the specified path. +.TP +.BR rmdir " hfs-path" +The specified directory is removed. It must be empty. +.TP +.BR create " hfs-path [type [creator]]" +An empty file is created with the specified path. The Macintosh type and +creator may be specified, or they will default to +.B TEXT +and +.BR UNIX , +respectively. +.TP +.BR del " hfs-path" +Both forks of the specified file are deleted. +.TP +.BR stat " hfs-path" +Status information about the specified HFS path-identified entity is +displayed. +.TP +.BR cat " hfs-path" +The data fork of the specified HFS file is displayed. +.TP +.BR copyin " unix-path [hfs-path [mode]]" +The specified UNIX file is copied to the named HFS destination path. Unless +specified otherwise, the file will be copied into the current HFS working +directory using a heuristically chosen mode. The +.I mode +may be one of: +.B macb +(MacBinary II), +.B binh +(BinHex), +.BR text , +or +.BR raw . +.TP +.BR copyout " hfs-path [unix-path [mode]]" +The specified HFS file is copied into the named UNIX destination path. Unless +specified otherwise, the file will be copied into the current UNIX working +directory using a heuristically chosen mode. The modes are the same as for +.BR copyin . +.TP +.BR format " path [partition-no [volume-name]]" +The specified UNIX path is initialized as an empty HFS volume with the given +name, and this volume is subsequently mounted. The default volume name is +.BR Untitled . +.PP +The shell is scriptable, however it should be understood that the above +commands are actually implemented by Tcl procedures prefixed with the +character "h", e.g. hmount, hcd, etc., in order to avoid name collisions with +other Tcl utilities. The "h" may be omitted in interactive use for +convenience. +.SH SEE ALSO +hfsutils(1), xhfs(1) +.SH BUGS +.B cat +can only display the data fork of a file. Text translations are performed +unconditionally on the output. Furthermore, binary data cannot be handled +properly from within Tcl scripts since the character with value 0 cannot be +represented in Tcl strings. Use +.B copyout +to copy files without these limitations. +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hfssh.1 b/doc/man/hfssh.1 new file mode 100644 index 0000000..3c8c065 --- /dev/null +++ b/doc/man/hfssh.1 @@ -0,0 +1,424 @@ +.TH HFSSH 1 19-Feb-1998 HFSUTILS +.SH NAME +hfssh \- Tcl interpreter with HFS extensions +.SH SYNOPSIS +hfssh +.RI [ script ] +.SH DESCRIPTION +.B hfssh +is a Tcl interpreter like tclsh(1) but which also implements the following +extensions to support manipulation of Macintosh HFS media: +.SP +.TP +.BI "hfs mount" " path [partno]" +Mounts the indicated HFS partition from the given +.IR path . +An HFS volume handle is returned, which may be used for further volume +commands described below. +.TP +.BI "hfs zero" " path nparts" +The given +.I path +is overwritten with a Macintosh partition structure which can accommodate up to +.I nparts +partitions. All space on the medium is initially allocated to an empty +partition, from which new partitions can be created using +.BR "hfs mkpart" . +The number of blocks in this empty space available for partitioning is +returned. +.TP +.BI "hfs mkpart" " path nblocks" +A new HFS partition is created from the available free space on the specified +Macintosh-partitioned medium. The partition is created with a size of +.IR nblocks . +Any remaining free blocks left in the empty partition space can be further +allocated to other new partitions, as long as there are enough partition slots +remaining. +.IP +N.B. When the last remaining partition slot is used, all remaining free space +must be allocated to it. It is therefore best to consider this when initially +creating the total number of partition slots with +.BR "hfs zero" . +.TP +.BI "hfs nparts" " path" +This command returns the number of HFS partitions which exist on the +Macintosh-formatted medium specified by +.IR path . +If +.I path +does not appear to have a Macintosh partition map, or if an error occurs, this +command will return -1. Otherwise, it will return a number greater than or +equal to 0. +.TP +.BI "hfs format" " path partno vname [bblist]" +This command creates a new HFS volume by formatting the given +.I path +and partition +.I partno +and giving it a volume label +.IR vname . +.IP +If it is desired to "spare" some blocks from being used by the volume, a list +of "bad block" numbers can be given, relative to the beginning of the +partition. The given blocks will be mapped out of use (if possible) and the +size of the resulting volume will be decreased. +.TP +.B "hfs flushall" +All pending changes to all open volumes are flushed immediately. +This is useful to do periodically to avoid accidental loss of data when +volumes are open for long periods of time. +.TP +.BI "hfs chartrans" " fromset toset string" +This command translates the given +.I string +from the +.I fromset +character set to the +.I toset +set. Both +.I fromset +and +.I toset +can be one of +.B latin1 +(ISO 8859-1) +or +.B macroman +(MacOS Standard Roman). +A new (translated) string is returned. +.IP +The translation is not necessarily reversible, since the two character sets do +not have a complete one-to-one mapping. +.TP +.B "hfs version" +The current running version of +.B hfsutils +is returned. +.TP +.B "hfs copyright" +A copyright notice is returned. +.TP +.B "hfs author" +The name and email address of the author of +.B hfsutils +is returned. +.TP +.B "hfs license" +A license statement for +.B hfsutils +is returned. +.TP +.IB "vol " vname +The volume name of the given +.I vol +handle is returned. This is also the name of the volume's root directory, +needed to construct absolute pathnames on the volume. +.TP +.IB "vol " size +A list of two numbers is returned; the first is the total size of the given +.I vol +(in bytes), and the second is the number of free bytes that are currently +available. +.TP +.IB "vol " crdate +The creation date of the given +.I vol +is returned, expressed as a number of seconds since 00:00:00 01-Jan-1970 UTC. +.TP +.IB "vol " mddate +The last modification date of the given +.I vol +is returned, expressed as a number of seconds since 00:00:00 01-Jan-1970 UTC. +.TP +.IB "vol " islocked +A boolean value (either 1 or 0) is returned, indicating whether the given +.I vol +handle is locked for read-only access. It may be locked because the medium is +physically locked through hardware, or because the medium was opened read-only +for special reasons (such as another process also has the medium open). +.TP +.IB "vol " umount +The indicated +.I vol +is unmounted, flushing any unsaved data to the volume and closing the access +path to the medium. The +.I vol +handle subsequently becomes invalid for further use. +.TP +.IB "vol " cwd +A numeric value is returned indicating the catalog node ID (CNID) of the +current working directory on the given +.IR vol . +This value can be passed to +.IB "vol " dirinfo +to learn the directory's name and parent CNID. +.TP +.IB "vol " path +A list of directory names is returned, representing the hierarchy between the +root and the current directory. These names can be joined with +.IB "vol " sepchar +characters (:) to construct an absolute pathname to the current directory. +.IP +The same information can be acquired by traversing the CNIDs from the current +directory to the root using +.I vol +.BR dirinfo . +(The root directory always has a CNID of 2.) +.TP +.IB "vol " dir " [path]" +A list is returned describing the contents of the given directory +.I path +(defaulting to the current directory) on the given +.IR vol . +Each element of the list describes one entry, and contains a set of +attribute/value pairs represented as another list, suitable for assignment to +a Tcl array using +.BR "array set" . +.TP +.IB "vol " flush +All pending changes to the given volume are flushed immediately. +.TP +.IB "vol " sepchar +The HFS path separator character ":" is returned. +.TP +.IB "vol " cd " path" +.PD 0 +.TP +.IB "vol " chdir " path" +.PD 1 +The current working directory on the given volume is changed to +.IR path , +which may be either an absolute or relative path. +.TP +.IB "vol " dirinfo " cnid" +A two-element list describing the directory having the given +.I cnid +on the given +.I vol +is returned. The first element contains the name of the directory, while the +second element contains the CNID of the directory's parent. Two CNID values +are special: the root directory of the volume has CNID 2, and the "parent" of +the root directory is returned with CNID 1. +.TP +.IB "vol " open " path" +The file on +.I vol +having the given +.I path +is opened. An HFS file handle is returned, which may be used for further file +commands described below. +.TP +.IB "vol " stat " path" +Information about the file or directory having the given +.I path +is returned in much the same way as +.IB "vol " dir +except that only the single argument is described (not its contents). +.TP +.IB "vol " mkdir " path" +A new directory on +.I vol +having the given +.I path +is created. All of the parent directories leading to +.I path +must already exist, but +.I path +itself must not. +.TP +.IB "vol " rmdir " path" +The directory on +.I vol +with the given +.I path +is removed. The directory must be empty. +.TP +.IB "vol " delete " path" +The file on +.I vol +with the given +.I path +is removed. Both resource and data forks of the file are deleted. +.TP +.IB "vol " touch " path" +The modification time for the file or directory specified by +.I path +on the given +.I vol +is updated to the current time. +.TP +.IB "vol " glob " pattern" +The given +.I pattern +is treated as a list of globbing patterns, each of which may be expanded to +the names of files or directories on the given +.I vol +according to the globbing rules described in the hfsutils(1) documentation. +The resulting pathnames are returned in a (possibly longer) list. If a pattern +does not match any file or directory name, it is returned in the resulting +list unchanged. +.TP +.IB "vol " bless " path" +The folder named by the given path is "blessed" as the MacOS System Folder. +For this to be useful, the folder should contain valid Macintosh System and +Finder files. +.TP +.IB "vol " rename " oldpath newpath" +The existing +.I oldpath +on the given +.I vol +is renamed to +.IR newpath , +possibly changing its location at the same time. If +.I newpath +already exists, it must be a directory, and the item will simply be moved into +it keeping the same name. (In the latter case, there must not be another file +or directory already with the same name; in no case will another file or +directory be overwritten.) +.TP +.IB "vol " create " path type creator" +A new, empty file is created on +.I vol +having the given +.IR path , +and an HFS file handle is returned in the same manner as +.I vol +.BR open . +The file is given the specified MacOS +.I type +and +.I creator +codes, which must be 4 character strings. +.TP +.IB "vol " copy " srcpath dstvol dstpath" +The given file +.I srcpath +located on +.I vol +is copied to +.I dstpath +located on +.I dstvol +(which may be the same as +.IR vol ). +The file and its attributes are copied verbatim; no translation is performed. +.TP +.IB "vol " copyin " mode srcpath dstpath" +The specified local (UNIX) +.I srcpath +is copied into the given +.I vol +as a file having the specified (HFS) +.IR dstpath . +A translation +.I mode +must be given as one of +.BR macbinary , +.BR binhex , +.BR text , +or +.BR raw . +.TP +.IB "vol " copyout " mode srcpath dstpath" +The specified (HFS) +.I srcpath +on the given +.I vol +is copied out as a local file having the specified (UNIX) +.IR dstpath . +A translation +.I mode +must be given as one of +.BR macbinary , +.BR binhex , +.BR text , +or +.BR raw . +.TP +.IB "file " close +The indicated +.I file +is closed, all pending changes to the file are flushed, and the file handle +becomes invalid for any subsequent operation. +.TP +.IB "file " tell +A numeric index is returned indicating the character position within +.I file +at which the next read or write operation will occur. +.TP +.IB "file " stat +Information about the given +.I file +is returned in much the same way as +.I vol +.BR stat . +.TP +.IB "file " getfork +If the given +.I file +is currently performing I/O on its data fork, the string "data" is returned. +Otherwise, the string "rsrc" is returned. When files are opened, they will +default to read/write on their data fork. The current fork may be changed with +.I file +.BR setfork . +.TP +.IB "file " setfork " fork" +The current fork of the given +.I file +is set to +.I fork +(which must be one of +.B data +or +.BR rsrc ), +and the current read/write position is reset to the beginning of the file. +.TP +.IB "file " seek " pos [from]" +The character position for the next read or write on +.I file +is changed to +.IR pos , +relative to the indicated +.I from +position, which must be one of +.BR start , +.BR current , +or +.BR end . +The default is to position relative to the +.B start +of the file. +.TP +.IB "file " read " length" +.I length +bytes are read from the current read/write position in +.IR file , +and these bytes are returned as a string. This string may be shorter than +.I length +in some circumstances, or may even be empty, indicating the end of the file +has been reached. +.TP +.IB "file " write " string" +The given +.I string +is written to +.I file +at the current read/write position. The number of bytes actually written to +the file is returned, and may be less than the length of the string in unusual +circumstances (such as when the volume is full). +.SH SEE ALSO +hfsutils(1), hfs(1), tclsh(1) +.SH NOTES +Precautions are taken to ensure all open files and mounted volumes are cleanly +closed and unmounted before exiting the shell, however abnormal termination +(e.g. CTRL-C) can circumvent this, potentially leaving volumes in an +inconsistent state. Judicious use of +.B "hfs flushall" +may help reduce this risk. +.SH BUGS +Tcl does not provide a mechanism for manipulating arbitrary binary data. +Therefore caution should be used when reading or writing files containing +anything other than plain text. +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hfsutils.1 b/doc/man/hfsutils.1 new file mode 100644 index 0000000..b4091c3 --- /dev/null +++ b/doc/man/hfsutils.1 @@ -0,0 +1,109 @@ +.TH HFSUTILS 1 08-Nov-1997 HFSUTILS +.SH NAME +hfsutils \- tools for reading and writing Macintosh HFS volumes +.SH SYNOPSIS +.nf +\fBhattrib\fR \- change HFS file or directory attributes +\fBhcd\fR \- change working HFS directory +\fBhcopy\fR \- copy files from or to an HFS volume +\fBhdel\fR \- delete both forks of an HFS file +\fBhdir\fR \- display an HFS directory in long format +\fBhformat\fR \- create a new HFS filesystem and make it current +\fBhls\fR \- list files in an HFS directory +\fBhmkdir\fR \- create a new HFS directory +\fBhmount\fR \- introduce a new HFS volume and make it current +\fBhpwd\fR \- print the full path to the current HFS working directory +\fBhrename\fR \- rename or move an HFS file or directory +\fBhrmdir\fR \- remove an empty HFS directory +\fBhumount\fR \- remove an HFS volume from the list of known volumes +\fBhvol\fR \- display or change the current HFS volume +.fi +.PP +.nf +\fBhfssh\fR \- Tcl interpreter with HFS extensions +.fi +.PP +.nf +\fBhfs\fR \- shell for manipulating HFS volumes +\fBxhfs\fR \- graphical interface for manipulating HFS volumes +.fi +.SH DESCRIPTION +.B hfsutils +is a collection of tools and programs for accessing Macintosh HFS-formatted +volumes. See the accompanying man page for each program above for more +information. +.SH NOTES +These utilities can manipulate HFS volumes on nearly any medium. A UNIX path +is initially specified to +.B hmount +or +.B hformat +which gives the location of the volume. This path can be a block device -- +corresponding to, for example, a floppy disk, CD-ROM, SCSI disk, or other +device -- or it can be a regular file containing an image of any of the +above. +.PP +The medium specified by the UNIX path may or may not contain an Apple +partition map. If partitioned, it is possible for more than one HFS volume to +be present on the medium. In this case, a partition number must also be given +which selects the desired partition. This number refers to the +.IR n th +ordinal HFS partition on the volume. (Other, non-HFS partitions are ignored.) +Partition number +.B 0 +refers to the entire medium, disregarding the partition map, if any. +.PP +HFS pathnames consist of colon-separated components. Unlike UNIX pathnames, an +HFS path which begins with a colon (e.g. :Foo:Bar) is a +.I relative +path, and one which does not (e.g. Foo:Bar) is an +.I absolute +path. As sole exception to this rule, a path not containing any colons is +assumed to be relative. +.PP +Absolute pathnames always begin with the name of the volume itself. Any +occurrence of two or more consecutive colons in a path causes resolution of +the path to ascend into parent directories. +.PP +Most of the command-line programs support HFS filename globbing. The following +forms of globbing are supported: +.SP +.TP +.B * +matches zero or more characters. +.TP +.B ? +matches exactly one character. +.TP +.BR [ ... ] +matches any single character enclosed within the brackets. A character range +may be specified by using a hypen (-). Note that matches are not case +sensitive. +.TP +.BR { ... , ... } +expands into the Cartesian product of each specified substring. +.TP +.B \\\\ +causes the following character to be matched literally. +.PP +Note that since globbing is performed by each HFS command rather than by the +UNIX shell (which knows nothing about HFS volumes), care should always be +taken to protect pathnames from the shell by using an appropriate quoting +technique. Typically it is best to surround HFS pathnames containing glob +characters with single quotes ('). +.PP +Time stamps on HFS volumes are interpreted as being relative to the current +time zone. This means that modification dates on HFS volumes written in +another time zone may appear to be off by some number of hours. +.PP +Hardware limitations prevent some systems from reading or writing native +Macintosh 800K floppy disks; only high-density 1440K disks can be used on +these systems. +.PP +The obsolete MFS volume format is not supported by this software. +.SH SEE ALSO +hattrib(1), hcd(1), hcopy(1), hdel(1), hdir(1), hformat(1), hls(1), hmkdir(1), +hmount(1), hpwd(1), hrename(1), hrmdir(1), hvol(1), +hfs(1), xhfs(1) +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hls.1 b/doc/man/hls.1 new file mode 100644 index 0000000..9542f12 --- /dev/null +++ b/doc/man/hls.1 @@ -0,0 +1,119 @@ +.TH HLS 1 14-Jan-1997 HFSUTILS +.SH NAME +hls \- list files in an HFS directory +.SH SYNOPSIS +hls +.RI [ options ] +.RI [ hfs-path +.RI ...] +.SH DESCRIPTION +.B hls +lists files and directories contained in an HFS volume. If one or more +arguments are given, each specified file or directory is shown; otherwise, the +contents of the current working directory are shown. +.SH OPTIONS +.TP +-1 +Output is formatted such that each entry appears on a single line. This is the +default when stdout is not a terminal. +.TP +-a +All files and directories are shown, including "invisible" files, as would be +perceived by the Macintosh Finder. Normally invisible files are omitted from +directory listings. +.TP +-b +Special characters are displayed in an escaped backslash notation. Normally +special or non-printable characters in filenames are replaced by a question +mark (?). +.TP +-c +Sort and display entries by their creation date, rather than their +modification date. +.TP +-d +List directory entries themselves rather than their contents. Normally the +contents are shown for named directories on the command-line. +.TP +-f +Do not sort directory contents; list them in the order they appear in the +directory. This option effectively enables -a and -U and disables -l, -s, and +-t. +.TP +-i +Show the catalog IDs for each entry. Every file and directory on an HFS volume +has a unique catalog ID. +.TP +-l +Display entries in long format. This format shows the entry type ("d" for +directory or "f" for file), flags ("i" for invisible), file type and creator +(four-character strings for files only), size (number of directory +sub-contents or file resource and data bytes, respectively), date of last +modification (or creation, with -c flag), and pathname. Macintosh "locked" +files are indicated by "F" in place of "f". +.TP +-m +Display entries in a continuous format separated by commas. +.TP +-q +Replace special and non-printable characters in displayed filenames with +question marks (?). This is the default when stdout is connected to a +terminal. +.TP +-r +Sort entries in reverse order before displaying. +.TP +-s +Show the file size for each entry in 1K block units. The size includes blocks +used for both data and resource forks. +.TP +-t +Sort and display entries by time. Normally files will be sorted by name. This +option uses the last modification date to sort unless -c is also specified. +.TP +-x +Display entries in column format like -C, but sorted horizontally into rows +rather than columns. +.TP +.RI "-w " width +Format output lines suitable for display in the given +.IR width . +Normally the width will be determined from your terminal, from the environment +variable COLUMNS, or from a default value of 80. +.TP +-C +Display entries in column format with entries sorted vertically. This is the +default output format when stdout is connected to a terminal. +.TP +-F +Cause certain output filenames to be followed by a single-character flag +indicating the nature of the entry; directories are followed by a colon (:) +and executable Macintosh applications are followed by an asterisk (*). +.TP +-N +Cause all filenames to be output verbatim without any escaping or +question-mark substitution. +.TP +-Q +Cause all filenames to be enclosed within double-quotes (") and +special/non-printable characters to be properly escaped. +.TP +-R +For each directory that is encountered in a listing, recursively descend into +and display its contents. +.TP +-S +Sort and display entries by size. For files, the combined resource and data +lengths are used to compute a file's size. +.TP +-U +Do not sort directory contents; list them in the order they appear in the +directory. On HFS volumes, this is usually an alphabetical case-insensitive +ordering, although there are some idiosyncrasies to the Macintosh +implementation of ordering. This option does not affect -a, -l, or -s. +.SH SEE ALSO +hfsutils(1), hcd(1), hpwd(1), hdir(1), hcopy(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hmkdir.1 b/doc/man/hmkdir.1 new file mode 100644 index 0000000..9d9332a --- /dev/null +++ b/doc/man/hmkdir.1 @@ -0,0 +1,17 @@ +.TH HMKDIR 1 13-Jan-1997 HFSUTILS +.SH NAME +hmkdir \- create a new HFS directory +.SH SYNOPSIS +hmkdir +.I hfs-path +[...] +.SH DESCRIPTION +.B hmkdir +creates new, empty directories (folders) on the current HFS volume. There must +not already be a file or directory with each named path. +.SH SEE ALSO +hfsutils(1), hrmdir(1), hcd(1), hpwd(1), hls(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hmount.1 b/doc/man/hmount.1 new file mode 100644 index 0000000..d7f7bd0 --- /dev/null +++ b/doc/man/hmount.1 @@ -0,0 +1,64 @@ +.TH HMOUNT 1 08-Nov-1997 HFSUTILS +.SH NAME +hmount \- introduce a new HFS volume and make it current +.SH SYNOPSIS +hmount +.I source-path +.RI [ partition-no ] +.SH DESCRIPTION +.B hmount +is used to introduce a new HFS volume. A UNIX pathname to the volume's source +must be specified. The source may be a block device or a regular file +containing an HFS volume image. +.PP +If the source medium is partitioned, one partition must be selected to be +mounted. If there is only one HFS partition on the medium, it will be selected +by default. Otherwise, the desired partition number must be specified (as the +ordinal +.IR n th +HFS partition) on the command-line. Partition number +.B 0 +can be specified to refer to the entire medium, ignoring what might otherwise +be perceived as a partition map, although in practice this is probably only +useful if you want this command to fail when the medium is partitioned. +.PP +The mounted volume becomes "current" so subsequent commands will refer to it. +The current working directory for the volume is set to the root of the volume. +This information is kept in a file named +.I .hcwd +in the user's home directory. +.PP +If the source medium is changed (e.g. floppy or CD-ROM disc exchanged) after +.B hmount +has been called, subsequent HFS commands will fail until the original medium +is replaced or a different volume is made current. To use the same source +path with the different medium, reissue the +.B hmount +command. +.SH EXAMPLES +.SP +.TP +% hmount /dev/fd0 +If a Macintosh floppy disk is available as +.BR /dev/fd0 , +this command makes the floppy current for other HFS commands such as hls(1), +hcd(1), hcopy(1), etc. +.TP +% hmount /dev/sd2 1 +If a SCSI disk is available as +.BR /dev/sd2 , +this command finds the first HFS partition on the medium and makes it +available for other HFS operations. +.SH NOTES +.B hmount +does not actually mount an HFS partition over a UNIX directory in the +traditional mount(8) sense. It is merely a "virtual" mount, as a point of +convenience for future HFS operations. Each HFS command independently opens, +operates on, and closes the named source path given to +.BR hmount . +.SH SEE ALSO +hfsutils(1), hformat(1), humount(1), hvol(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hpwd.1 b/doc/man/hpwd.1 new file mode 100644 index 0000000..88f4d94 --- /dev/null +++ b/doc/man/hpwd.1 @@ -0,0 +1,15 @@ +.TH HPWD 1 05-Apr-1996 HFSUTILS +.SH NAME +hpwd \- print the full path to the current HFS working directory +.SH SYNOPSIS +hpwd +.SH DESCRIPTION +.B hpwd +displays the complete (absolute) HFS pathname to the current working directory +on the current volume. The current working directory can be changed with the +.B hcd +command. +.SH SEE ALSO +hfsutils(1), hcd(1) +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hrename.1 b/doc/man/hrename.1 new file mode 100644 index 0000000..6097e65 --- /dev/null +++ b/doc/man/hrename.1 @@ -0,0 +1,27 @@ +.TH HRENAME 1 04-Jun-1996 HFSUTILS +.SH NAME +hrename \- rename or move an HFS file or directory +.SH SYNOPSIS +hrename +.I hfs-src-path +[...] +.I hfs-target-path +.SH DESCRIPTION +.B hrename +changes the name and/or location of one or more files or directories. If only +one source path is specified and the target path is in the same directory as +the source, only the name is changed. Otherwise the source object is moved as +well as possibly being renamed. +.PP +If multiple source paths are specified, the target must be a directory, and +all of the source objects will be moved into it (keeping the same names.) +.PP +If the source path refers to the root directory of a volume, the volume name +will be changed. The root directory cannot be moved. Note that volume names +can only have 1-27 characters, while all other names may have 1-31 characters. +.SH SEE ALSO +hfsutils(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hrmdir.1 b/doc/man/hrmdir.1 new file mode 100644 index 0000000..a07f721 --- /dev/null +++ b/doc/man/hrmdir.1 @@ -0,0 +1,17 @@ +.TH HRMDIR 1 05-Apr-1996 HFSUTILS +.SH NAME +hrmdir \- remove an empty HFS directory +.SH SYNOPSIS +hrmdir +.I hfs-path +[...] +.SH DESCRIPTION +.B hrmdir +deletes directories (folders) from the current HFS volume. Each named +directory must already be empty. +.SH SEE ALSO +hfsutils(1), hmkdir(1), hdel(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/humount.1 b/doc/man/humount.1 new file mode 100644 index 0000000..b857178 --- /dev/null +++ b/doc/man/humount.1 @@ -0,0 +1,24 @@ +.TH HUMOUNT 1 05-Apr-1996 HFSUTILS +.SH NAME +humount \- remove an HFS volume from the list of known volumes +.SH SYNOPSIS +humount +.RI [ volume-name-or-path ] +.SH DESCRIPTION +.B humount +is used to forget about an HFS volume previously mounted with +.BR hmount . +Either the volume's name or the UNIX path to the volume may be used to specify +the volume. If no name or path is given, the current volume is assumed. +.SH NOTES +It is not strictly necessary to use this command. It does not affect any +physical volumes; it only updates the file named +.I .hcwd +in the user's home directory used to keep track of the currently known HFS +volumes. +.SH SEE ALSO +hfsutils(1), hmount(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie diff --git a/doc/man/hvol.1 b/doc/man/hvol.1 new file mode 100644 index 0000000..69b19cb --- /dev/null +++ b/doc/man/hvol.1 @@ -0,0 +1,30 @@ +.TH HVOL 1 21-Apr-1996 HFSUTILS +.SH NAME +hvol \- display or change the current HFS volume +.SH SYNOPSIS +hvol +.RI [ volume-or-path ] +.SH DESCRIPTION +With no arguments, +.B hvol +displays the name and path to the "current" HFS volume as well as the names +and paths of all previously mounted ("known") volumes. +.PP +With an argument, +.B hvol +changes the current volume to be the one specified. Either the name of the +volume or the path to its UNIX source can be specified. The volume must have +been previously mounted using +.BR hmount . +.PP +A separate "current working directory" is maintained for each mounted volume. +This information is kept in a file named +.I .hcwd +in the user's home directory. +.SH SEE ALSO +hfsutils(1), hmount(1), humount(1) +.SH FILES +$HOME/.hcwd +.SH AUTHOR +Robert Leslie + diff --git a/doc/man/xhfs.1 b/doc/man/xhfs.1 new file mode 100644 index 0000000..394cc01 --- /dev/null +++ b/doc/man/xhfs.1 @@ -0,0 +1,47 @@ +.TH XHFS 1 02-Apr-1996 HFSUTILS +.SH NAME +xhfs \- graphical interface for manipulating HFS volumes +.SH SYNOPSIS +xhfs +.RI [ "left-path " [ right-path ]] +.SH DESCRIPTION +.B xhfs +presents a graphical front-end for browsing and copying files on HFS-formatted +volumes. +.PP +The display is divided into two parts, left and right, which can each +independently view a directory on either an HFS volume or the host (UNIX) +filesystem. Double-clicking the name of a directory in either view will open +that directory. A pop-up menu at the top of each directory view can be used to +navigate to any directory between the current and the beginning of the +hierarchy. +.PP +Text files can be viewed by double-clicking them. Any file or set of files can +be copied to the directory shown in the other view by selecting them and +clicking the "Copy" button. Copying is performed according to the selected +copy mode: +.SP +.TP +.B "MacBinary II" +The file(s) will be copied using the MacBinary II format. This is the +recommended mode for transferring arbitrary Macintosh files. +.TP +.B BinHex +The file(s) will be copied using the BinHex format. This mode should be used +to encode Macintosh files into strict ASCII format. +.TP +.B Text +In this mode, only the data fork(s) of the selected file(s) are copied. +Furthermore, translation is performed on the data's end-of-line characters to +conform to the standard for text files on the destination. +.TP +.B "Raw Data" +In this mode, only the data fork(s) of the selected file(s) are copied. +However, no translation is performed whatsoever on the data. +.TP +.B Automatic +A copy mode will be selected automatically according to a set of heuristics. +.SH SEE ALSO +hfsutils(1), hfs(1) +.SH AUTHOR +Robert Leslie diff --git a/dstring.c b/dstring.c new file mode 100644 index 0000000..0b9d5f7 --- /dev/null +++ b/dstring.c @@ -0,0 +1,124 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: dstring.c,v 1.7 1998/11/02 22:08:28 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "dstring.h" + +/* + * NAME: dstring->init() + * DESCRIPTION: initialize a new dynamic string + */ +void dstr_init(dstring *string) +{ + string->str = string->sbuf; + string->len = 0; + string->space = sizeof(string->sbuf); + string->sbuf[0] = 0; +} + +/* + * NAME: dstring->append() + * DESCRIPTION: append to a dynamic string + */ +int dstr_append(dstring *string, const char *str, size_t len) +{ + size_t newlen; + + if (len == (size_t) -1) + len = strlen(str); + + newlen = string->len + len; + + /* make sure there is room */ + + if (newlen >= string->space) + { + char *new; + + newlen *= 2; + + new = malloc(newlen); + if (new == 0) + return -1; + + string->space = newlen; + + memcpy(new, string->str, string->len); + + if (string->str != string->sbuf) + free(string->str); + + string->str = new; + } + + /* append the string */ + + memcpy(string->str + string->len, str, len); + + string->len += len; + string->str[string->len] = 0; + + return 0; +} + +/* + * NAME: dstring->string() + * DESCRIPTION: return a pointer to a dynamic string's content + */ +char *dstr_string(dstring *string) +{ + return string->str; +} + +/* + * NAME: dstring->length() + * DESCRIPTION: return the length of a dynamic string + */ +int dstr_length(dstring *string) +{ + return string->len; +} + +/* + * NAME: dstring->shrink() + * DESCRIPTION: truncate a dynamic string to a shorter length + */ +void dstr_shrink(dstring *string, size_t len) +{ + if (len < string->len) + string->len = len; +} + +/* + * NAME: dstring->free() + * DESCRIPTION: free a dynamic string + */ +void dstr_free(dstring *string) +{ + if (string->str != string->sbuf) + free(string->str); +} diff --git a/dstring.h b/dstring.h new file mode 100644 index 0000000..bb8d74e --- /dev/null +++ b/dstring.h @@ -0,0 +1,34 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: dstring.h,v 1.7 1998/11/02 22:08:29 rob Exp $ + */ + +typedef struct { + char *str; + size_t len; + size_t space; + char sbuf[50]; +} dstring; + +void dstr_init(dstring *); +int dstr_append(dstring *, const char *, size_t); +char *dstr_string(dstring *); +int dstr_length(dstring *); +void dstr_shrink(dstring *, size_t); +void dstr_free(dstring *); diff --git a/glob.c b/glob.c new file mode 100644 index 0000000..fc47344 --- /dev/null +++ b/glob.c @@ -0,0 +1,359 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: glob.c,v 1.6 1998/04/11 08:26:55 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "dlist.h" +# include "dstring.h" +# include "hfs.h" +# include "glob.h" + +/* + * NAME: strmatch() + * DESCRIPTION: return 1 iff a string matches a given (glob) pattern + */ +static +int strmatch(const char *str, const char *pat) +{ + while (1) + { + if (*str == 0 && *pat != 0 && *pat != '*') + return 0; + + switch (*pat) + { + case 0: + return (*str == 0); + + case '*': + if (*++pat == 0) + return 1; + + while (1) + { + if (strmatch(str, pat)) + return 1; + + if (*str++ == 0) + return 0; + } + + case '?': + break; + + case '[': + { + ++pat; + + while (1) + { + unsigned char p0, p1, s; + + p0 = *pat; + + if (p0 == ']' || p0 == 0) + return 0; + + s = *str; + + if (hfs_charorder[p0] == hfs_charorder[s]) + break; + + if (pat[1] == '-') + { + p1 = pat[2]; + + if (p1 == 0) + return 0; + + if ((hfs_charorder[p0] <= hfs_charorder[s] && + hfs_charorder[p1] >= hfs_charorder[s]) || + (hfs_charorder[p0] >= hfs_charorder[s] && + hfs_charorder[p1] <= hfs_charorder[s])) + break; + + pat += 2; + } + + ++pat; + } + + while (*pat != ']') + { + if (*pat == 0) + { + --pat; + break; + } + + ++pat; + } + } + break; + + case '\\': + if (*++pat == 0) + return 0; + + /* fall through */ + + default: + if (hfs_charorder[(unsigned char) *pat] != + hfs_charorder[(unsigned char) *str]) + return 0; + } + + ++pat, ++str; + } +} + +/* + * NAME: doglob() + * DESCRIPTION: perform recursive depth-first traversal of path to be globbed + */ +static +int doglob(hfsvol *vol, dlist *list, const char *dir, const char *rem) +{ + dstring new; + int special, len, result = 0; + const char *obrace, *cbrace, *ptr; + + dstr_init(&new); + + special = 0; + obrace = cbrace = 0; + + for (ptr = rem; *ptr && (obrace || *ptr != ':'); ++ptr) + { + switch (*ptr) + { + case '{': + if (obrace == 0) + obrace = ptr; + break; + + case '}': + if (obrace && cbrace == 0) + cbrace = ptr; + break; + + case '\\': + if (*++ptr == 0) + --ptr; + + case '*': + case '[': + case '?': + special = 1; + break; + } + } + + if (obrace) + { + const char *elt; + + if (cbrace == 0 || + dstr_append(&new, rem, obrace - rem) == -1) + { + dstr_free(&new); + return -1; + } + len = dstr_length(&new); + + for (ptr = obrace; *ptr != '}'; ) + { + elt = ptr + 1; + + ptr = elt; + while (*ptr != '}' && *ptr != ',') + ++ptr; + + if (dstr_append(&new, elt, ptr - elt) == -1 || + dstr_append(&new, cbrace + 1, -1) == -1 || + doglob(vol, list, dir, dstr_string(&new)) == -1) + { + dstr_free(&new); + return -1; + } + + dstr_shrink(&new, len); + } + + dstr_free(&new); + return 0; + } + + if (dstr_append(&new, dir, -1) == -1) + { + dstr_free(&new); + return -1; + } + len = dstr_length(&new); + + if (special) + { + hfsdirent ent; + hfsdir *d; + dstring pat; + int found = 0; + + dstr_init(&pat); + if (dstr_append(&pat, rem, ptr - rem) == -1) + { + dstr_free(&pat); + dstr_free(&new); + return -1; + } + + if (*dir == 0 && strchr(rem, ':') == 0) + d = hfs_opendir(vol, ":"); + else + d = hfs_opendir(vol, dir); + + if (d == 0) + { + dstr_free(&pat); + dstr_free(&new); + return -1; + } + + while (hfs_readdir(d, &ent) != -1) + { + if (ent.fdflags & HFS_FNDR_ISINVISIBLE) + continue; + + if (strmatch(ent.name, dstr_string(&pat))) + { + dstr_shrink(&new, len); + if (dstr_append(&new, ent.name, -1) == -1) + { + result = -1; + break; + } + + if (*ptr == 0) + { + found = 1; + result = dl_append(list, dstr_string(&new)); + + if (result == -1) + break; + } + else if (ent.flags & HFS_ISDIR) + { + if (dstr_append(&new, ":", 1) == -1) + result = -1; + else + { + found = 1; + result = doglob(vol, list, dstr_string(&new), ptr + 1); + } + + if (result == -1) + break; + } + } + } + + hfs_closedir(d); + + if (result == 0 && ! found) + { + dstr_shrink(&new, len); + if (dstr_append(&new, rem, -1) == -1) + result = -1; + else + { + char *ptr, *rem; + + for (rem = dstr_string(&new) + len, ptr = rem; *rem; + ++rem, ++ptr) + { + if (*rem == '\\') + ++rem; + + *ptr = *rem; + } + *ptr = 0; + + result = dl_append(list, dstr_string(&new)); + } + } + + dstr_free(&pat); + dstr_free(&new); + + return result; + } + + if (dstr_append(&new, rem, ptr - rem) == -1) + result = -1; + else + { + if (*ptr) + { + if (dstr_append(&new, ":", 1) == -1) + result = -1; + else + result = doglob(vol, list, dstr_string(&new), ptr + 1); + + dstr_free(&new); + return result; + } + + result = dl_append(list, dstr_string(&new)); + } + + dstr_free(&new); + return result; +} + +/* + * NAME: hfs->glob() + * DESCRIPTION: perform glob pattern matching + */ +char **hfs_glob(hfsvol *vol, int argc, char *argv[], int *nelts) +{ + dlist list; + int i; + + if (dl_init(&list) == -1) + return 0; + + for (i = 0; i < argc; ++i) + { + if (doglob(vol, &list, "", argv[i]) == -1) + { + dl_free(&list); + return 0; + } + } + + *nelts = dl_size(&list); + + return dl_array(&list); +} diff --git a/glob.h b/glob.h new file mode 100644 index 0000000..02eabe6 --- /dev/null +++ b/glob.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: glob.h,v 1.6 1998/04/11 08:26:55 rob Exp $ + */ + +char **hfs_glob(hfsvol *, int, char *[], int *); diff --git a/hattrib.c b/hattrib.c new file mode 100644 index 0000000..2f70ce6 --- /dev/null +++ b/hattrib.c @@ -0,0 +1,231 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hattrib.c,v 1.8 1998/08/31 04:27:17 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hattrib.h" + +/* + * NAME: usage() + * DESCRIPTION: display usage message + */ +static +int usage(void) +{ + fprintf(stderr, + "Usage: %s [-t TYPE] [-c CREA] [-|+i] [-|+l] hfs-path [...]\n" + " %s -b hfs-path\n", + argv0, argv0); + + return 1; +} + +/* + * NAME: hattrib->main() + * DESCRIPTION: implement hattrib command + */ +int hattrib_main(int argc, char *argv[]) +{ + const char *type = 0, *crea = 0; + int invis = 0, lock = 0, bless = 0; + hfsvol *vol; + int fargc; + char **fargv; + int i, result = 0; + + for (i = 1; i < argc; ++i) + { + switch (argv[i][0]) + { + case '-': + switch (argv[i][1]) + { + case 't': + type = argv[++i]; + + if (type == 0) + return usage(); + + if (strlen(type) != 4) + { + fprintf(stderr, "%s: file type must be 4 characters\n", + argv0); + return 1; + } + continue; + + case 'c': + crea = argv[++i]; + + if (crea == 0) + return usage(); + + if (strlen(crea) != 4) + { + fprintf(stderr, "%s: file creator must be 4 characters\n", + argv0); + return 1; + } + continue; + + case 'i': + invis = -1; + continue; + + case 'l': + lock = -1; + continue; + + case 'b': + bless = 1; + continue; + + default: + return usage(); + } + break; + + case '+': + switch (argv[i][1]) + { + case 'i': + invis = 1; + continue; + + case 'l': + lock = 1; + continue; + + default: + return usage(); + } + break; + } + + break; + } + + if (argc - i == 0) + return usage(); + + if (i == 1) + { + fprintf(stderr, "%s: no attributes specified\n", argv0); + return 1; + } + + if (bless && (lock || invis || type || crea || argc - i > 1)) + return usage(); + + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - i, &argv[i], &fargc, &result); + + if (result == 0) + { + hfsdirent ent; + + if (bless) + { + if (fargc != 1) + { + fprintf(stderr, "%s: %s: ambiguous path\n", argv0, argv[i]); + result = 1; + } + else + { + hfsvolent volent; + + if (hfs_stat(vol, fargv[0], &ent) == -1 || + hfs_vstat(vol, &volent) == -1) + { + hfsutil_perrorp(fargv[0]); + result = 1; + } + else + { + volent.blessed = ent.cnid; + + if (hfs_vsetattr(vol, &volent) == -1) + { + hfsutil_perrorp(fargv[0]); + result = 1; + } + } + } + } + else + { + for (i = 0; i < fargc; ++i) + { + if (hfs_stat(vol, fargv[i], &ent) == -1) + { + hfsutil_perrorp(fargv[i]); + result = 1; + } + else + { + if (! (ent.flags & HFS_ISDIR)) + { + if (type) + memcpy(ent.u.file.type, type, 4); + if (crea) + memcpy(ent.u.file.creator, crea, 4); + } + + if (invis < 0) + ent.fdflags &= ~HFS_FNDR_ISINVISIBLE; + else if (invis > 0) + ent.fdflags |= HFS_FNDR_ISINVISIBLE; + + if (lock < 0) + ent.flags &= ~HFS_ISLOCKED; + else if (lock > 0) + ent.flags |= HFS_ISLOCKED; + + if (hfs_setattr(vol, fargv[i], &ent) == -1) + { + hfsutil_perrorp(fargv[i]); + result = 1; + } + } + } + } + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hattrib.h b/hattrib.h new file mode 100644 index 0000000..a9ff98a --- /dev/null +++ b/hattrib.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hattrib.h,v 1.6 1998/04/11 08:26:56 rob Exp $ + */ + +int hattrib_main(int, char *[]); diff --git a/hcd.c b/hcd.c new file mode 100644 index 0000000..fb0daba --- /dev/null +++ b/hcd.c @@ -0,0 +1,115 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcd.c,v 1.8 1998/11/02 22:08:30 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hcd.h" + +/* + * NAME: hcd->main() + * DESCRIPTION: implement hcd command + */ +int hcd_main(int argc, char *argv[]) +{ + mountent *ment; + hfsvol *vol; + hfsvolent vent; + char *path, root[HFS_MAX_VLEN + 1 + 1]; + int fargc; + char **fargv = 0; + int result = 0; + + if (argc > 2) + { + fprintf(stderr, "Usage: %s [hfs-path]\n", argv0); + return 1; + } + + vol = hfsutil_remount(ment = hcwd_getvol(-1), HFS_MODE_RDONLY); + if (vol == 0) + return 1; + + if (argc == 2) + { + fargv = hfsutil_glob(vol, 1, &argv[1], &fargc, &result); + if (result == 0) + { + if (fargc != 1) + { + fprintf(stderr, "%s: %s: ambiguous path\n", argv0, argv[1]); + result = 1; + } + else + path = fargv[0]; + } + } + else + { + hfs_vstat(vol, &vent); + + strcpy(root, vent.name); + strcat(root, ":"); + path = root; + } + + if (result == 0) + { + if (hfs_chdir(vol, path) == -1) + { + hfsutil_perrorp(path); + result = 1; + } + else + { + path = hfsutil_getcwd(vol); + if (path == 0) + { + hfsutil_perror("Can't get new HFS directory path"); + result = 1; + } + + if (result == 0 && hcwd_setcwd(ment, path) == -1) + { + perror("Can't set new HFS directory"); + result = 1; + } + + if (path) + free(path); + } + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hcd.h b/hcd.h new file mode 100644 index 0000000..0fb0699 --- /dev/null +++ b/hcd.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcd.h,v 1.7 1998/04/11 08:26:56 rob Exp $ + */ + +int hcd_main(int, char *[]); diff --git a/hcopy.c b/hcopy.c new file mode 100644 index 0000000..3a0ea86 --- /dev/null +++ b/hcopy.c @@ -0,0 +1,318 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcopy.c,v 1.8 1998/04/11 08:26:56 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# include +# include +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hcopy.h" +# include "copyin.h" +# include "copyout.h" + +extern int optind; + +/* + * NAME: automode_unix() + * DESCRIPTION: automatically choose copyin transfer mode for UNIX path + */ +static +cpifunc automode_unix(const char *path) +{ + int i; + struct { + const char *ext; + cpifunc func; + } exts[] = { + { ".bin", cpi_macb }, + { ".hqx", cpi_binh }, + + { ".txt", cpi_text }, + { ".c", cpi_text }, + { ".h", cpi_text }, + { ".html", cpi_text }, + { ".htm", cpi_text }, + { ".rtf", cpi_text }, + + { 0, 0 } + }; + + path += strlen(path); + + for (i = 0; exts[i].ext; ++i) + { + if (strcasecmp(path - strlen(exts[i].ext), exts[i].ext) == 0) + return exts[i].func; + } + + return cpi_raw; +} + +/* + * NAME: do_copyin() + * DESCRIPTION: copy files from UNIX to HFS + */ +static +int do_copyin(hfsvol *vol, int argc, char *argv[], const char *dest, int mode) +{ + hfsdirent ent; + struct stat sbuf; + cpifunc copyfile; + int i, result = 0; + + if (argc > 1 && (hfs_stat(vol, dest, &ent) == -1 || + ! (ent.flags & HFS_ISDIR))) + { + ERROR(ENOTDIR, 0); + hfsutil_perrorp(dest); + + return 1; + } + + switch (mode) + { + case 'm': + copyfile = cpi_macb; + break; + + case 'b': + copyfile = cpi_binh; + break; + + case 't': + copyfile = cpi_text; + break; + + case 'r': + copyfile = cpi_raw; + break; + } + + for (i = 0; i < argc; ++i) + { + if (stat(argv[i], &sbuf) != -1 && + S_ISDIR(sbuf.st_mode)) + { + ERROR(EISDIR, 0); + hfsutil_perrorp(argv[i]); + + result = 1; + } + else + { + if (mode == 'a') + copyfile = automode_unix(argv[i]); + + if (copyfile(argv[i], vol, dest) == -1) + { + ERROR(errno, cpi_error); + hfsutil_perrorp(argv[i]); + + result = 1; + } + } + } + + return result; +} + +/* + * NAME: automode_hfs() + * DESCRIPTION: automatically choose copyout transfer mode for HFS path + */ +static +cpofunc automode_hfs(hfsvol *vol, const char *path) +{ + hfsdirent ent; + + if (hfs_stat(vol, path, &ent) != -1) + { + if (strcmp(ent.u.file.type, "TEXT") == 0 || + strcmp(ent.u.file.type, "ttro") == 0) + return cpo_text; + else if (ent.u.file.rsize == 0) + return cpo_raw; + } + + return cpo_macb; +} + +/* + * NAME: do_copyout() + * DESCRIPTION: copy files from HFS to UNIX + */ +static +int do_copyout(hfsvol *vol, int argc, char *argv[], const char *dest, int mode) +{ + struct stat sbuf; + hfsdirent ent; + cpofunc copyfile; + int i, result = 0; + + if (argc > 1 && (stat(dest, &sbuf) == -1 || + ! S_ISDIR(sbuf.st_mode))) + { + ERROR(ENOTDIR, 0); + hfsutil_perrorp(dest); + + return 1; + } + + switch (mode) + { + case 'm': + copyfile = cpo_macb; + break; + + case 'b': + copyfile = cpo_binh; + break; + + case 't': + copyfile = cpo_text; + break; + + case 'r': + copyfile = cpo_raw; + break; + } + + for (i = 0; i < argc; ++i) + { + if (hfs_stat(vol, argv[i], &ent) != -1 && + (ent.flags & HFS_ISDIR)) + { + ERROR(EISDIR, 0); + hfsutil_perrorp(argv[i]); + + result = 1; + } + else + { + if (mode == 'a') + copyfile = automode_hfs(vol, argv[i]); + + if (copyfile(vol, argv[i], dest) == -1) + { + ERROR(errno, cpo_error); + hfsutil_perrorp(argv[i]); + + result = 1; + } + } + } + + return result; +} + +/* + * NAME: usage() + * DESCRIPTION: display usage message + */ +static +int usage(void) +{ + fprintf(stderr, "Usage: %s [-m|-b|-t|-r|-a] source-path [...] target-path\n", + argv0); + + return 1; +} + +/* + * NAME: hcopy->main() + * DESCRIPTION: implement hcopy command + */ +int hcopy_main(int argc, char *argv[]) +{ + int nargs, mode = 'a', result = 0; + const char *target; + int fargc; + char **fargv; + hfsvol *vol; + int (*copy)(hfsvol *, int, char *[], const char *, int); + + while (1) + { + int opt; + + opt = getopt(argc, argv, "mbtra"); + if (opt == EOF) + break; + + switch (opt) + { + case '?': + return usage(); + + default: + mode = opt; + } + } + + nargs = argc - optind; + + if (nargs < 2) + return usage(); + + target = argv[argc - 1]; + + if (strchr(target, ':') && target[0] != '.' && target[0] != '/') + { + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + copy = do_copyin; + fargc = nargs - 1; + fargv = &argv[optind]; + } + else + { + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_RDONLY); + if (vol == 0) + return 1; + + copy = do_copyout; + fargv = hfsutil_glob(vol, nargs - 1, &argv[optind], &fargc, &result); + } + + if (result == 0) + result = copy(vol, fargc, fargv, target, mode); + + hfsutil_unmount(vol, &result); + + if (fargv && fargv != &argv[optind]) + free(fargv); + + return result; +} diff --git a/hcopy.h b/hcopy.h new file mode 100644 index 0000000..a2f40cd --- /dev/null +++ b/hcopy.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcopy.h,v 1.6 1998/04/11 08:26:56 rob Exp $ + */ + +int hcopy_main(int, char *[]); diff --git a/hcwd.c b/hcwd.c new file mode 100644 index 0000000..4c9d181 --- /dev/null +++ b/hcwd.c @@ -0,0 +1,371 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcwd.c,v 1.9 1998/04/11 08:26:56 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# include +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" + +# define STATEFNAME ".hcwd" +# define TABCHUNKSZ 8 + +static FILE *statef = 0; +static mountent *mounts = 0; +static int mtabsz = 0, nmounts = 0; +static int curvol = -1, dirty = 0; + +extern int errno; + +/* + * NAME: addent() + * DESCRIPTION: insert mount entry into table + */ +static +int addent(mountent *ent) +{ + if (nmounts >= mtabsz) + { + mountent *newmem; + + mtabsz += TABCHUNKSZ; + newmem = mounts ? realloc(mounts, mtabsz * sizeof(mountent)) : + malloc(mtabsz * sizeof(mountent)); + if (newmem == 0) + return -1; + + mounts = newmem; + } + + mounts[nmounts++] = *ent; + + dirty = 1; + + return 0; +} + +/* + * NAME: hcwd->init() + * DESCRIPTION: read state file + */ +int hcwd_init(void) +{ + const char *home, *start; + char buf[512], *path, *ptr; + mountent entry; + int newcur = -1; + + home = getenv("HOME"); + if (home == 0) + home = ""; + + path = malloc(strlen(home) + 1 + sizeof(STATEFNAME)); + if (path == 0) + return -1; + + strcpy(path, home); + strcat(path, "/" STATEFNAME); + + statef = fopen(path, "r+"); + if (statef == 0 && errno == ENOENT) + statef = fopen(path, "w+"); + + free(path); + + if (statef == 0) + return -1; + + if (fgets(buf, sizeof(buf), statef)) + newcur = atoi(buf); + + while (fgets(buf, sizeof(buf), statef)) + { + start = ptr = buf; + + while (*ptr && *ptr != '\n' && *ptr != '\t') + ++ptr; + + entry.vname[0] = 0; + if (*ptr == '\t') + { + *ptr++ = 0; + strcpy(entry.vname, start); + start = ptr; + } + + while (*ptr && *ptr != '\n' && *ptr != '\t') + ++ptr; + + if (*ptr == '\t') + { + *ptr++ = 0; + entry.vcrdate = strtol(start, 0, 0); + start = ptr; + } + + while (*ptr && *ptr != '\n' && *ptr != '\t') + ++ptr; + + if (*ptr == '\t') + { + *ptr++ = 0; + entry.path = strdup(start); + if (entry.path == 0) + return -1; + + start = ptr; + } + + while (*ptr && *ptr != '\n' && *ptr != '\t') + ++ptr; + + if (*ptr == '\t') + { + *ptr++ = 0; + entry.partno = atoi(start); + start = ptr; + } + + while (*ptr && *ptr != '\n' && *ptr != '\t') + ++ptr; + + if (*ptr == '\n' || *ptr == '\t') + *ptr = 0; + + if (*start) + { + entry.cwd = strdup(start); + if (entry.cwd == 0) + return -1; + + if (addent(&entry) == -1) + return -1; + } + } + + curvol = (newcur >= 0 && newcur < nmounts) ? newcur : -1; + + return 0; +} + +/* + * NAME: hcwd->finish() + * DESCRIPTION: flush changes to state file + */ +int hcwd_finish(void) +{ + if (statef && mounts && dirty) + { + int i; + + rewind(statef); + if (ftruncate(fileno(statef), 0) == -1) + return -1; + + fprintf(statef, "%d\n", curvol); + + for (i = 0; i < nmounts; ++i) + fprintf(statef, "%s\t%ld\t%s\t%d\t%s\n", + mounts[i].vname, + mounts[i].vcrdate, + mounts[i].path, + mounts[i].partno, + mounts[i].cwd); + + dirty = 0; + } + + if (mounts) + { + mountent *ent; + + for (ent = mounts; ent < mounts + nmounts; ++ent) + { + if (ent->path) + free(ent->path); + if (ent->cwd) + free(ent->cwd); + } + + free(mounts); + } + + if (statef && fclose(statef) == EOF) + return -1; + + return 0; +} + +/* + * NAME: hcwd->mounted() + * DESCRIPTION: register a mounted volume + */ +int hcwd_mounted(const char *vname, long vcrdate, const char *path, int partno) +{ + mountent *entry, new; + + for (entry = mounts; entry < mounts + nmounts; ++entry) + { + if (strcmp(entry->path, path) == 0 && + entry->partno == partno) + { + /* update entry */ + + if (entry->cwd) + free(entry->cwd); + + strcpy(entry->vname, vname); + entry->vcrdate = vcrdate; + entry->cwd = strdup(":"); + if (entry->cwd == 0) + return -1; + + curvol = entry - mounts; + dirty = 1; + + return 0; + } + } + + strcpy(new.vname, vname); + new.vcrdate = vcrdate; + new.path = strdup(path); + new.partno = partno; + new.cwd = strdup(":"); + + if (new.path == 0 || + new.cwd == 0) + return -1; + + if (addent(&new) == -1) + return -1; + + curvol = nmounts - 1; + + return 0; +} + +/* + * NAME: hcwd->umounted() + * DESCRIPTION: unregister a previously mounted volume + */ +int hcwd_umounted(int vol) +{ + mountent *entry; + int i; + + if (vol < 0) + vol = curvol; + + if (vol < 0 || vol >= nmounts) + return -1; + + entry = &mounts[vol]; + + if (entry->path) + free(entry->path); + if (entry->cwd) + free(entry->cwd); + + for (i = vol + 1; i < nmounts; ++i) + mounts[i - 1] = mounts[i]; + + --nmounts; + + if (curvol > vol) + --curvol; + else if (curvol == vol) + curvol = -1; + + return 0; +} + +/* + * NAME: hcwd->getvol() + * DESCRIPTION: return a mount entity + */ +mountent *hcwd_getvol(int vol) +{ + if (vol < 0) + vol = curvol; + + if (vol < 0 || vol >= nmounts) + return 0; + + return &mounts[vol]; +} + +/* + * NAME: hcwd->setvol() + * DESCRIPTION: change the current volume + */ +int hcwd_setvol(int vol) +{ + if (vol < 0 || vol >= nmounts) + return -1; + + if (curvol != vol) + { + curvol = vol; + dirty = 1; + } + + return 0; +} + +/* + * NAME: hcwd->setcwd() + * DESCRIPTION: change the current working directory for a mount entity + */ +int hcwd_setcwd(mountent *ent, const char *newcwd) +{ + const char *path; + char *cwd; + + path = newcwd; + while (*path && *path != ':') + ++path; + + memcpy(ent->vname, newcwd, path - newcwd); + ent->vname[path - newcwd] = 0; + + cwd = (*path == 0) ? strdup(":") : strdup(path); + if (cwd == 0) + return -1; + + if (ent->cwd) + free(ent->cwd); + + ent->cwd = cwd; + dirty = 1; + + return 0; +} diff --git a/hcwd.h b/hcwd.h new file mode 100644 index 0000000..4b0a934 --- /dev/null +++ b/hcwd.h @@ -0,0 +1,37 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hcwd.h,v 1.8 1998/04/11 08:26:56 rob Exp $ + */ + +typedef struct { + char vname[HFS_MAX_VLEN + 1]; + long vcrdate; + char *path; + int partno; + char *cwd; +} mountent; + +int hcwd_init(void); +int hcwd_finish(void); + +int hcwd_mounted(const char *, long, const char *, int); +int hcwd_umounted(int vol); +mountent *hcwd_getvol(int); +int hcwd_setvol(int); +int hcwd_setcwd(mountent *, const char *); diff --git a/hdel.c b/hdel.c new file mode 100644 index 0000000..9d4253a --- /dev/null +++ b/hdel.c @@ -0,0 +1,75 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hdel.c,v 1.6 1998/04/11 08:26:56 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hdel.h" + +/* + * NAME: hdel->main() + * DESCRIPTION: implement hdel command + */ +int hdel_main(int argc, char *argv[]) +{ + hfsvol *vol; + int fargc; + char **fargv; + int i, result = 0; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s hfs-path [...]\n", argv0); + return 1; + } + + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - 1, &argv[1], &fargc, &result); + + if (result == 0) + { + for (i = 0; i < fargc; ++i) + { + if (hfs_delete(vol, fargv[i]) == -1) + { + hfsutil_perrorp(fargv[i]); + result = 1; + } + } + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hdel.h b/hdel.h new file mode 100644 index 0000000..3b004d5 --- /dev/null +++ b/hdel.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hdel.h,v 1.6 1998/04/11 08:26:57 rob Exp $ + */ + +int hdel_main(int, char *[]); diff --git a/hdisk.pl b/hdisk.pl new file mode 100644 index 0000000..e12bc14 --- /dev/null +++ b/hdisk.pl @@ -0,0 +1,64 @@ +#! /usr/bin/perl +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: hdisk.pl,v 1.6 1998/04/11 08:26:57 rob Exp $ +# + +die "Usage: $0 device-path\n" unless (@ARGV == 1); + +($disk) = @ARGV; + +format STDOUT_TOP = + # Partition Type HFS Volume Name Start Length +------------------------------------------------------------------------------- +. + +format STDOUT = +@# @<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<< @####### @######## +$bnum, $pmParType, $drVN, $pmPyPartStart, $pmPartBlkCnt +. + +open(DISK, $disk) || die "$disk: $!\n"; + +$bnum = 1; + +do { + seek(DISK, 512 * $bnum, 0) || die "seek: $!\n"; + read(DISK, $block, 512) || die "read: $!\n"; + + ($pmSig, $pmMapBlkCnt, $pmPyPartStart, $pmPartBlkCnt, $pmParType) = + (unpack('n2 N3 A32 A32 N10 A16', $block))[0, 2..4, 6]; + + die "$disk: unsupported partition map\n" if ($pmSig == 0x5453); + die "$disk: no partition map\n" unless ($pmSig == 0x504d); + + if ($pmParType eq 'Apple_HFS') { + seek(DISK, 512 * ($pmPyPartStart + 2), 0) || die "seek: $!\n"; + read(DISK, $block, 512) || die "read: $!\n"; + + ($len, $drVN) = (unpack('n N2 n5 N2 n N n c A27', $block))[13, 14]; + $drVN = substr($drVN, 0, $len); + } else { + $drVN = ''; + } + + write; +} while ($bnum++ < $pmMapBlkCnt); + +close(DISK); diff --git a/hformat.c b/hformat.c new file mode 100644 index 0000000..aae2dd6 --- /dev/null +++ b/hformat.c @@ -0,0 +1,190 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hformat.c,v 1.9 1998/11/02 22:08:31 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "suid.h" +# include "hformat.h" + +# define O_FORCE 0x01 + +extern char *optarg; +extern int optind; + +/* + * NAME: usage() + * DESCRIPTION: display usage message + */ +static +void usage(void) +{ + fprintf(stderr, "Usage: %s [-f] [-l label] path [partition-no]\n", argv0); +} + +/* + * NAME: do_format() + * DESCRIPTION: call hfs_format() with necessary privileges + */ +static +hfsvol *do_format(const char *path, int partno, int mode, const char *vname) +{ + hfsvol *vol = 0; + + suid_enable(); + + if (hfs_format(path, partno, mode, vname, 0, 0) != -1) + vol = hfs_mount(path, partno, HFS_MODE_ANY); + + suid_disable(); + + return vol; +} + +/* + * NAME: hformat->main() + * DESCRIPTION: implement hformat command + */ +int hformat_main(int argc, char *argv[]) +{ + const char *vname; + char *path = 0; + hfsvol *vol; + hfsvolent ent; + int nparts, partno, options = 0, result = 0; + + vname = "Untitled"; + + while (1) + { + int opt; + + opt = getopt(argc, argv, "fl:"); + if (opt == EOF) + break; + + switch (opt) + { + case '?': + usage(); + goto fail; + + case 'f': + options |= O_FORCE; + break; + + case 'l': + vname = optarg; + break; + } + } + + if (argc - optind < 1 || argc - optind > 2) + { + usage(); + goto fail; + } + + path = hfsutil_abspath(argv[optind]); + if (path == 0) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + goto fail; + } + + suid_enable(); + nparts = hfs_nparts(path); + suid_disable(); + + if (argc - optind == 2) + { + partno = atoi(argv[optind + 1]); + + if (nparts != -1 && partno == 0) + { + if (options & O_FORCE) + { + fprintf(stderr, + "%s: warning: erasing partition information\n", argv0); + } + else + { + fprintf(stderr, "%s: medium is partitioned; " + "select partition > 0 or use -f\n", argv0); + goto fail; + } + } + } + else + { + if (nparts > 1) + { + fprintf(stderr, + "%s: must specify partition number (%d available)\n", + argv0, nparts); + goto fail; + } + else if (nparts == -1) + partno = 0; + else + partno = 1; + } + + vol = do_format(path, partno, 0, vname); + if (vol == 0) + { + hfsutil_perror(path); + goto fail; + } + + hfs_vstat(vol, &ent); + hfsutil_pinfo(&ent); + + if (hcwd_mounted(ent.name, ent.crdate, path, partno) == -1) + { + perror("Failed to record mount"); + result = 1; + } + + hfsutil_unmount(vol, &result); + + free(path); + + return result; + +fail: + if (path) + free(path); + + return 1; +} diff --git a/hformat.h b/hformat.h new file mode 100644 index 0000000..e47b72f --- /dev/null +++ b/hformat.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hformat.h,v 1.7 1998/04/11 08:26:57 rob Exp $ + */ + +int hformat_main(int, char *[]); diff --git a/hfs.tcl b/hfs.tcl new file mode 100644 index 0000000..6e9374d --- /dev/null +++ b/hfs.tcl @@ -0,0 +1,491 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: hfs.tcl,v 1.9 1998/08/31 04:27:18 rob Exp $ +# + +proc ctime {secs} { + return [clock format $secs -format "%c"] +} + +proc getvol {var} { + global curvol + upvar $var vol + + if {! [info exists curvol]} { + error "no volume is current" + } + + set vol $curvol +} + +proc hinfo {} { + getvol vol + + if {[$vol islocked]} { + set locked " (locked)" + } else { + set locked "" + } + + puts stdout "Volume name is \"[$vol vname]\"$locked" + puts stdout "Volume was created on [ctime [$vol crdate]]" + puts stdout "Volume was last modified on [ctime [$vol mddate]]" + puts stdout "Volume has [lindex [$vol size] 1] bytes free" +} + +proc hmount {path {partno ""}} { + global mounts curpath curvol + + set nparts [hfs nparts $path] + if {$nparts >= 0} { + puts stdout [concat "$path contains $nparts HFS" \ + [ternary {$nparts == 1} "partition" "partitions"]] + } + + if {[string length $partno] == 0} { + if {$nparts > 0} { + puts stderr "partition unspecified; selecting number 1" + set partno 1 + } elseif {$nparts == -1} { + set partno 0 + } else { + set partno 1 + } + } + + set vol [hfs mount $path $partno] + + if {[info exists mounts($path)]} { + humount $path + } + + set curpath $path + set curvol $vol + set mounts($path) $vol + + hinfo +} + +proc humount {{path {}}} { + global mounts curpath curvol + + if {[string length $path] == 0} { + if {! [info exists curpath]} { + error "no volume is current" + } + + set path $curpath + } elseif {! [info exists mounts($path)]} { + error "$path not mounted" + } + + set vol $mounts($path) + unset mounts($path) + + if {[string compare $vol $curvol] == 0} { + unset curpath + unset curvol + } + + $vol umount +} + +proc hvol {name} { + global mounts curpath curvol + + if {[info exists mounts($name)]} { + set curpath $name + set curvol $mounts($name) + return + } + + error "unknown volume" +} + +proc hpwd {} { + getvol vol + + return "[join [$vol path] ":"]:" +} + +proc hcd {{path ""}} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + if {[string length $path] == 0} { + set path "[$vol vname]:" + } + + $vol cd $path +} + +proc timestr {secs} { + set ctime [ctime $secs] + + return "[string range $ctime 4 15][string range $ctime 19 23]" +} + +proc ternary {test true false} { + if {[uplevel expr $test]} { + return $true + } else { + return $false + } +} + +proc hdir {{path ":"}} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + foreach ent [$vol dir $path] { + array set item $ent + + if {$item(kind) == "directory"} { + puts stdout [format "d%s %9lu item%s %s %s" \ + [ternary {[lsearch $item(flags) "invis"] >= 0} "i" " "] \ + $item(size) \ + [ternary {$item(size) == 1} " " "s"] \ + [timestr $item(mddate)] \ + $item(name)] + } else { + puts stdout [format "%s%s %4s/%4s %9lu %9lu %s %s" \ + [ternary {[lsearch $item(flags) "locked"] >= 0} "F" "f"] \ + [ternary {[lsearch $item(flags) "invis"] >= 0} "i" " "] \ + $item(type) \ + $item(creator) \ + $item(rsize) \ + $item(dsize) \ + [timestr $item(mddate)] \ + $item(name)] + } + } +} + +proc hstat {{path ":"}} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + array set item [$vol stat $path] + + foreach elt [lsort [array names item]] { + if {[regexp {date$} $elt]} { + set value [ctime $item($elt)] + } else { + set value $item($elt) + } + + puts stdout [format "%-10s %s" "$elt:" $value] + } +} + +proc hmkdir {args} { + getvol vol + + foreach arg [$vol glob $args] { + $vol mkdir $arg + } +} + +proc hrmdir {args} { + getvol vol + + foreach arg [$vol glob $args] { + $vol rmdir $arg + } +} + +proc hcreate {path {type "TEXT"} {creator "UNIX"}} { + getvol vol + + set file [$vol create $path $type $creator] + $file close +} + +proc htouch {args} { + getvol vol + + foreach arg [$vol glob $args] { + if [catch {$vol touch $arg}] { + hcreate $arg + } + } +} + +proc hdel {args} { + getvol vol + + foreach arg [$vol glob $args] { + $vol delete $arg + } +} + +proc hrename {src dst} { + getvol vol + + set globbed [$vol glob [list $src]] + if {[llength $globbed] != 1} { + error "$src: ambiguous path" + } + set src [lindex $globbed 0] + + $vol rename $src $dst +} + +proc hbless {path} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + $vol bless $path +} + +proc hcat {path} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + set file [$vol open $path] + + while {1} { + set buf [$file read 512] + if {[string length $buf] == 0} { + $file close + break + } + + regsub -all "\r" $buf "\n" buf + + puts -nonewline stdout $buf + } +} + +proc hcopyout {path {dest "."} {mode ""}} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + if {[string length $mode] == 0} { + array set item [$vol stat $path] + + if {$item(kind) == "directory"} { + error "can't copy whole directories" + } elseif {[regexp {^TEXT|ttro$} $item(type)]} { + set mode text + } else { + set mode macb + } + } + + $vol copyout $mode $path $dest +} + +proc hcopyin {path {dest ":"} {mode ""}} { + getvol vol + + set globbed [$vol glob [list $path]] + if {[llength $globbed] != 1} { + error "$path: ambiguous path" + } + set path [lindex $globbed 0] + + if {[string length $mode] == 0} { + if {[regexp {\.bin$} $path]} { + set mode macb + } elseif {[regexp {\.hqx$} $path]} { + set mode binh + } elseif {[regexp {\.(txt|c|h)$} $path]} { + set mode text + } elseif {[regexp {\.(sit|sea|cpt|tar|gz|Z|gif|jpg)$} $path]} { + set mode raw + } elseif {[catch {exec file -L $path} type] == 0 && \ + [regexp {text} $type]} { + set mode text + } else { + set mode raw + } + } + + $vol copyin $mode $path $dest +} + +proc hformat {path {partno 0} {vname "Untitled"} {badblocks {}}} { + global mounts + + if {[info exists mounts($path)]} { + humount $path + } + + hfs format $path $partno $vname $badblocks + + hmount $path $partno +} + +############################################################################### + +proc help {{what ""}} { + if {[string length $what]} { + if {[catch {info args h$what} msg]} { + puts stdout "Sorry, no help for \"$what\"" + } else { + puts stdout "$what $msg" + } + } else { + puts stdout { +info Show current volume information +mount Open a new HFS volume +umount Close an HFS volume +vol Select an open volume +pwd Show the current working directory path +cd Change the current working directory +dir Show a directory listing +stat Show details about a given path +mkdir Create a new directory +rmdir Delete an empty directory +create Create an empty file +touch Update modification date or create a file +del Delete a file +rename Rename a file, directory, or volume +cat Display file's data fork contents +copyout Copy a complete file out into the native filesystem +copyin Copy a MacBinary/BinHex/text/raw file into HFS +format Create an empty HFS volume +} + } +} + +proc ? {args} { + eval help $args +} + +############################################################################### + +proc version {} { + puts stdout "[hfs version] - [hfs copyright]" +} + +proc license {} { + puts -nonewline stdout "\n[hfs license]" +} + +proc author {} { + puts stdout [hfs author] +} + +if {[string compare [lindex $argv 0] "--license"] == 0} { + license + exit +} + +version + +if {[string compare [lindex $argv 0] "--version"] == 0} { + puts stdout "`$argv0 --license' for licensing information." + exit +} + +puts stdout "This is free software but comes with ABSOLUTELY NO WARRANTY." +if {$hfs_interactive} { + puts stdout "Type `license' for details." +} +puts stdout "" + +############################################################################### + +proc echo {args} { + puts stdout [join $args " "] +} + +proc quit {} { + exit +} + +############################################################################### + +# Apparently some shells don't grok "$@" correctly +if {$argc == 1 && [string length [lindex $argv 0]] == 0} { + incr argc -1 + set argv [lreplace $argv 0 0] +} + +if {$argc > 0} { + eval hmount $argv +} + +while {1} { + if {$hfs_interactive} { + puts -nonewline stdout "hfs> " + flush stdout + } + + if {[gets stdin line] == -1} { + exit + } + + while {! [info complete $line]} { + if {[gets stdin more] == -1} { + break + } else { + set line "$line$more" + } + } + + if {[string length [info procs "h[lindex $line 0]"]] > 0} { + set result [catch {eval h$line} msg] + } else { + set result [catch {eval $line} msg] + } + + if {[string length $msg] > 0} { + if {$result == 1} { + puts stdout "Error: $msg" + if {! $hfs_interactive} { + exit 1 + } + } else { + puts stdout $msg + } + } +} diff --git a/hfsck/Makefile.in b/hfsck/Makefile.in new file mode 100644 index 0000000..45a4dd2 --- /dev/null +++ b/hfsck/Makefile.in @@ -0,0 +1,107 @@ +# +# hfsck - tool for checking and repairing the integrity of HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile.in,v 1.6 1998/04/11 23:40:39 rob Exp $ +# + +@SET_MAKE@ +src = @srcdir@ +VPATH = @srcdir@ + +### USER CUSTOMIZATIONS FOLLOW ################################################ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +MANDEST = @mandir@ + +INCDEST = @includedir@ +LIBDEST = @libdir@ + +INSTALL = @INSTALL@ +LIBINSTALL = @INSTALL_DATA@ + +CC = @CC@ +INCLUDES = @CPPFLAGS@ -I.. -I../libhfs +DEFINES = @DEFS@ +LIBOBJS = @LIBOBJS@ ../suid.o ../version.o +LIBS = @LIBS@ -L../libhfs -lhfs + +COPTS = @CFLAGS@ +LDOPTS = @LDFLAGS@ + +### END OF USER CUSTOMIZATION ################################################# + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(HFSCKTARGET) + +HFSCKTARGET = hfsck +HFSCKOBJS = ck_mdb.o ck_volume.o ck_btree.o hfsck.o util.o main.o \ + $(LIBOBJS) + +############################################################################### + +all :: $(TARGETS) + +again :: clean all + +depend :: + ( sed -n '1,/^### DEPEND/p' Makefile.in; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.in.new + mv -f Makefile.in.new Makefile.in + +clean :: + rm -f $(TARGETS) *.o gmon.* core + +############################################################################### + +$(HFSCKTARGET): $(HFSCKOBJS) + $(CC) $(LDFLAGS) $(HFSCKOBJS) $(LIBS) -o $@ + +### DEPENDENCIES FOLLOW ####################################################### + +ck_btree.o: ck_btree.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h util.h ck_btree.h +ck_mdb.o: ck_mdb.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h util.h ck_mdb.h +ck_volume.o: ck_volume.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h ck_volume.h +hfsck.o: hfsck.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h ck_mdb.h ck_volume.h ck_btree.h +main.o: main.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h ../suid.h ../version.h +util.o: util.c hfsck.h ../libhfs/libhfs.h ../libhfs/hfs.h \ + ../libhfs/apple.h ../libhfs/data.h ../libhfs/block.h ../libhfs/low.h \ + ../libhfs/file.h ../libhfs/btree.h ../libhfs/node.h \ + ../libhfs/record.h ../libhfs/volume.h util.h diff --git a/hfsck/ck_btree.c b/hfsck/ck_btree.c new file mode 100644 index 0000000..5d4affd --- /dev/null +++ b/hfsck/ck_btree.c @@ -0,0 +1,96 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_btree.c,v 1.7 1998/04/11 08:27:07 rob Exp $ + */ + +# include + +# include "hfsck.h" +# include "util.h" + +# include "ck_btree.h" + +/* + * NAME: ck->btree() + * DESCRIPTION: verify/repair a b*-tree + */ +int ck_btree(btree *bt) +{ + printf("*** Checking %s B*-tree\n", bt->f.name); + + if (bt_readhdr(bt) == -1) + return -1; + + if (VERBOSE) + { + printf(" bthDepth = %u\n", bt->hdr.bthDepth); + printf(" bthRoot = %lu\n", bt->hdr.bthRoot); + printf(" bthNRecs = %lu\n", bt->hdr.bthNRecs); + printf(" bthFNode = %lu\n", bt->hdr.bthFNode); + printf(" bthLNode = %lu\n", bt->hdr.bthLNode); + printf(" bthNodeSize = %u\n", bt->hdr.bthNodeSize); + printf(" bthKeyLen = %u\n", bt->hdr.bthKeyLen); + printf(" bthNNodes = %lu\n", bt->hdr.bthNNodes); + printf(" bthFree = %lu\n", bt->hdr.bthFree); + } + +# if 0 + if (VERBOSE) + { + node n; + int i; + + n.bt = bt; + n.nnum = bt->hdr.bthFNode; + + while (n.nnum) + { + if (bt_getnode(&n) == -1) + return -1; + + if (n.nnum == bt->hdr.bthFNode) + printf(" { "); + else + printf("} { "); + + for (i = 0; i < n.nd.ndNRecs; ++i) + { + byte *ptr; + + ptr = HFS_NODEREC(n, i); + + outhex(ptr, 1 + HFS_RECKEYLEN(ptr)); + printf(": "); + outhex(HFS_RECDATA(ptr), + HFS_NODEREC(n, i + 1) - HFS_RECDATA(ptr)); + printf("\n"); + + if (i < n.nd.ndNRecs - 1) + printf(" "); + } + + n.nnum = n.nd.ndFLink; + } + + printf("}\n"); + } +# endif + + return 0; +} diff --git a/hfsck/ck_btree.h b/hfsck/ck_btree.h new file mode 100644 index 0000000..c9ef8a2 --- /dev/null +++ b/hfsck/ck_btree.h @@ -0,0 +1,22 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_btree.h,v 1.6 1998/04/11 08:27:07 rob Exp $ + */ + +int ck_btree(btree *); diff --git a/hfsck/ck_mdb.c b/hfsck/ck_mdb.c new file mode 100644 index 0000000..3c7bacd --- /dev/null +++ b/hfsck/ck_mdb.c @@ -0,0 +1,234 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_mdb.c,v 1.8 1998/04/11 08:27:07 rob Exp $ + */ + +# include +# include +# include + +# include "hfsck.h" +# include "util.h" + +# include "ck_mdb.h" + +/* + * NAME: ck->mdb() + * DESCRIPTION: verify/repair the master directory block + */ +int ck_mdb(hfsvol *vol) +{ + MDB *mdb = &vol->mdb; + time_t now; + + printf("*** Checking volume MDB\n"); + + if (VERBOSE) + { + int flag; + + printf(" drSigWord = 0x%04x\n", mdb->drSigWord); + + printf(" drCrDate = %s\n", mctime(mdb->drCrDate)); + printf(" drLsMod = %s\n", mctime(mdb->drLsMod)); + + printf(" drAtrb ="); + flag = 0; + if (mdb->drAtrb & HFS_ATRB_BUSY) + { + printf("%s BUSY", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_HLOCKED) + { + printf("%s HLOCKED", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_UMOUNTED) + { + printf("%s UMOUNTED", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_BBSPARED) + { + printf("%s BBSPARED", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_BVINCONSIS) + { + printf("%s BVINCONSIS", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_COPYPROT) + { + printf("%s COPYPROT", flag ? " |" : ""); + flag = 1; + } + if (mdb->drAtrb & HFS_ATRB_SLOCKED) + { + printf("%s SLOCKED", flag ? " |" : ""); + flag = 1; + } + + if (flag == 0) + printf(" 0"); + + printf("\n"); + + printf(" drNmFls = %u\n", mdb->drNmFls); + printf(" drVBMSt = %u\n", mdb->drVBMSt); + printf(" drAllocPtr = %u\n", mdb->drAllocPtr); + + printf(" drNmAlBlks = %u\n", mdb->drNmAlBlks); + printf(" drAlBlkSiz = %lu\n", mdb->drAlBlkSiz); + printf(" drClpSiz = %lu\n", mdb->drClpSiz); + printf(" drAlBlSt = %u\n", mdb->drAlBlSt); + + printf(" drNxtCNID = %lu\n", mdb->drNxtCNID); + printf(" drFreeBks = %u\n", mdb->drFreeBks); + printf(" drVN = \"%s\"\n", mdb->drVN); + + printf(" drVolBkUp = %s\n", mctime(mdb->drVolBkUp)); + printf(" drVSeqNum = %u\n", mdb->drVSeqNum); + printf(" drWrCnt = %lu\n", mdb->drWrCnt); + + printf(" drXTClpSiz = %lu\n", mdb->drXTClpSiz); + printf(" drCTClpSiz = %lu\n", mdb->drCTClpSiz); + + printf(" drNmRtDirs = %u\n", mdb->drNmRtDirs); + printf(" drFilCnt = %lu\n", mdb->drFilCnt); + printf(" drDirCnt = %lu\n", mdb->drDirCnt); + + /* drFndrInfo */ + + printf(" drEmbedSigWord = 0x%04x\n", mdb->drEmbedSigWord); + printf(" drEmbedExtent = %s\n", extstr(&mdb->drEmbedExtent)); + + printf(" drXTFlSize = %lu\n", mdb->drXTFlSize); + printf(" drXTExtRec = %s\n", extrecstr(&mdb->drXTExtRec)); + + printf(" drCTFlSize = %lu\n", mdb->drCTFlSize); + printf(" drCTExtRec = %s\n", extrecstr(&mdb->drCTExtRec)); + } + + /* mdb->drSigWord */ + + if (mdb->drSigWord != HFS_SIGWORD && + ask("Bad volume signature (0x%04x); should be 0x%04x", + mdb->drSigWord, HFS_SIGWORD)) + { + mdb->drSigWord = HFS_SIGWORD; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* mdb->drCrDate */ + + time(&now); + + if (mdb->drCrDate == 0 && + ask("Volume creation date is unset")) + { + mdb->drCrDate = d_mtime(now); + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + if (d_ltime(mdb->drCrDate) > now && + ask("Volume creation date is in the future (%s)", mctime(mdb->drCrDate))) + { + mdb->drCrDate = d_mtime(now); + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* mdb->drLsMod */ + + if (mdb->drLsMod == 0 && + ask("Volume last modify date is unset")) + { + mdb->drLsMod = mdb->drCrDate; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + if (d_ltime(mdb->drLsMod) > now && + ask("Volume last modify date is in the future (%s)", + mctime(mdb->drLsMod))) + { + mdb->drLsMod = d_mtime(now); + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + if (mdb->drLsMod < mdb->drCrDate && + ask("Volume last modify date is before volume creation")) + { + mdb->drLsMod = mdb->drCrDate; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* mdb->drAtrb */ + /* mdb->drNmFls */ + + /* mdb->drVBMSt */ + + if (mdb->drVBMSt != 3 && + ask("Volume bitmap starts at unusual location (%u), not 3")) + { + mdb->drVBMSt = 3; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* mdb->drAllocPtr */ + /* ... */ + + /* initialize other important fields */ + + vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS; + + /* extents pseudo-file structs */ + + vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN; + vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize; + vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize; + + vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; + vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; + + vol->ext.f.cat.u.fil.filClpSize = vol->mdb.drXTClpSiz; + + memcpy(&vol->ext.f.cat.u.fil.filExtRec, + &vol->mdb.drXTExtRec, sizeof(ExtDataRec)); + + f_selectfork(&vol->ext.f, fkData); + + /* catalog pseudo-file structs */ + + vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN; + vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize; + vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize; + + vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; + vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; + + vol->cat.f.cat.u.fil.filClpSize = vol->mdb.drCTClpSiz; + + memcpy(&vol->cat.f.cat.u.fil.filExtRec, + &vol->mdb.drCTExtRec, sizeof(ExtDataRec)); + + f_selectfork(&vol->cat.f, fkData); + + return 0; +} diff --git a/hfsck/ck_mdb.h b/hfsck/ck_mdb.h new file mode 100644 index 0000000..55c9e06 --- /dev/null +++ b/hfsck/ck_mdb.h @@ -0,0 +1,22 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_mdb.h,v 1.6 1998/04/11 08:27:07 rob Exp $ + */ + +int ck_mdb(hfsvol *); diff --git a/hfsck/ck_volume.c b/hfsck/ck_volume.c new file mode 100644 index 0000000..76c72ad --- /dev/null +++ b/hfsck/ck_volume.c @@ -0,0 +1,37 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_volume.c,v 1.6 1998/04/11 08:27:08 rob Exp $ + */ + +# include + +# include "hfsck.h" + +# include "ck_volume.h" + +/* + * NAME: ck->volume() + * DESCRIPTION: verify/repair general volume structure + */ +int ck_volume(hfsvol *vol) +{ + printf("*** Checking volume structure\n"); + + return 0; +} diff --git a/hfsck/ck_volume.h b/hfsck/ck_volume.h new file mode 100644 index 0000000..e79eab9 --- /dev/null +++ b/hfsck/ck_volume.h @@ -0,0 +1,22 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: ck_volume.h,v 1.6 1998/04/11 08:27:08 rob Exp $ + */ + +int ck_volume(hfsvol *); diff --git a/hfsck/hfsck.c b/hfsck/hfsck.c new file mode 100644 index 0000000..7552548 --- /dev/null +++ b/hfsck/hfsck.c @@ -0,0 +1,34 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfsck.c,v 1.7 1998/04/11 08:27:08 rob Exp $ + */ + +# include + +# include "hfsck.h" + +# include "ck_mdb.h" +# include "ck_volume.h" +# include "ck_btree.h" + +int hfsck(hfsvol *vol) +{ + return ck_mdb(vol) || ck_volume(vol) || + ck_btree(&vol->ext) || ck_btree(&vol->cat); +} diff --git a/hfsck/hfsck.h b/hfsck/hfsck.h new file mode 100644 index 0000000..8565598 --- /dev/null +++ b/hfsck/hfsck.h @@ -0,0 +1,42 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfsck.h,v 1.6 1998/04/11 08:27:08 rob Exp $ + */ + +# include "libhfs.h" +# include "data.h" +# include "block.h" +# include "low.h" +# include "file.h" +# include "btree.h" +# include "node.h" +# include "record.h" +# include "volume.h" + +# define HFSCK_REPAIR 0x0001 +# define HFSCK_VERBOSE 0x0100 +# define HFSCK_YES 0x0200 + +# define REPAIR (options & HFSCK_REPAIR) +# define VERBOSE (options & HFSCK_VERBOSE) +# define YES (options & HFSCK_YES) + +extern int options; + +int hfsck(hfsvol *); diff --git a/hfsck/main.c b/hfsck/main.c new file mode 100644 index 0000000..b963610 --- /dev/null +++ b/hfsck/main.c @@ -0,0 +1,220 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: main.c,v 1.8 1998/11/02 22:08:50 rob Exp $ + */ + +# include +# include +# include +# include +# include +# include +# include +# include + +# include "hfsck.h" +# include "suid.h" +# include "version.h" + +int options; + +extern int optind; + +/* + * NAME: usage() + * DESCRIPTION: display usage message + */ +static +int usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s [-v] [-n] [-a] device-path [partition-no]\n", + argv[0]); + + return 1; +} + +/* + * NAME: main() + * DESCRIPTION: program entry + */ +int main(int argc, char *argv[]) +{ + char *path; + int nparts, pnum, result; + hfsvol vol; + + suid_init(); + + if (argc == 2) + { + if (strcmp(argv[1], "--version") == 0) + { + printf("%s - %s\n", hfsutils_version, hfsutils_copyright); + printf("`%s --license' for licensing information.\n", argv[0]); + return 0; + } + else if (strcmp(argv[1], "--license") == 0) + { + printf("\n%s", hfsutils_license); + return 0; + } + } + + options = HFSCK_REPAIR; + + while (1) + { + int opt; + + opt = getopt(argc, argv, "vna"); + if (opt == EOF) + break; + + switch (opt) + { + case '?': + return usage(argv); + + case 'v': + options |= HFSCK_VERBOSE; + break; + + case 'n': + options &= ~HFSCK_REPAIR; + break; + + case 'a': + options |= HFSCK_YES; + break; + } + } + + if (argc - optind < 1 || + argc - optind > 2) + return usage(argv); + + path = argv[optind]; + + suid_enable(); + nparts = hfs_nparts(path); + suid_disable(); + + if (nparts == 0) + { + fprintf(stderr, "%s: partitioned medium contains no HFS partitions\n", + argv[0]); + return 1; + } + + if (argc - optind == 2) + { + pnum = atoi(argv[optind + 1]); + + if (pnum < 0) + { + fprintf(stderr, "%s: invalid partition number\n", argv[0]); + return 1; + } + + if (nparts == -1 && pnum > 0) + { + fprintf(stderr, "%s: warning: ignoring partition number for" + " non-partitioned medium\n", argv[0]); + pnum = 0; + } + else if (nparts > 0 && pnum == 0) + { + fprintf(stderr, "%s: cannot specify whole medium" + " (has %d partition%s)\n", argv[0], nparts, + nparts == 1 ? "" : "s"); + return 1; + } + else if (nparts > 0 && pnum > nparts) + { + fprintf(stderr, "%s: invalid partition number (only %d available)\n", + argv[0], nparts); + return 1; + } + } + else + { + if (nparts > 1) + { + fprintf(stderr, "%s: must specify partition number (%d available)\n", + argv[0], nparts); + return 1; + } + else if (nparts == -1) + pnum = 0; + else + pnum = 1; + } + + v_init(&vol, HFS_OPT_NOCACHE); + + if (REPAIR) + { + suid_enable(); + result = v_open(&vol, path, HFS_MODE_RDWR); + suid_disable(); + + if (result == -1) + { + vol.flags |= HFS_VOL_READONLY; + + suid_enable(); + result = v_open(&vol, path, HFS_MODE_RDONLY); + suid_disable(); + } + } + + if (result == -1) + { + perror(path); + return 1; + } + + if (REPAIR && (vol.flags & HFS_VOL_READONLY)) + { + fprintf(stderr, "%s: warning: %s not writable; cannot repair\n", + argv[0], path); + + options &= ~HFSCK_REPAIR; + } + + if (v_geometry(&vol, pnum) == -1 || + l_getmdb(&vol, &vol.mdb, 0) == -1) + { + perror(path); + v_close(&vol); + return 1; + } + + result = hfsck(&vol); + + vol.flags |= HFS_VOL_MOUNTED; + + if (v_close(&vol) == -1) + { + perror("closing volume"); + return 1; + } + + return result; +} diff --git a/hfsck/util.c b/hfsck/util.c new file mode 100644 index 0000000..c6ead5d --- /dev/null +++ b/hfsck/util.c @@ -0,0 +1,179 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: util.c,v 1.7 1998/04/11 08:27:08 rob Exp $ + */ + +# include +# include +# include + +# include "hfsck.h" +# include "util.h" + +/* + * NAME: mctime() + * DESCRIPTION: convert Macintosh time to an ASCII string + */ +char *mctime(unsigned long secs) +{ + time_t date; + static char str[26]; + + if (secs == 0) + return "(Never)"; + + date = d_ltime(secs); + strcpy(str, ctime(&date)); + str[24] = 0; + + return str; +} + +/* + * NAME: extstr() + * DESCRIPTION: convert an extent descriptor into a printable string + */ +char *extstr(ExtDescriptor *ext) +{ + static char str[20]; + + switch (ext->xdrNumABlks) + { + case 0: + return "[]"; + + case 1: + sprintf(str, "1[%u]", ext->xdrStABN); + break; + + default: + sprintf(str, "%u[%u..%u]", ext->xdrNumABlks, + ext->xdrStABN, ext->xdrStABN + ext->xdrNumABlks - 1); + } + + return str; +} + +/* + * NAME: extrecstr() + * DESCRIPTION: convert an extent record into a printable string + */ +char *extrecstr(ExtDataRec *rec) +{ + static char str[60]; + ExtDescriptor *ext; + int i; + + str[0] = 0; + + for (i = 0, ext = &(*rec)[0]; i < 3; ++i, ++ext) + { + if (i > 0) + strcat(str, "+"); + + strcat(str, extstr(ext)); + } + + return str; +} + +/* + * NAME: outhex() + * DESCRIPTION: output a block of data in hex format + */ +void outhex(unsigned char *data, unsigned int len) +{ + int toggle = 0; + + while (len--) + printf("%02x%s", *data++, toggle++ & 0x01 ? " " : ""); +} + +/* + * NAME: ask() + * DESCRIPTION: answer question from user + */ +int ask(char *question, ...) +{ + int result = -1; + va_list args; + + va_start(args, question); + + vprintf(question, args); + + if (! REPAIR) + { + printf(".\n"); + result = 0; + } + else if (YES) + { + printf(": fixing.\n"); + result = 1; + } + else + { + while (1) + { + char answer[80]; + + if (feof(stdin)) + { + printf("...\n"); + result = 0; + break; + } + + printf(". Fix? "); + fflush(stdout); + + fgets(answer, sizeof(answer), stdin); + + if (feof(stdin)) + { + printf("\n"); + result = 0; + break; + } + + switch (answer[0]) + { + case 'y': + case 'Y': + result = 1; + break; + + case 'n': + case 'N': + result = 0; + break; + } + + if (result == -1) + vprintf(question, args); + else + break; + } + } + + va_end(args); + + return result; +} diff --git a/hfsck/util.h b/hfsck/util.h new file mode 100644 index 0000000..1a9d161 --- /dev/null +++ b/hfsck/util.h @@ -0,0 +1,26 @@ +/* + * hfsck - tool for checking and repairing the integrity of HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: util.h,v 1.6 1998/04/11 08:27:08 rob Exp $ + */ + +char *mctime(unsigned long); +char *extstr(ExtDescriptor *); +char *extrecstr(ExtDataRec *); +void outhex(unsigned char *, unsigned int); +int ask(char *, ...); diff --git a/hfssh.c b/hfssh.c new file mode 100644 index 0000000..ebf8427 --- /dev/null +++ b/hfssh.c @@ -0,0 +1,64 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfssh.c,v 1.8 1998/04/11 08:26:57 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# include + +# include "tclhfs.h" +# include "suid.h" + +/* + * NAME: Tcl->AppInit() + * DESCRIPTION: initialize interpreter + */ +int Tcl_AppInit(Tcl_Interp *interp) +{ + Tcl_SetVar(interp, "hfs_interactive", + isatty(STDIN_FILENO) ? "1" : "0", TCL_GLOBAL_ONLY); + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + + if (Hfs_Init(interp) == TCL_ERROR) + return TCL_ERROR; + + return TCL_OK; +} + +/* + * NAME: main() + * DESCRIPTION: program entry + */ +int main(int argc, char *argv[]) +{ + suid_init(); + + Tcl_Main(argc, argv, Tcl_AppInit); + + return 0; /* not reached */ +} diff --git a/hfsutil.c b/hfsutil.c new file mode 100644 index 0000000..7007604 --- /dev/null +++ b/hfsutil.c @@ -0,0 +1,401 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfsutil.c,v 1.9 1998/09/28 23:21:49 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include +# include +# include +# include +# include + +# ifdef HAVE_UNISTD_H +# include +# else +char *getcwd(char *buf, size_t size); +# endif + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "suid.h" +# include "glob.h" +# include "version.h" + +# include "hattrib.h" +# include "hcd.h" +# include "hcopy.h" +# include "hdel.h" +# include "hformat.h" +# include "hls.h" +# include "hmkdir.h" +# include "hmount.h" +# include "hpwd.h" +# include "hrename.h" +# include "hrmdir.h" +# include "humount.h" +# include "hvol.h" + +const char *argv0, *bargv0; + +/* + * NAME: main() + * DESCRIPTION: program entry dispatch + */ +int main(int argc, char *argv[]) +{ + int i, len; + const char *dot; + + struct { + const char *name; + int (*func)(int, char *[]); + } list[] = { + { "hattrib", hattrib_main }, + { "hcd", hcd_main }, + { "hcopy", hcopy_main }, + { "hdel", hdel_main }, + { "hdir", hls_main }, + { "hformat", hformat_main }, + { "hls", hls_main }, + { "hmkdir", hmkdir_main }, + { "hmount", hmount_main }, + { "hpwd", hpwd_main }, + { "hrename", hrename_main }, + { "hrmdir", hrmdir_main }, + { "humount", humount_main }, + { "hvol", hvol_main }, + { 0, 0 } + }; + + suid_init(); + + if (argc == 2) + { + if (strcmp(argv[1], "--version") == 0) + { + printf("%s - %s\n", hfsutils_version, hfsutils_copyright); + printf("`%s --license' for licensing information.\n", argv[0]); + return 0; + } + else if (strcmp(argv[1], "--license") == 0) + { + printf("\n%s", hfsutils_license); + return 0; + } + } + + argv0 = argv[0]; + + bargv0 = strrchr(argv0, '/'); + if (bargv0 == 0) + bargv0 = argv0; + else + ++bargv0; + + dot = strchr(bargv0, '.'); + len = dot ? dot - bargv0 : strlen(bargv0); + + for (i = 0; list[i].name; ++i) + { + if (strncmp(bargv0, list[i].name, len) == 0) + { + int result; + + bargv0 = list[i].name; + + if (hcwd_init() == -1) + { + perror("Failed to initialize HFS working directories"); + return 1; + } + + result = list[i].func(argc, argv); + + if (hcwd_finish() == -1) + { + perror("Failed to save working directory state"); + return 1; + } + + return result; + } + } + + fprintf(stderr, "%s: Unknown operation `%s'\n", argv0, bargv0); + return 1; +} + +/* + * NAME: hfsutil->perror() + * DESCRIPTION: output an HFS error + */ +void hfsutil_perror(const char *msg) +{ + const char *str = strerror(errno); + + if (hfs_error == 0) + fprintf(stderr, "%s: %s: %c%s\n", argv0, msg, tolower(*str), str + 1); + else + fprintf(stderr, "%s: %s: %s (%s)\n", argv0, msg, hfs_error, str); +} + +/* + * NAME: hfsutil->perrorp() + * DESCRIPTION: output an HFS error for a pathname + */ +void hfsutil_perrorp(const char *path) +{ + const char *str = strerror(errno); + + if (hfs_error == 0) + fprintf(stderr, "%s: \"%s\": %c%s\n", argv0, path, tolower(*str), str + 1); + else + fprintf(stderr, "%s: \"%s\": %s (%s)\n", argv0, path, hfs_error, str); +} + +/* + * NAME: hfsutil->remount() + * DESCRIPTION: mount a volume as though it were still mounted + */ +hfsvol *hfsutil_remount(mountent *ment, int flags) +{ + hfsvol *vol; + hfsvolent vent; + + if (ment == 0) + { + fprintf(stderr, "%s: No volume is current; use `hmount' or `hvol'\n", + argv0); + return 0; + } + + suid_enable(); + vol = hfs_mount(ment->path, ment->partno, flags); + suid_disable(); + + if (vol == 0) + { + hfsutil_perror(ment->path); + return 0; + } + + hfs_vstat(vol, &vent); + + if (strcmp(vent.name, ment->vname) != 0) + { + fprintf(stderr, "%s: Expected volume \"%s\" not found\n", + argv0, ment->vname); + fprintf(stderr, "%s: Replace media on %s or use `hmount'\n", + argv0, ment->path); + + hfs_umount(vol); + return 0; + } + + if (hfs_chdir(vol, ment->cwd) == -1) + { + fprintf(stderr, "%s: Current HFS directory \"%s%s:\" no longer exists\n", + argv0, ment->vname, ment->cwd); + } + + return vol; +} + +/* + * NAME: hfsutil->unmount() + * DESCRIPTION: unmount a volume + */ +void hfsutil_unmount(hfsvol *vol, int *result) +{ + if (hfs_umount(vol) == -1 && *result == 0) + { + hfsutil_perror("Error closing HFS volume"); + *result = 1; + } +} + +/* + * NAME: hfsutil->pinfo() + * DESCRIPTION: print information about a volume + */ +void hfsutil_pinfo(hfsvolent *ent) +{ + printf("Volume name is \"%s\"%s\n", ent->name, + (ent->flags & HFS_ISLOCKED) ? " (locked)" : ""); + printf("Volume was created on %s", ctime(&ent->crdate)); + printf("Volume was last modified on %s", ctime(&ent->mddate)); + printf("Volume has %lu bytes free\n", ent->freebytes); +} + +/* + * NAME: hfsutil->glob() + * DESCRIPTION: perform filename globbing + */ +char **hfsutil_glob(hfsvol *vol, int argc, char *argv[], + int *nelts, int *result) +{ + char **fargv; + + fargv = hfs_glob(vol, argc, argv, nelts); + if (fargv == 0 && *result == 0) + { + fprintf(stderr, "%s: globbing error\n", argv0); + *result = 1; + } + + return fargv; +} + +/* + * NAME: hfsutil->getcwd() + * DESCRIPTION: return full path to current directory (must be free()'d) + */ +char *hfsutil_getcwd(hfsvol *vol) +{ + char *path, name[HFS_MAX_FLEN + 1 + 1]; + long cwd; + int pathlen; + + path = malloc(1); + path[0] = 0; + pathlen = 0; + cwd = hfs_getcwd(vol); + + while (cwd != HFS_CNID_ROOTPAR) + { + char *new; + int namelen, i; + + if (hfs_dirinfo(vol, &cwd, name) == -1) + return 0; + + if (pathlen) + strcat(name, ":"); + + namelen = strlen(name); + + new = realloc(path, namelen + pathlen + 1); + if (new == 0) + { + free(path); + ERROR(ENOMEM, 0); + return 0; + } + + if (pathlen == 0) + new[0] = 0; + + path = new; + + /* push string down to make room for path prefix (memmove()-ish) */ + + i = pathlen + 1; + for (new = path + namelen + pathlen; i--; new--) + *new = *(new - namelen); + + memcpy(path, name, namelen); + + pathlen += namelen; + } + + return path; +} + +/* + * NAME: hfsutil->samepath() + * DESCRIPTION: return 1 iff paths refer to same object + */ +int hfsutil_samepath(const char *path1, const char *path2) +{ + struct stat sbuf1, sbuf2; + + return + stat(path1, &sbuf1) == 0 && + stat(path2, &sbuf2) == 0 && + sbuf1.st_dev == sbuf2.st_dev && + sbuf1.st_ino == sbuf2.st_ino; +} + +/* + * NAME: hfsutil->abspath() + * DESCRIPTION: make given UNIX path absolute (must be free()'d) + */ +char *hfsutil_abspath(const char *path) +{ + char *cwd, *buf; + size_t len; + + if (path[0] == '/') + return strdup(path); + + cwd = getenv("PWD"); + if (cwd && hfsutil_samepath(cwd, ".")) + { + buf = malloc(strlen(cwd) + 1 + strlen(path) + 1); + if (buf == 0) + return 0; + + strcpy(buf, cwd); + } + else + { + len = 32; + cwd = malloc(len); + if (cwd == 0) + return 0; + + while (getcwd(cwd, len) == 0) + { + if (errno != ERANGE) + { + free(cwd); + return 0; + } + + len <<= 1; + buf = realloc(cwd, len); + if (buf == 0) + { + free(cwd); + return 0; + } + + cwd = buf; + } + + buf = realloc(cwd, strlen(cwd) + 1 + strlen(path) + 1); + if (buf == 0) + { + free(cwd); + return 0; + } + } + + strcat(buf, "/"); + strcat(buf, path); + + return buf; +} diff --git a/hfsutil.h b/hfsutil.h new file mode 100644 index 0000000..2ad42f7 --- /dev/null +++ b/hfsutil.h @@ -0,0 +1,39 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfsutil.h,v 1.8 1998/04/11 08:26:57 rob Exp $ + */ + +extern int errno; + +# define ERROR(code, str) (hfs_error = (str), errno = (code)) + +extern const char *argv0, *bargv0; + +void hfsutil_perror(const char *); +void hfsutil_perrorp(const char *); + +hfsvol *hfsutil_remount(mountent *, int); +void hfsutil_unmount(hfsvol *, int *); + +void hfsutil_pinfo(hfsvolent *); +char **hfsutil_glob(hfsvol *, int, char *[], int *, int *); +char *hfsutil_getcwd(hfsvol *); + +int hfsutil_samepath(const char *, const char *); +char *hfsutil_abspath(const char *); diff --git a/hfswish.c b/hfswish.c new file mode 100644 index 0000000..e730ba2 --- /dev/null +++ b/hfswish.c @@ -0,0 +1,136 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfswish.c,v 1.9 1998/04/11 08:26:57 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include + +# include "tclhfs.h" +# include "xhfs.h" +# include "suid.h" +# include "images.h" + +static +char *argv0; + +# define DEFBITMAP(interp, name) \ + Tk_DefineBitmap(interp, Tk_GetUid(#name), \ + (char *) name##_bits, name##_width, name##_height) + +/* + * NAME: Tcl->AppInit() + * DESCRIPTION: initialize interpreter and execute main script + */ +int Tcl_AppInit(Tcl_Interp *interp) +{ + if (Tcl_Init(interp) == TCL_ERROR || + Tk_Init(interp) == TCL_ERROR || + Hfs_Init(interp) == TCL_ERROR) + return TCL_ERROR; + + /* initialize bitmaps */ + + if (DEFBITMAP(interp, macdaemon) == TCL_ERROR || + DEFBITMAP(interp, macdaemon_mask) == TCL_ERROR || + + DEFBITMAP(interp, stop) == TCL_ERROR || + DEFBITMAP(interp, caution) == TCL_ERROR || + DEFBITMAP(interp, note) == TCL_ERROR || + + DEFBITMAP(interp, floppy) == TCL_ERROR || + DEFBITMAP(interp, harddisk) == TCL_ERROR || + DEFBITMAP(interp, cdrom) == TCL_ERROR || + + DEFBITMAP(interp, floppy_mask) == TCL_ERROR || + DEFBITMAP(interp, harddisk_mask) == TCL_ERROR || + DEFBITMAP(interp, cdrom_mask) == TCL_ERROR || + + DEFBITMAP(interp, sm_floppy) == TCL_ERROR || + DEFBITMAP(interp, sm_harddisk) == TCL_ERROR || + DEFBITMAP(interp, sm_cdrom) == TCL_ERROR || + + DEFBITMAP(interp, folder) == TCL_ERROR || + DEFBITMAP(interp, document) == TCL_ERROR || + DEFBITMAP(interp, application) == TCL_ERROR || + + DEFBITMAP(interp, folder_mask) == TCL_ERROR || + DEFBITMAP(interp, document_mask) == TCL_ERROR || + DEFBITMAP(interp, application_mask) == TCL_ERROR || + + DEFBITMAP(interp, sm_folder) == TCL_ERROR || + DEFBITMAP(interp, sm_document) == TCL_ERROR || + DEFBITMAP(interp, sm_application) == TCL_ERROR || + + DEFBITMAP(interp, help) == TCL_ERROR || + DEFBITMAP(interp, padlock) == TCL_ERROR) + return TCL_ERROR; + + /* execute main script */ + + Tcl_SetVar(interp, "argv0", argv0, TCL_GLOBAL_ONLY); + + if (Tcl_Eval(interp, xhfs) == TCL_ERROR) + { + fprintf(stderr, "Error: %s\n", interp->result); + exit(1); + } + + return TCL_OK; +} + +/* + * NAME: main() + * DESCRIPTION: program entry + */ +int main(int argc, char *argv[]) +{ + int nargc; + char **nargv; + int i; + + suid_init(); + + argv0 = argv[0]; + + nargc = argc + 2; + nargv = malloc(sizeof(*nargv) * nargc); + if (nargv == 0) + { + perror(argv[0]); + return 1; + } + + nargv[0] = argv0; + nargv[1] = "/dev/null"; + nargv[2] = "--"; + + for (i = 3; i < nargc; ++i) + nargv[i] = argv[i - 2]; + + Tk_Main(nargc, nargv, Tcl_AppInit); + + return 0; /* not reached */ +} diff --git a/hls.c b/hls.c new file mode 100644 index 0000000..b14b39c --- /dev/null +++ b/hls.c @@ -0,0 +1,1029 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hls.c,v 1.8 1998/09/28 23:21:50 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# ifdef HAVE_TERMIOS_H +# include +# endif + +# ifdef HAVE_SYS_IOCTL_H +# include +# else +int ioctl(int, int, ...); +# endif + +# include +# include +# include +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "darray.h" +# include "dlist.h" +# include "dstring.h" +# include "hls.h" + +# define HLS_ALL_FILES 0x0001 +# define HLS_ESCAPE 0x0002 +# define HLS_QUOTE 0x0004 +# define HLS_QMARK_CTRL 0x0008 +# define HLS_IMMEDIATE_DIRS 0x0010 +# define HLS_CATIDS 0x0020 +# define HLS_REVERSE 0x0040 +# define HLS_SIZE 0x0080 +# define HLS_INDICATOR 0x0100 +# define HLS_RECURSIVE 0x0200 +# define HLS_NAME 0x0400 +# define HLS_SPACE 0x0800 + +# define F_MASK 0x0007 +# define F_LONG 0x0000 +# define F_ONE 0x0001 +# define F_MANY 0x0002 +# define F_HORIZ 0x0003 +# define F_COMMAS 0x0004 + +# define T_MASK 0x0008 +# define T_MOD 0x0000 +# define T_CREATE 0x0008 + +# define S_MASK 0x0030 +# define S_NAME 0x0000 +# define S_TIME 0x0010 +# define S_SIZE 0x0020 + +# define PATH(ent) ((ent).path ? (ent).path : (ent).dirent.name) + +typedef struct _queueent_ { + char *path; + hfsdirent dirent; + void (*free)(struct _queueent_ *); +} queueent; + +extern char *optarg; +extern int optind; + +/* + * NAME: usage() + * DESCRIPTION: display usage message + */ +static +int usage(void) +{ + fprintf(stderr, "Usage: %s [options] [hfs-path ...]\n", argv0); + + return 1; +} + +/* + * NAME: dpfree() + * DESCRIPTION: free a queue entry containing dynamically-allocated data + */ +static +void dpfree(queueent *ent) +{ + free(ent->path); +} + +/* + * NAME: qnew() + * DESCRIPTION: create a new queue array + */ +static +darray *qnew(void) +{ + return darr_new(sizeof(queueent)); +} + +/* + * NAME: qfree() + * DESCRIPTION: free a queue array + */ +static +void qfree(darray *array) +{ + int i, sz; + queueent *ents; + + sz = darr_size(array); + ents = darr_array(array); + + for (i = 0; i < sz; ++i) + if (ents[i].free) + ents[i].free(&ents[i]); + + darr_free(array); +} + +static +int reverse; + +/* + * NAME: compare_names() + * DESCRIPTION: lexicographically compare two filenames + */ +static +int compare_names(const queueent *ent1, const queueent *ent2) +{ + return reverse * strcasecmp(PATH(*ent1), PATH(*ent2)); +} + +/* + * NAME: compare_mtimes() + * DESCRIPTION: chronologically compare two modification dates + */ +static +int compare_mtimes(const queueent *ent1, const queueent *ent2) +{ + return reverse * (ent2->dirent.mddate - ent1->dirent.mddate); +} + +/* + * NAME: compare_ctimes() + * DESCRIPTION: chronologically compare two creation dates + */ +static +int compare_ctimes(const queueent *ent1, const queueent *ent2) +{ + return reverse * (ent2->dirent.crdate - ent1->dirent.crdate); +} + +/* + * NAME: compare_sizes() + * DESCRIPTION: compare two file sizes + */ +static +int compare_sizes(const queueent *ent1, const queueent *ent2) +{ + return reverse * + ((ent2->dirent.u.file.dsize + ent2->dirent.u.file.rsize) - + (ent1->dirent.u.file.dsize + ent1->dirent.u.file.rsize)); +} + +/* + * NAME: sortfiles() + * DESCRIPTION: arrange files in order according to sort selection + */ +static +void sortfiles(darray *files, int flags, int options) +{ + int (*compare)(const queueent *, const queueent *); + + switch (options & S_MASK) + { + case S_NAME: + compare = compare_names; + break; + + case S_TIME: + switch (options & T_MASK) + { + case T_MOD: + compare = compare_mtimes; + break; + + case T_CREATE: + compare = compare_ctimes; + break; + + default: + abort(); + } + break; + + case S_SIZE: + compare = compare_sizes; + break; + + default: + return; + } + + reverse = (flags & HLS_REVERSE) ? -1 : 1; + + darr_sort(files, (int (*)(const void *, const void *)) compare); +} + +/* + * NAME: outpath() + * DESCRIPTION: modulate an output string given current flags + */ +static +int outpath(dstring *str, queueent *ent, int flags) +{ + const char *path; + + path = PATH(*ent); + + dstr_shrink(str, 0); + + if ((flags & HLS_QUOTE) && + dstr_append(str, "\"", 1) == -1) + return -1; + + if (flags & (HLS_ESCAPE | HLS_QUOTE | HLS_QMARK_CTRL)) + { + const char *ptr; + + for (ptr = path; *ptr; ++ptr) + { + const char *add; + char buf[5]; + + if (flags & HLS_ESCAPE) + { + switch (*ptr) + { + case '\\': + add = "\\\\"; + break; + + case '\n': + add = "\\n"; + break; + + case '\b': + add = "\\b"; + break; + + case '\r': + add = "\\r"; + break; + + case '\t': + add = "\\t"; + break; + + case '\f': + add = "\\f"; + break; + + case ' ': + add = "\\ "; + break; + + case '\"': + add = "\\\""; + break; + + default: + if (isgraph(*ptr)) + { + sprintf(buf, "%c", *ptr); + add = buf; + } + else + { + sprintf(buf, "\\%03o", (unsigned char) *ptr); + add = buf; + } + } + } + else /* ! (flags & HLS_ESCAPE) */ + { + if (isprint(*ptr) || ! (flags & HLS_QMARK_CTRL)) + { + sprintf(buf, "%c", *ptr); + add = buf; + } + else + { + sprintf(buf, "?"); + add = buf; + } + } + + if (dstr_append(str, add, -1) == -1) + return -1; + } + } + else + { + if (dstr_append(str, path, -1) == -1) + return -1; + } + + if ((flags & HLS_QUOTE) && + dstr_append(str, "\"", 1) == -1) + return -1; + + if (flags & HLS_INDICATOR) + { + char c = 0; + + if (ent->dirent.flags & HFS_ISDIR) + c = ':'; + else if (strcmp(ent->dirent.u.file.type, "APPL") == 0) + c = '*'; + + if (c && dstr_append(str, &c, 1) == -1) + return -1; + } + + return 0; +} + +/* + * NAME: misclen() + * DESCRIPTION: string length of miscellaneous section + */ +static +int misclen(int flags) +{ + return ((flags & HLS_CATIDS) ? 8 : 0) + + ((flags & HLS_SIZE) ? 5 : 0); +} + +/* + * NAME: showmisc() + * DESCRIPTION: output miscellaneous numbers + */ +static +void showmisc(hfsdirent *ent, int flags) +{ + unsigned long size; + + size = ent->u.file.rsize + ent->u.file.dsize; + + if (flags & HLS_CATIDS) + printf("%7lu ", ent->cnid); + if (flags & HLS_SIZE) + printf("%4lu ", size / 1024 + (size % 1024 != 0)); +} + +/* + * NAME: show_long() + * DESCRIPTION: output a list of files in long format + */ +static +void show_long(int sz, queueent *ents, char **strs, + int flags, int options, int width) +{ + int i; + time_t now; + + now = time(0); + + for (i = 0; i < sz; ++i) + { + hfsdirent *ent; + time_t when; + char timebuf[26]; + + ent = &ents[i].dirent; + + switch (options & T_MASK) + { + case T_MOD: + when = ent->mddate; + break; + + case T_CREATE: + when = ent->crdate; + break; + + default: + abort(); + } + + strcpy(timebuf, ctime(&when)); + + if (now > when + 6L * 30L * 24L * 60L * 60L || + now < when - 60L * 60L) + strcpy(timebuf + 11, timebuf + 19); + + timebuf[16] = 0; + + showmisc(ent, flags); + + if (ent->flags & HFS_ISDIR) + printf("d%c %9u item%c %s %s\n", + ent->fdflags & HFS_FNDR_ISINVISIBLE ? 'i' : ' ', + ent->u.dir.valence, ent->u.dir.valence == 1 ? ' ' : 's', + timebuf + 4, strs[i]); + else + printf("%c%c %4s/%4s %9lu %9lu %s %s\n", + ent->flags & HFS_ISLOCKED ? 'F' : 'f', + ent->fdflags & HFS_FNDR_ISINVISIBLE ? 'i' : ' ', + ent->u.file.type, ent->u.file.creator, + ent->u.file.rsize, ent->u.file.dsize, + timebuf + 4, strs[i]); + } +} + +/* + * NAME: show_one() + * DESCRIPTION: output a list of files in single-column format + */ +static +void show_one(int sz, queueent *ents, char **strs, + int flags, int options, int width) +{ + int i; + + for (i = 0; i < sz; ++i) + { + showmisc(&ents[i].dirent, flags); + printf("%s\n", strs[i]); + } +} + +/* + * NAME: show_many() + * DESCRIPTION: output a list of files in vertical-column format + */ +static +void show_many(int sz, queueent *ents, char **strs, + int flags, int options, int width) +{ + int i, len, misc, maxlen = 0, rows, cols, row; + + misc = misclen(flags); + + for (i = 0; i < sz; ++i) + { + len = strlen(strs[i]) + misc; + if (len > maxlen) + maxlen = len; + } + + maxlen += 2; + + cols = width / maxlen; + if (cols == 0) + cols = 1; + + rows = sz / cols + (sz % cols != 0); + + for (row = 0; row < rows; ++row) + { + i = row; + + while (1) + { + showmisc(&ents[i].dirent, flags); + printf("%s", strs[i]); + + i += rows; + if (i >= sz) + break; + + for (len = strlen(strs[i - rows]) + misc; + len < maxlen; ++len) + putchar(' '); + } + + putchar('\n'); + } +} + +/* + * NAME: show_horiz() + * DESCRIPTION: output a list of files in horizontal-column format + */ +static +void show_horiz(int sz, queueent *ents, char **strs, + int flags, int options, int width) +{ + int i, len, misc, maxlen = 0, cols; + + misc = misclen(flags); + + for (i = 0; i < sz; ++i) + { + len = strlen(strs[i]) + misc; + if (len > maxlen) + maxlen = len; + } + + maxlen += 2; + + cols = width / maxlen; + if (cols == 0) + cols = 1; + + for (i = 0; i < sz; ++i) + { + if (i) + { + if (i % cols == 0) + putchar('\n'); + else + { + for (len = strlen(strs[i - 1]) + misc; + len < maxlen; ++len) + putchar(' '); + } + } + + showmisc(&ents[i].dirent, flags); + printf("%s", strs[i]); + } + + if (i) + putchar('\n'); +} + +/* + * NAME: show_commas() + * DESCRIPTION: output a list of files in comma-delimited format + */ +static +void show_commas(int sz, queueent *ents, char **strs, + int flags, int options, int width) +{ + int i, pos = 0; + + for (i = 0; i < sz; ++i) + { + hfsdirent *ent; + int len; + + ent = &ents[i].dirent; + len = strlen(strs[i]) + misclen(flags) + ((i < sz - 1) ? 2 : 0); + + if (pos && pos + len >= width) + { + putchar('\n'); + pos = 0; + } + + showmisc(ent, flags); + printf("%s", strs[i]); + + if (i < sz - 1) + { + putchar(','); + putchar(' '); + } + + pos += len; + } + + if (pos) + putchar('\n'); +} + +/* + * NAME: showfiles() + * DESCRIPTION: display a set of files + */ +static +int showfiles(darray *files, int flags, int options, int width) +{ + dlist list; + int i, sz, result = 0; + queueent *ents; + dstring str; + char **strs; + void (*show)(int, queueent *, char **, int, int, int); + + if (dl_init(&list) == -1) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + return -1; + } + + sz = darr_size(files); + ents = darr_array(files); + + dstr_init(&str); + + for (i = 0; i < sz; ++i) + { + if (outpath(&str, &ents[i], flags) == -1 || + dl_append(&list, dstr_string(&str)) == -1) + { + result = -1; + break; + } + } + + dstr_free(&str); + + strs = dl_array(&list); + + switch (options & F_MASK) + { + case F_LONG: + show = show_long; + break; + + case F_ONE: + show = show_one; + break; + + case F_MANY: + show = show_many; + break; + + case F_HORIZ: + show = show_horiz; + break; + + case F_COMMAS: + show = show_commas; + break; + + default: + abort(); + } + + show(sz, ents, strs, flags, options, width); + + dl_free(&list); + + return result; +} + +/* + * NAME: process() + * DESCRIPTION: sort and display results + */ +static +int process(hfsvol *vol, darray *dirs, darray *files, + int flags, int options, int width) +{ + int i, dsz, fsz; + queueent *ents; + int result = 0; + + dsz = darr_size(dirs); + fsz = darr_size(files); + + if (fsz) + { + sortfiles(files, flags, options); + if (showfiles(files, flags, options, width) == -1) + result = -1; + + flags |= HLS_NAME | HLS_SPACE; + } + else if (dsz > 1) + flags |= HLS_NAME; + + ents = darr_array(dirs); + + for (i = 0; i < dsz; ++i) + { + const char *path; + hfsdir *dir; + queueent ent; + + darr_shrink(files, 0); + + path = PATH(ents[i]); + dir = hfs_opendir(vol, path); + if (dir == 0) + { + hfsutil_perrorp(path); + result = -1; + continue; + } + + while (hfs_readdir(dir, &ent.dirent) != -1) + { + if ((ent.dirent.fdflags & HFS_FNDR_ISINVISIBLE) && + ! (flags & HLS_ALL_FILES)) + continue; + + ent.path = 0; + ent.free = 0; + + if (darr_append(files, &ent) == 0) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + result = -1; + break; + } + + if ((ent.dirent.flags & HFS_ISDIR) && (flags & HLS_RECURSIVE)) + { + dstring str; + + dstr_init(&str); + + if (strchr(path, ':') == 0 && + dstr_append(&str, ":", 1) == -1) + result = -1; + + if (dstr_append(&str, path, -1) == -1) + result = -1; + + if (path[strlen(path) - 1] != ':' && + dstr_append(&str, ":", 1) == -1) + result = -1; + + if (dstr_append(&str, ent.dirent.name, -1) == -1) + result = -1; + + ent.path = strdup(dstr_string(&str)); + if (ent.path) + ent.free = dpfree; + else + result = -1; + + dstr_free(&str); + + if (darr_append(dirs, &ent) == 0) + { + result = -1; + if (ent.path) + free(ent.path); + } + + if (result) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + break; + } + + dsz = darr_size(dirs); + ents = darr_array(dirs); + } + } + + hfs_closedir(dir); + + if (result) + break; + + if (flags & HLS_SPACE) + printf("\n"); + if (flags & HLS_NAME) + printf("%s%s", path, + path[strlen(path) - 1] == ':' ? "\n" : ":\n"); + + sortfiles(files, flags, options); + if (showfiles(files, flags, options, width) == -1) + result = -1; + + flags |= HLS_NAME | HLS_SPACE; + } + + return result; +} + +/* + * NAME: queuepath() + * DESCRIPTION: append a file or directory to the list to process + */ +static +int queuepath(hfsvol *vol, char *path, darray *dirs, darray *files, int flags) +{ + queueent ent; + darray *array; + + if (hfs_stat(vol, path, &ent.dirent) == -1) + { + hfsutil_perrorp(path); + return (errno == ENOENT) ? 0 : -1; + } + + ent.path = path; + ent.free = 0; + + array = ((ent.dirent.flags & HFS_ISDIR) && + ! (flags & HLS_IMMEDIATE_DIRS)) ? dirs : files; + + if (darr_append(array, &ent) == 0) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + return -1; + } + + return 0; +} + +/* + * NAME: hls->main() + * DESCRIPTION: implement hls command + */ +int hls_main(int argc, char *argv[]) +{ + hfsvol *vol; + int fargc, i; + char **fargv = 0; + int result = 0; + int flags, options, width; + char *ptr; + darray *dirs, *files; + + options = T_MOD | S_NAME; + + if (isatty(STDOUT_FILENO)) + { + options |= F_MANY; + flags = HLS_QMARK_CTRL; + } + else + { + options |= F_ONE; + flags = 0; + } + + if (strcmp(bargv0, "hdir") == 0) + options = (options & ~F_MASK) | F_LONG; + + ptr = getenv("COLUMNS"); + width = ptr ? atoi(ptr) : 80; + +# ifdef TIOCGWINSZ + { + struct winsize ws; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && + ws.ws_col != 0) + width = ws.ws_col; + } +# endif + + while (1) + { + int opt; + + opt = getopt(argc, argv, "1abcdfilmqrstxw:CFNQRSU"); + if (opt == EOF) + break; + + switch (opt) + { + case '?': + return usage(); + + case '1': + options = (options & ~F_MASK) | F_ONE; + break; + + case 'a': + flags |= HLS_ALL_FILES; + break; + + case 'b': + flags |= HLS_ESCAPE; + flags &= ~HLS_QMARK_CTRL; + break; + + case 'c': + options = (options & ~(T_MASK | S_MASK)) | T_CREATE | S_TIME; + break; + + case 'd': + flags |= HLS_IMMEDIATE_DIRS; + break; + + case 'f': + flags |= HLS_ALL_FILES; + flags &= ~HLS_SIZE; + options &= ~S_MASK; + if ((options & F_MASK) == F_LONG) + options = (options & ~F_MASK) | + (isatty(STDOUT_FILENO) ? F_MANY : F_ONE); + break; + + case 'i': + flags |= HLS_CATIDS; + break; + + case 'l': + options = (options & ~F_MASK) | F_LONG; + break; + + case 'm': + options = (options & ~F_MASK) | F_COMMAS; + break; + + case 'q': + flags |= HLS_QMARK_CTRL; + flags &= ~HLS_ESCAPE; + break; + + case 'r': + flags |= HLS_REVERSE; + break; + + case 's': + flags |= HLS_SIZE; + break; + + case 't': + options = (options & ~S_MASK) | S_TIME; + break; + + case 'x': + options = (options & ~F_MASK) | F_HORIZ; + break; + + case 'w': + width = atoi(optarg); + break; + + case 'C': + options = (options & ~F_MASK) | F_MANY; + break; + + case 'F': + flags |= HLS_INDICATOR; + break; + + case 'N': + flags &= ~(HLS_ESCAPE | HLS_QMARK_CTRL); + break; + + case 'Q': + flags |= HLS_QUOTE | HLS_ESCAPE; + flags &= ~HLS_QMARK_CTRL; + break; + + case 'R': + flags |= HLS_RECURSIVE; + break; + + case 'S': + options = (options & ~S_MASK) | S_SIZE; + break; + + case 'U': + options &= ~S_MASK; + break; + } + } + + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_RDONLY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - optind, &argv[optind], &fargc, &result); + + dirs = qnew(); + files = qnew(); + if (result == 0 && (dirs == 0 || files == 0)) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + result = 1; + } + + if (result == 0) + { + if (fargc == 0) + { + if (queuepath(vol, ":", dirs, files, flags) == -1) + result = 1; + } + else + { + for (i = 0; i < fargc; ++i) + { + if (queuepath(vol, fargv[i], dirs, files, flags) == -1) + { + result = 1; + break; + } + } + } + } + + if (result == 0 && process(vol, dirs, files, flags, options, width) == -1) + result = 1; + + if (files) + qfree(files); + if (dirs) + qfree(dirs); + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hls.h b/hls.h new file mode 100644 index 0000000..eae4920 --- /dev/null +++ b/hls.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hls.h,v 1.6 1998/04/11 08:26:58 rob Exp $ + */ + +int hls_main(int, char *[]); diff --git a/hmkdir.c b/hmkdir.c new file mode 100644 index 0000000..e8e00b7 --- /dev/null +++ b/hmkdir.c @@ -0,0 +1,74 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hmkdir.c,v 1.6 1998/04/11 08:26:58 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hmkdir.h" + +/* + * NAME: hmkdir->main() + * DESCRIPTION: implement hmkdir command + */ +int hmkdir_main(int argc, char *argv[]) +{ + hfsvol *vol; + char **fargv; + int fargc, i, result = 0; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s hfs-path [...]\n", argv0); + return 1; + } + + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - 1, &argv[1], &fargc, &result); + + if (result == 0) + { + for (i = 0; i < fargc; ++i) + { + if (hfs_mkdir(vol, fargv[i]) == -1) + { + hfsutil_perrorp(fargv[i]); + result = 1; + } + } + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hmkdir.h b/hmkdir.h new file mode 100644 index 0000000..2b30bb4 --- /dev/null +++ b/hmkdir.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hmkdir.h,v 1.6 1998/04/11 08:26:58 rob Exp $ + */ + +int hmkdir_main(int, char *[]); diff --git a/hmount.c b/hmount.c new file mode 100644 index 0000000..e338abd --- /dev/null +++ b/hmount.c @@ -0,0 +1,112 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hmount.c,v 1.8 1998/04/11 08:26:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "suid.h" +# include "hmount.h" + +/* + * NAME: hmount->main() + * DESCRIPTION: implement hmount command + */ +int hmount_main(int argc, char *argv[]) +{ + char *path = 0; + hfsvol *vol; + hfsvolent ent; + int nparts, partno, result = 0; + + if (argc < 2 || argc > 3) + { + fprintf(stderr, "Usage: %s source-path [partition-no]\n", argv0); + goto fail; + } + + path = hfsutil_abspath(argv[1]); + if (path == 0) + { + fprintf(stderr, "%s: not enough memory\n", argv0); + goto fail; + } + + suid_enable(); + nparts = hfs_nparts(path); + suid_disable(); + + if (nparts >= 0) + printf("%s: contains %d HFS partition%s\n", path, nparts, + nparts == 1 ? "" : "s"); + + if (argc == 3) + partno = atoi(argv[2]); + else + { + if (nparts > 1) + { + fprintf(stderr, "%s: must specify partition number\n", argv0); + goto fail; + } + else if (nparts == -1) + partno = 0; + else + partno = 1; + } + + suid_enable(); + vol = hfs_mount(path, partno, HFS_MODE_ANY); + suid_disable(); + + if (vol == 0) + { + hfsutil_perror(path); + goto fail; + } + + hfs_vstat(vol, &ent); + hfsutil_pinfo(&ent); + + if (hcwd_mounted(ent.name, ent.crdate, path, partno) == -1) + { + perror("Failed to record mount"); + result = 1; + } + + hfsutil_unmount(vol, &result); + + free(path); + + return result; + +fail: + if (path) + free(path); + + return 1; +} diff --git a/hmount.h b/hmount.h new file mode 100644 index 0000000..e25915d --- /dev/null +++ b/hmount.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hmount.h,v 1.7 1998/04/11 08:26:59 rob Exp $ + */ + +int hmount_main(int, char *[]); diff --git a/hpwd.c b/hpwd.c new file mode 100644 index 0000000..cd3b100 --- /dev/null +++ b/hpwd.c @@ -0,0 +1,61 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hpwd.c,v 1.7 1998/04/11 08:26:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hpwd.h" + +/* + * NAME: hpwd->main() + * DESCRIPTION: implement hpwd command + */ +int hpwd_main(int argc, char *argv[]) +{ + mountent *ent; + + if (argc != 1) + { + fprintf(stderr, "Usage: %s\n", argv0); + return 1; + } + + ent = hcwd_getvol(-1); + if (ent == 0) + { + fprintf(stderr, "%s: No volume is current; use `hmount' or `hvol'\n", + argv0); + return 1; + } + + if (strcmp(ent->cwd, ":") == 0) + printf("%s:\n", ent->vname); + else + printf("%s%s:\n", ent->vname, ent->cwd); + + return 0; +} diff --git a/hpwd.h b/hpwd.h new file mode 100644 index 0000000..273eeca --- /dev/null +++ b/hpwd.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hpwd.h,v 1.7 1998/04/11 08:26:59 rob Exp $ + */ + +int hpwd_main(int, char *[]); diff --git a/hrename.c b/hrename.c new file mode 100644 index 0000000..9b0103a --- /dev/null +++ b/hrename.c @@ -0,0 +1,119 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hrename.c,v 1.7 1998/04/11 08:26:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hrename.h" + +/* + * NAME: do_rename() + * DESCRIPTION: move/rename files + */ +static +int do_rename(hfsvol *vol, int argc, char *argv[], const char *dest) +{ + hfsdirent ent; + int i, result = 0; + + if (argc > 1 && (hfs_stat(vol, dest, &ent) == -1 || + ! (ent.flags & HFS_ISDIR))) + { + ERROR(ENOTDIR, 0); + hfsutil_perrorp(dest); + + return 1; + } + + for (i = 0; i < argc; ++i) + { + if (hfs_rename(vol, argv[i], dest) == -1) + { + hfsutil_perrorp(argv[i]); + result = 1; + } + } + + return result; +} + +/* + * NAME: hrename->main() + * DESCRIPTION: implement hrename command + */ +int hrename_main(int argc, char *argv[]) +{ + mountent *ment; + hfsvol *vol; + int fargc; + char **fargv; + int result = 0; + + if (argc < 3) + { + fprintf(stderr, "Usage: %s hfs-src-path [...] hfs-target-path\n", argv0); + return 1; + } + + vol = hfsutil_remount(ment = hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - 2, &argv[1], &fargc, &result); + + if (result == 0) + result = do_rename(vol, fargc, fargv, argv[argc - 1]); + + if (result == 0) + { + char *path; + + path = hfsutil_getcwd(vol); + if (path == 0) + { + hfsutil_perror("Can't get current HFS directory path"); + result = 1; + } + else if (hcwd_setcwd(ment, path) == -1) + { + perror("Can't set current HFS directory"); + result = 1; + } + + if (path) + free(path); + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hrename.h b/hrename.h new file mode 100644 index 0000000..98f3333 --- /dev/null +++ b/hrename.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hrename.h,v 1.6 1998/04/11 08:26:59 rob Exp $ + */ + +int hrename_main(int, char *[]); diff --git a/hrmdir.c b/hrmdir.c new file mode 100644 index 0000000..10c33e3 --- /dev/null +++ b/hrmdir.c @@ -0,0 +1,74 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hrmdir.c,v 1.6 1998/04/11 08:26:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hrmdir.h" + +/* + * NAME: hrmdir->main() + * DESCRIPTION: implement hrmdir command + */ +int hrmdir_main(int argc, char *argv[]) +{ + hfsvol *vol; + char **fargv; + int fargc, i, result = 0; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s hfs-path [...]\n", argv0); + return 1; + } + + vol = hfsutil_remount(hcwd_getvol(-1), HFS_MODE_ANY); + if (vol == 0) + return 1; + + fargv = hfsutil_glob(vol, argc - 1, &argv[1], &fargc, &result); + + if (result == 0) + { + for (i = 0; i < fargc; ++i) + { + if (hfs_rmdir(vol, fargv[i]) == -1) + { + hfsutil_perrorp(fargv[i]); + result = 1; + } + } + } + + hfsutil_unmount(vol, &result); + + if (fargv) + free(fargv); + + return result; +} diff --git a/hrmdir.h b/hrmdir.h new file mode 100644 index 0000000..4eee1ac --- /dev/null +++ b/hrmdir.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hrmdir.h,v 1.6 1998/04/11 08:26:59 rob Exp $ + */ + +int hrmdir_main(int, char *[]); diff --git a/humount.c b/humount.c new file mode 100644 index 0000000..55a9618 --- /dev/null +++ b/humount.c @@ -0,0 +1,74 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: humount.c,v 1.6 1998/04/11 08:26:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "humount.h" + +/* + * NAME: humount->main() + * DESCRIPTION: implement humount command + */ +int humount_main(int argc, char *argv[]) +{ + int vnum; + mountent *ent; + + if (argc > 2) + { + fprintf(stderr, "Usage: %s [volume-name-or-path]\n", argv0); + return 1; + } + + if (argc == 1) + { + if (hcwd_umounted(-1) == -1 && + hcwd_getvol(-1) == 0) + { + fprintf(stderr, "%s: No volume is current\n", argv0); + return 1; + } + + return 0; + } + + for (ent = hcwd_getvol(vnum = 0); ent; ent = hcwd_getvol(++vnum)) + { + if (hfsutil_samepath(argv[1], ent->path) || + strcasecmp(argv[1], ent->vname) == 0) + { + hcwd_umounted(vnum); + return 0; + } + } + + fprintf(stderr, "%s: Unknown volume \"%s\"\n", argv0, argv[1]); + + return 1; +} diff --git a/humount.h b/humount.h new file mode 100644 index 0000000..9174c19 --- /dev/null +++ b/humount.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: humount.h,v 1.6 1998/04/11 08:26:59 rob Exp $ + */ + +int humount_main(int, char *[]); diff --git a/hvol.c b/hvol.c new file mode 100644 index 0000000..ca17be5 --- /dev/null +++ b/hvol.c @@ -0,0 +1,134 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hvol.c,v 1.7 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "hfs.h" +# include "hcwd.h" +# include "hfsutil.h" +# include "hvol.h" + +/* + * NAME: showvol() + * DESCRIPTION: output information about a mounted volume + */ +static +int showvol(mountent *ment) +{ + hfsvol *vol; + hfsvolent vent; + int result = 0; + + printf("Current volume is mounted from"); + if (ment->partno > 0) + printf(" partition %d of", ment->partno); + printf(":\n %s\n", ment->path); + + vol = hfsutil_remount(ment, HFS_MODE_ANY); + if (vol == 0) + return 1; + + printf("\n"); + + hfs_vstat(vol, &vent); + hfsutil_pinfo(&vent); + hfsutil_unmount(vol, &result); + + return result; +} + +/* + * NAME: hvol->main() + * DESCRIPTION: implement hvol command + */ +int hvol_main(int argc, char *argv[]) +{ + int vnum; + mountent *ment; + + if (argc > 2) + { + fprintf(stderr, "Usage: %s [volume-name-or-path]\n", argv0); + return 1; + } + + if (argc == 1) + { + int output = 0, header = 0; + + ment = hcwd_getvol(-1); + if (ment) + { + showvol(ment); + output = 1; + } + + for (vnum = 0; ; ++vnum) + { + mountent *ent; + + ent = hcwd_getvol(vnum); + if (ent == 0) + break; + + if (ent == ment) + continue; + + if (header == 0) + { + printf("%s volumes:\n", ment ? "\nOther known" : "Known"); + header = 1; + } + + if (ent->partno <= 0) + printf(" %-35s \"%s\"\n", ent->path, ent->vname); + else + printf(" %-35s %2d \"%s\"\n", ent->path, ent->partno, + ent->vname); + + output = 1; + } + + if (output == 0) + printf("No known volumes; use `hmount' to introduce new volumes\n"); + + return 0; + } + + for (ment = hcwd_getvol(vnum = 0); ment; ment = hcwd_getvol(++vnum)) + { + if (hfsutil_samepath(argv[1], ment->path) || + strcasecmp(argv[1], ment->vname) == 0) + { + hcwd_setvol(vnum); + return showvol(ment); + } + } + + fprintf(stderr, "%s: Unknown volume \"%s\"\n", argv0, argv[1]); + + return 1; +} diff --git a/hvol.h b/hvol.h new file mode 100644 index 0000000..3d51d55 --- /dev/null +++ b/hvol.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hvol.h,v 1.7 1998/04/11 08:27:00 rob Exp $ + */ + +int hvol_main(int, char *[]); diff --git a/images.h b/images.h new file mode 100644 index 0000000..8009d28 --- /dev/null +++ b/images.h @@ -0,0 +1,54 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: images.h,v 1.6 1998/04/11 08:27:00 rob Exp $ + */ + +# include "images/macdaemon.xbm" +# include "images/macdaemon_mask.xbm" + +# include "images/stop.xbm" +# include "images/caution.xbm" +# include "images/note.xbm" + +# include "images/floppy.xbm" +# include "images/harddisk.xbm" +# include "images/cdrom.xbm" + +# include "images/floppy_mask.xbm" +# include "images/harddisk_mask.xbm" +# include "images/cdrom_mask.xbm" + +# include "images/sm_floppy.xbm" +# include "images/sm_harddisk.xbm" +# include "images/sm_cdrom.xbm" + +# include "images/folder.xbm" +# include "images/document.xbm" +# include "images/application.xbm" + +# include "images/folder_mask.xbm" +# include "images/document_mask.xbm" +# include "images/application_mask.xbm" + +# include "images/sm_folder.xbm" +# include "images/sm_document.xbm" +# include "images/sm_application.xbm" + +# include "images/help.xbm" +# include "images/padlock.xbm" diff --git a/images/application.xbm b/images/application.xbm new file mode 100644 index 0000000..6912bbe --- /dev/null +++ b/images/application.xbm @@ -0,0 +1,14 @@ +#define application_width 32 +#define application_height 32 +static unsigned char application_bits[] = { + 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x20, 0x02, 0x00, + 0x00, 0x10, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x10, 0x00, + 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x40, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x04, + 0x08, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x00, 0xfc, 0x20, + 0x01, 0x00, 0x02, 0x41, 0x02, 0x00, 0x01, 0x82, 0x04, 0x80, 0x0c, 0x44, + 0x08, 0x80, 0x13, 0x28, 0x10, 0x70, 0xfe, 0xf1, 0x20, 0x40, 0x0c, 0xe0, + 0x40, 0x80, 0x00, 0xe0, 0x80, 0x00, 0x01, 0xe0, 0x00, 0x01, 0x06, 0xe0, + 0x00, 0x02, 0xf8, 0xe7, 0x00, 0x04, 0x40, 0xf8, 0x00, 0x08, 0x20, 0xe0, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x40, 0x04, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00}; diff --git a/images/application_mask.xbm b/images/application_mask.xbm new file mode 100644 index 0000000..7c76180 --- /dev/null +++ b/images/application_mask.xbm @@ -0,0 +1,14 @@ +#define application_mask_width 32 +#define application_mask_height 32 +static unsigned char application_mask_bits[] = { + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xfc, 0x1f, 0x00, + 0x00, 0xfe, 0x3f, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x80, 0xff, 0xff, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x03, 0xf0, 0xff, 0xff, 0x07, + 0xf8, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x3f, + 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x7f, + 0xf8, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, + 0xc0, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, + 0x00, 0xfe, 0xff, 0xff, 0x00, 0xfc, 0x7f, 0xf8, 0x00, 0xf8, 0x3f, 0xe0, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x00, + 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00}; diff --git a/images/caution.xbm b/images/caution.xbm new file mode 100644 index 0000000..76f29ac --- /dev/null +++ b/images/caution.xbm @@ -0,0 +1,14 @@ +#define caution_width 32 +#define caution_height 32 +static unsigned char caution_bits[] = { + 0x00, 0x80, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, + 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0c, 0x00, + 0x00, 0x30, 0x0c, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x98, 0x19, 0x00, + 0x00, 0xcc, 0x33, 0x00, 0x00, 0xcc, 0x33, 0x00, 0x00, 0xc6, 0x63, 0x00, + 0x00, 0xc6, 0x63, 0x00, 0x00, 0xc3, 0xc3, 0x00, 0x00, 0xc3, 0xc3, 0x00, + 0x80, 0xc1, 0x83, 0x01, 0x80, 0xc1, 0x83, 0x01, 0xc0, 0xc0, 0x03, 0x03, + 0xc0, 0xc0, 0x03, 0x03, 0x60, 0xc0, 0x03, 0x06, 0x60, 0x80, 0x01, 0x06, + 0x30, 0x80, 0x01, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x18, + 0x18, 0x80, 0x01, 0x18, 0x0c, 0xc0, 0x03, 0x30, 0x0c, 0xc0, 0x03, 0x30, + 0x06, 0x80, 0x01, 0x60, 0x06, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x7f}; diff --git a/images/cdrom.xbm b/images/cdrom.xbm new file mode 100644 index 0000000..7f20700 --- /dev/null +++ b/images/cdrom.xbm @@ -0,0 +1,14 @@ +#define cdrom_width 31 +#define cdrom_height 31 +static unsigned char cdrom_bits[] = { + 0x00, 0xf0, 0x07, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x80, 0x01, 0xc0, 0x00, + 0x40, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x02, 0xb0, 0x00, 0x00, 0x04, + 0x78, 0x01, 0x00, 0x08, 0xf4, 0x01, 0x00, 0x10, 0xc4, 0x03, 0x00, 0x10, + 0x9a, 0x07, 0x00, 0x20, 0x62, 0x0f, 0x00, 0x20, 0x82, 0x1d, 0x00, 0x20, + 0x01, 0xfe, 0x03, 0x40, 0x01, 0xd8, 0x05, 0x40, 0x01, 0x30, 0x06, 0x40, + 0x01, 0x30, 0x06, 0x40, 0x01, 0x30, 0x06, 0x40, 0x01, 0xd0, 0x0d, 0x40, + 0x01, 0xe0, 0x3f, 0x40, 0x02, 0x00, 0xfc, 0x20, 0x02, 0x00, 0x78, 0x23, + 0x02, 0x00, 0xf0, 0x2c, 0x04, 0x00, 0xe0, 0x13, 0x04, 0x00, 0xc0, 0x17, + 0x08, 0x00, 0x40, 0x0f, 0x10, 0x00, 0x80, 0x06, 0x20, 0x00, 0x00, 0x03, + 0x40, 0x00, 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x0e, 0x38, 0x00, + 0x00, 0xf0, 0x07, 0x00}; diff --git a/images/cdrom_mask.xbm b/images/cdrom_mask.xbm new file mode 100644 index 0000000..9745b3b --- /dev/null +++ b/images/cdrom_mask.xbm @@ -0,0 +1,14 @@ +#define cdrom_mask_width 31 +#define cdrom_mask_height 31 +static unsigned char cdrom_mask_bits[] = { + 0x00, 0xf0, 0x07, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x80, 0xff, 0xff, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x03, 0xf0, 0xff, 0xff, 0x07, + 0xf8, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x1f, + 0xfe, 0xff, 0xff, 0x3f, 0xfe, 0xff, 0xff, 0x3f, 0xfe, 0xff, 0xff, 0x3f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x3f, 0xfe, 0xff, 0xff, 0x3f, + 0xfe, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x1f, + 0xf8, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x03, + 0xc0, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, 0x00, 0x00, 0xfe, 0x3f, 0x00, + 0x00, 0xf0, 0x07, 0x00}; diff --git a/images/document.xbm b/images/document.xbm new file mode 100644 index 0000000..9a72cd6 --- /dev/null +++ b/images/document.xbm @@ -0,0 +1,14 @@ +#define document_width 25 +#define document_height 32 +static unsigned char document_bits[] = { + 0xff, 0xff, 0x07, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x01, 0x00, 0x24, 0x00, 0x01, 0x00, 0x44, 0x00, 0x01, 0x00, 0x84, 0x00, + 0x01, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x01}; diff --git a/images/document_mask.xbm b/images/document_mask.xbm new file mode 100644 index 0000000..675b1b1 --- /dev/null +++ b/images/document_mask.xbm @@ -0,0 +1,14 @@ +#define document_mask_width 25 +#define document_mask_height 32 +static unsigned char document_mask_bits[] = { + 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0x1f, 0x00, + 0xff, 0xff, 0x3f, 0x00, 0xff, 0xff, 0x7f, 0x00, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x01}; diff --git a/images/floppy.xbm b/images/floppy.xbm new file mode 100644 index 0000000..ea487de --- /dev/null +++ b/images/floppy.xbm @@ -0,0 +1,14 @@ +#define floppy_width 32 +#define floppy_height 32 +static unsigned char floppy_bits[] = { + 0xfe, 0xff, 0xff, 0x0f, 0x81, 0x00, 0x80, 0x10, 0x81, 0x00, 0x8e, 0x20, + 0x81, 0x00, 0x91, 0x40, 0x81, 0x00, 0x91, 0x80, 0x81, 0x00, 0x91, 0x80, + 0x81, 0x00, 0x91, 0x80, 0x81, 0x00, 0x91, 0x80, 0x81, 0x00, 0x8e, 0x80, + 0x81, 0x00, 0x80, 0x80, 0x01, 0xff, 0x7f, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xe1, 0xff, 0xff, 0x87, + 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, + 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, + 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, + 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, + 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x88, + 0x11, 0x00, 0x00, 0x88, 0xfe, 0xff, 0xff, 0x7f}; diff --git a/images/floppy_mask.xbm b/images/floppy_mask.xbm new file mode 100644 index 0000000..16f8584 --- /dev/null +++ b/images/floppy_mask.xbm @@ -0,0 +1,14 @@ +#define floppy_mask_width 32 +#define floppy_mask_height 32 +static unsigned char floppy_mask_bits[] = { + 0xfe, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x3f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x7f}; diff --git a/images/folder.xbm b/images/folder.xbm new file mode 100644 index 0000000..bd15c2f --- /dev/null +++ b/images/folder.xbm @@ -0,0 +1,12 @@ +#define folder_width 31 +#define folder_height 25 +static unsigned char folder_bits[] = { + 0xe0, 0x0f, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, + 0x04, 0x40, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, + 0xff, 0xff, 0xff, 0x7f}; diff --git a/images/folder_mask.xbm b/images/folder_mask.xbm new file mode 100644 index 0000000..c6569cf --- /dev/null +++ b/images/folder_mask.xbm @@ -0,0 +1,12 @@ +#define folder_mask_width 31 +#define folder_mask_height 25 +static unsigned char folder_mask_bits[] = { + 0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, + 0xfc, 0x7f, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, + 0xff, 0xff, 0xff, 0x7f}; diff --git a/images/harddisk.xbm b/images/harddisk.xbm new file mode 100644 index 0000000..b5ab970 --- /dev/null +++ b/images/harddisk.xbm @@ -0,0 +1,7 @@ +#define harddisk_width 32 +#define harddisk_height 11 +static unsigned char harddisk_bits[] = { + 0xfe, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xfe, 0xff, 0xff, 0x7f}; diff --git a/images/harddisk_mask.xbm b/images/harddisk_mask.xbm new file mode 100644 index 0000000..a7666cd --- /dev/null +++ b/images/harddisk_mask.xbm @@ -0,0 +1,7 @@ +#define harddisk_mask_width 32 +#define harddisk_mask_height 11 +static unsigned char harddisk_mask_bits[] = { + 0xfe, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x7f}; diff --git a/images/help.xbm b/images/help.xbm new file mode 100644 index 0000000..8ec64b8 --- /dev/null +++ b/images/help.xbm @@ -0,0 +1,6 @@ +#define help_width 16 +#define help_height 16 +static unsigned char help_bits[] = { + 0xfc, 0x3f, 0x02, 0x40, 0xc1, 0x83, 0x61, 0x86, 0x61, 0x86, 0x01, 0x83, + 0x81, 0x81, 0x81, 0x81, 0x01, 0x80, 0x81, 0x81, 0x81, 0x81, 0x02, 0x40, + 0xe4, 0x3f, 0x14, 0x00, 0x0c, 0x00, 0x04, 0x00}; diff --git a/images/macdaemon.xbm b/images/macdaemon.xbm new file mode 100644 index 0000000..b246f54 --- /dev/null +++ b/images/macdaemon.xbm @@ -0,0 +1,16 @@ +#define macdaemon_width 24 +#define macdaemon_height 51 +static unsigned char macdaemon_bits[] = { + 0x10, 0x00, 0x04, 0x18, 0x00, 0x0c, 0x14, 0x00, 0x14, 0x14, 0x00, 0x14, + 0x12, 0x00, 0x24, 0x12, 0x3e, 0x24, 0xa2, 0xc1, 0x22, 0x42, 0x00, 0x21, + 0x84, 0xb6, 0x10, 0x08, 0x08, 0x08, 0x14, 0x00, 0x14, 0x94, 0xe3, 0x14, + 0x42, 0x14, 0x21, 0x22, 0x08, 0x22, 0x22, 0x49, 0x22, 0xa2, 0xaa, 0x22, + 0xa2, 0xeb, 0x22, 0xa2, 0xeb, 0x22, 0x24, 0x49, 0x12, 0x44, 0x14, 0x11, + 0xfe, 0xff, 0x7f, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0xf1, 0xff, 0x8f, + 0x09, 0x00, 0x90, 0xa9, 0xaa, 0x90, 0x09, 0x00, 0x90, 0xa9, 0x0a, 0x90, + 0x09, 0x00, 0x90, 0xa9, 0x00, 0x90, 0x09, 0x00, 0x90, 0xa9, 0x00, 0x90, + 0x09, 0x00, 0x90, 0xa9, 0x02, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x90, + 0xf1, 0xff, 0x8f, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, + 0x01, 0x00, 0x80, 0x01, 0xf0, 0x8f, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, + 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0xfe, 0xff, 0x7f, 0x02, 0x00, 0x40, + 0x02, 0x00, 0x40, 0x02, 0x00, 0x40, 0xfe, 0xff, 0x7f}; diff --git a/images/macdaemon_mask.xbm b/images/macdaemon_mask.xbm new file mode 100644 index 0000000..2ccfefc --- /dev/null +++ b/images/macdaemon_mask.xbm @@ -0,0 +1,16 @@ +#define macdaemon_mask_width 24 +#define macdaemon_mask_height 51 +static unsigned char macdaemon_mask_bits[] = { + 0x10, 0x00, 0x04, 0x18, 0x00, 0x0c, 0x1c, 0x00, 0x1c, 0x1c, 0x00, 0x1c, + 0x1e, 0x00, 0x3c, 0x1e, 0x3e, 0x3c, 0xbe, 0xff, 0x3e, 0xfe, 0xff, 0x3f, + 0xfc, 0xff, 0x1f, 0xf8, 0xff, 0x0f, 0xfc, 0xff, 0x1f, 0xfc, 0xff, 0x1f, + 0xfe, 0xff, 0x3f, 0xfe, 0xff, 0x3f, 0xfe, 0xff, 0x3f, 0xfe, 0xff, 0x3f, + 0xfe, 0xff, 0x3f, 0xfe, 0xff, 0x3f, 0xfc, 0xff, 0x1f, 0xfc, 0xff, 0x1f, + 0xfe, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, + 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f}; diff --git a/images/note.xbm b/images/note.xbm new file mode 100644 index 0000000..6ca6bf9 --- /dev/null +++ b/images/note.xbm @@ -0,0 +1,14 @@ +#define note_width 32 +#define note_height 32 +static unsigned char note_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x01, 0xfe, 0xff, 0xff, 0x01, 0xfe, 0xff, 0xff, + 0x01, 0xfe, 0xff, 0xff, 0x01, 0xfe, 0xff, 0xff, 0x01, 0xfe, 0x03, 0xff, + 0x11, 0xfe, 0x00, 0xfc, 0x11, 0x7e, 0x00, 0xf8, 0x11, 0x3e, 0x00, 0xf0, + 0x01, 0x1e, 0x00, 0xe0, 0x01, 0x1e, 0x00, 0xe0, 0x01, 0x0e, 0x00, 0xc0, + 0x01, 0x8e, 0xbb, 0xc3, 0x01, 0x0e, 0x00, 0xc0, 0x01, 0x0e, 0x00, 0xc0, + 0x01, 0x8e, 0xbb, 0xc2, 0x01, 0x0e, 0x00, 0xc0, 0x01, 0x0e, 0x00, 0xc0, + 0x01, 0x8e, 0xeb, 0xc0, 0x01, 0x0e, 0x00, 0xc0, 0xe1, 0x0f, 0x00, 0xc0, + 0x81, 0x8f, 0x77, 0xc3, 0x81, 0x0f, 0x00, 0xe0, 0x81, 0x0f, 0x00, 0xe0, + 0x81, 0x0f, 0x00, 0xf0, 0x81, 0x07, 0x00, 0xf8, 0xf1, 0x01, 0x00, 0xfe, + 0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, + 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; diff --git a/images/padlock.xbm b/images/padlock.xbm new file mode 100644 index 0000000..ac340b6 --- /dev/null +++ b/images/padlock.xbm @@ -0,0 +1,4 @@ +#define padlock_width 7 +#define padlock_height 9 +static unsigned char padlock_bits[] = { + 0x1c, 0x22, 0x22, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}; diff --git a/images/sm_application.xbm b/images/sm_application.xbm new file mode 100644 index 0000000..153e70b --- /dev/null +++ b/images/sm_application.xbm @@ -0,0 +1,6 @@ +#define sm_application_width 15 +#define sm_application_height 15 +static unsigned char sm_application_bits[] = { + 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, 0x10, 0x04, 0x08, 0x08, 0x04, 0x10, + 0x02, 0x26, 0x01, 0x49, 0x82, 0x72, 0x64, 0x6f, 0x88, 0x60, 0x10, 0x7f, + 0x20, 0x62, 0x40, 0x01, 0x80, 0x00}; diff --git a/images/sm_cdrom.xbm b/images/sm_cdrom.xbm new file mode 100644 index 0000000..e18bbf7 --- /dev/null +++ b/images/sm_cdrom.xbm @@ -0,0 +1,6 @@ +#define sm_cdrom_width 16 +#define sm_cdrom_height 16 +static unsigned char sm_cdrom_bits[] = { + 0xe0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x0a, 0x40, 0x12, 0x40, 0xed, 0x83, + 0xb1, 0x85, 0x61, 0x86, 0x61, 0x86, 0xa1, 0x8d, 0xc1, 0xb7, 0x02, 0x48, + 0x02, 0x50, 0x04, 0x20, 0x18, 0x18, 0xe0, 0x07}; diff --git a/images/sm_document.xbm b/images/sm_document.xbm new file mode 100644 index 0000000..15f5004 --- /dev/null +++ b/images/sm_document.xbm @@ -0,0 +1,6 @@ +#define sm_document_width 13 +#define sm_document_height 16 +static unsigned char sm_document_bits[] = { + 0xff, 0x03, 0x01, 0x06, 0x01, 0x0a, 0x01, 0x1e, 0x01, 0x10, 0x01, 0x10, + 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, + 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0xff, 0x1f}; diff --git a/images/sm_floppy.xbm b/images/sm_floppy.xbm new file mode 100644 index 0000000..cf62603 --- /dev/null +++ b/images/sm_floppy.xbm @@ -0,0 +1,6 @@ +#define sm_floppy_width 16 +#define sm_floppy_height 16 +static unsigned char sm_floppy_bits[] = { + 0xfe, 0x3f, 0x09, 0x50, 0x09, 0x96, 0x09, 0x90, 0xf1, 0x8f, 0x01, 0x80, + 0xf9, 0x9f, 0x05, 0xa0, 0x05, 0xa0, 0x05, 0xa0, 0x05, 0xa0, 0x05, 0xa0, + 0x05, 0xa0, 0x05, 0xa0, 0x05, 0xa0, 0xff, 0xff}; diff --git a/images/sm_folder.xbm b/images/sm_folder.xbm new file mode 100644 index 0000000..e41c2a0 --- /dev/null +++ b/images/sm_folder.xbm @@ -0,0 +1,6 @@ +#define sm_folder_width 16 +#define sm_folder_height 13 +static unsigned char sm_folder_bits[] = { + 0x3c, 0x00, 0x42, 0x00, 0xff, 0x7f, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0xff, 0xff}; diff --git a/images/sm_harddisk.xbm b/images/sm_harddisk.xbm new file mode 100644 index 0000000..231824a --- /dev/null +++ b/images/sm_harddisk.xbm @@ -0,0 +1,4 @@ +#define sm_harddisk_width 16 +#define sm_harddisk_height 6 +static unsigned char sm_harddisk_bits[] = { + 0xff, 0xff, 0x01, 0x80, 0x01, 0x80, 0x05, 0x80, 0x01, 0x80, 0xff, 0xff}; diff --git a/images/stop.xbm b/images/stop.xbm new file mode 100644 index 0000000..290e1a9 --- /dev/null +++ b/images/stop.xbm @@ -0,0 +1,14 @@ +#define stop_width 32 +#define stop_height 32 +static unsigned char stop_bits[] = { + 0x00, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x01, 0xc0, 0xff, 0xff, 0x03, + 0xe0, 0xff, 0xfe, 0x07, 0xf0, 0x7f, 0xfc, 0x0f, 0xf8, 0x6f, 0xcc, 0x1f, + 0xfc, 0x47, 0xc4, 0x3f, 0xfe, 0x47, 0xc4, 0x7f, 0xff, 0x45, 0xc4, 0xff, + 0xff, 0x44, 0xc4, 0xff, 0xff, 0x44, 0xc4, 0xff, 0xff, 0x44, 0xc4, 0xff, + 0xff, 0x44, 0xc4, 0xff, 0xff, 0x44, 0xc4, 0xff, 0xff, 0x04, 0xc0, 0xf1, + 0xff, 0x00, 0xc0, 0xf0, 0xff, 0x00, 0xc0, 0xf0, 0xff, 0x00, 0x40, 0xf8, + 0xff, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xfc, 0xff, 0x00, 0x00, 0xfc, + 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xff, + 0xfe, 0x00, 0x00, 0x7f, 0xfc, 0x40, 0x80, 0x3f, 0xf8, 0x81, 0xc0, 0x1f, + 0xf0, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0x07, 0xc0, 0xff, 0xff, 0x03, + 0x80, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x00}; diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ebc6691 --- /dev/null +++ b/install-sh @@ -0,0 +1,250 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libhfs/.stamp/config.h b/libhfs/.stamp/config.h new file mode 100644 index 0000000..e69de29 diff --git a/libhfs/.stamp/config.h.in b/libhfs/.stamp/config.h.in new file mode 100644 index 0000000..e69de29 diff --git a/libhfs/.stamp/configure b/libhfs/.stamp/configure new file mode 100644 index 0000000..e69de29 diff --git a/libhfs/Makefile.in b/libhfs/Makefile.in new file mode 100644 index 0000000..996a22e --- /dev/null +++ b/libhfs/Makefile.in @@ -0,0 +1,143 @@ +# +# libhfs - library for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile.in,v 1.11 1998/11/02 22:08:51 rob Exp $ +# + +@SET_MAKE@ +src = @srcdir@ +VPATH = @srcdir@ + +### USER CUSTOMIZATIONS FOLLOW ################################################ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +MANDEST = @mandir@ + +INCDEST = @includedir@ +LIBDEST = @libdir@ + +INSTALL = @INSTALL@ +LIBINSTALL = @INSTALL_DATA@ +SOFTLINK = @LN_S@ + +OS = unix + +CC = @CC@ +INCLUDES = @CPPFLAGS@ +DEFINES = @DEFS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ + +COPTS = @CFLAGS@ +LDOPTS = @LDFLAGS@ + +AR = ar rc +RANLIB = @RANLIB@ + +### END OF USER CUSTOMIZATION ################################################# + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(HFSTARGET) + +HFSTARGET = libhfs.a +HFSOBJS = os.o data.o block.o low.o medium.o file.o btree.o node.o \ + record.o volume.o hfs.o version.o $(LIBOBJS) + +############################################################################### + +all :: $(TARGETS) + +check :: all + @echo "No self-tests available." + +install :: + $(LIBINSTALL) libhfs.a "$(LIBDEST)/." + $(LIBINSTALL) hfs.h "$(INCDEST)/." + +again :: clean all + +depend :: + ( sed -n '1,/^### DEPEND/p' Makefile.in; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.in.new + mv -f Makefile.in.new Makefile.in + +clean :: + rm -f $(TARGETS) *.o gmon.* core + +distclean :: clean + rm -f config.status config.cache config.log config.h Makefile + +maintainer-clean :: distclean + rm -f config.h.in configure + +############################################################################### + +Makefile: config.status + ./config.status && touch .stamp/config.h + +.stamp/config.h: config.status + ./config.status && touch .stamp/config.h + +config.status: .stamp/configure .stamp/config.h.in Makefile.in + ./config.status --recheck + +.stamp/configure: configure.in + autoconf && touch .stamp/configure + +.stamp/config.h.in: configure.in acconfig.h + autoheader && touch .stamp/config.h.in + +############################################################################### + +$(HFSTARGET): $(HFSOBJS) + $(AR) $@ $(HFSOBJS) + $(RANLIB) $@ + +os.c: os/$(OS).c + rm -f $@ + $(SOFTLINK) os/$(OS).c $@ + +### DEPENDENCIES FOLLOW ####################################################### + +block.o: block.c config.h libhfs.h hfs.h apple.h volume.h block.h os.h +btree.o: btree.c config.h libhfs.h hfs.h apple.h btree.h data.h file.h \ + block.h node.h +data.o: data.c config.h data.h +file.o: file.c config.h libhfs.h hfs.h apple.h file.h btree.h record.h \ + volume.h +hfs.o: hfs.c config.h libhfs.h hfs.h apple.h data.h block.h medium.h \ + file.h btree.h node.h record.h volume.h +low.o: low.c config.h libhfs.h hfs.h apple.h low.h data.h block.h \ + file.h +medium.o: medium.c config.h libhfs.h hfs.h apple.h block.h low.h \ + medium.h +memcmp.o: memcmp.c config.h +node.o: node.c config.h libhfs.h hfs.h apple.h node.h data.h btree.h +os.o: os.c config.h libhfs.h hfs.h apple.h os.h +record.o: record.c config.h libhfs.h hfs.h apple.h record.h data.h +version.o: version.c version.h +volume.o: volume.c config.h libhfs.h hfs.h apple.h volume.h data.h \ + block.h low.h medium.h file.h btree.h record.h os.h diff --git a/libhfs/acconfig.h b/libhfs/acconfig.h new file mode 100644 index 0000000..a4e50ba --- /dev/null +++ b/libhfs/acconfig.h @@ -0,0 +1,38 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ +@TOP@ + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +@BOTTOM@ + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ + +# ifdef DEBUG +# include +# endif diff --git a/libhfs/apple.h b/libhfs/apple.h new file mode 100644 index 0000000..eef34d6 --- /dev/null +++ b/libhfs/apple.h @@ -0,0 +1,272 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: apple.h,v 1.1 1998/04/11 08:27:11 rob Exp $ + */ + +typedef signed char Char; +typedef unsigned char UChar; +typedef signed char SignedByte; +typedef signed short Integer; +typedef unsigned short UInteger; +typedef signed long LongInt; +typedef unsigned long ULongInt; +typedef char Str15[16]; +typedef char Str31[32]; +typedef long OSType; + +typedef struct { + Integer sbSig; /* device signature (should be 0x4552) */ + Integer sbBlkSize; /* block size of the device (in bytes) */ + LongInt sbBlkCount; /* number of blocks on the device */ + Integer sbDevType; /* reserved */ + Integer sbDevId; /* reserved */ + LongInt sbData; /* reserved */ + Integer sbDrvrCount; /* number of driver descriptor entries */ + LongInt ddBlock; /* first driver's starting block */ + Integer ddSize; /* size of the driver, in 512-byte blocks */ + Integer ddType; /* driver operating system type (MacOS = 1) */ + Integer ddPad[243]; /* additional drivers, if any */ +} Block0; + +typedef struct { + Integer pmSig; /* partition signature (0x504d or 0x5453) */ + Integer pmSigPad; /* reserved */ + LongInt pmMapBlkCnt; /* number of blocks in partition map */ + LongInt pmPyPartStart; /* first physical block of partition */ + LongInt pmPartBlkCnt; /* number of blocks in partition */ + Char pmPartName[33]; /* partition name */ + Char pmParType[33]; /* partition type */ + LongInt pmLgDataStart; /* first logical block of data area */ + LongInt pmDataCnt; /* number of blocks in data area */ + LongInt pmPartStatus; /* partition status information */ + LongInt pmLgBootStart; /* first logical block of boot code */ + LongInt pmBootSize; /* size of boot code, in bytes */ + LongInt pmBootAddr; /* boot code load address */ + LongInt pmBootAddr2; /* reserved */ + LongInt pmBootEntry; /* boot code entry point */ + LongInt pmBootEntry2; /* reserved */ + LongInt pmBootCksum; /* boot code checksum */ + Char pmProcessor[17];/* processor type */ + Integer pmPad[188]; /* reserved */ +} Partition; + +typedef struct { + Integer bbID; /* boot blocks signature */ + LongInt bbEntry; /* entry point to boot code */ + Integer bbVersion; /* boot blocks version number */ + Integer bbPageFlags; /* used internally */ + Str15 bbSysName; /* System filename */ + Str15 bbShellName; /* Finder filename */ + Str15 bbDbg1Name; /* debugger filename */ + Str15 bbDbg2Name; /* debugger filename */ + Str15 bbScreenName; /* name of startup screen */ + Str15 bbHelloName; /* name of startup program */ + Str15 bbScrapName; /* name of system scrap file */ + Integer bbCntFCBs; /* number of FCBs to allocate */ + Integer bbCntEvts; /* number of event queue elements */ + LongInt bb128KSHeap; /* system heap size on 128K Mac */ + LongInt bb256KSHeap; /* used internally */ + LongInt bbSysHeapSize; /* system heap size on all machines */ + Integer filler; /* reserved */ + LongInt bbSysHeapExtra; /* additional system heap space */ + LongInt bbSysHeapFract; /* fraction of RAM for system heap */ +} BootBlkHdr; + +typedef struct { + UInteger xdrStABN; /* first allocation block */ + UInteger xdrNumABlks; /* number of allocation blocks */ +} ExtDescriptor; + +typedef ExtDescriptor ExtDataRec[3]; + +typedef struct { + SignedByte xkrKeyLen; /* key length */ + SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */ + ULongInt xkrFNum; /* file number */ + UInteger xkrFABN; /* starting file allocation block */ +} ExtKeyRec; + +typedef struct { + SignedByte ckrKeyLen; /* key length */ + SignedByte ckrResrv1; /* reserved */ + ULongInt ckrParID; /* parent directory ID */ + Str31 ckrCName; /* catalog node name */ +} CatKeyRec; + +typedef struct { + Integer v; /* vertical coordinate */ + Integer h; /* horizontal coordinate */ +} Point; + +typedef struct { + Integer top; /* top edge of rectangle */ + Integer left; /* left edge */ + Integer bottom; /* bottom edge */ + Integer right; /* right edge */ +} Rect; + +typedef struct { + Rect frRect; /* folder's rectangle */ + Integer frFlags; /* flags */ + Point frLocation; /* folder's location */ + Integer frView; /* folder's view */ +} DInfo; + +typedef struct { + Point frScroll; /* scroll position */ + LongInt frOpenChain; /* directory ID chain of open folders */ + Integer frUnused; /* reserved */ + Integer frComment; /* comment ID */ + LongInt frPutAway; /* directory ID */ +} DXInfo; + +typedef struct { + OSType fdType; /* file type */ + OSType fdCreator; /* file's creator */ + Integer fdFlags; /* flags */ + Point fdLocation; /* file's location */ + Integer fdFldr; /* file's window */ +} FInfo; + +typedef struct { + Integer fdIconID; /* icon ID */ + Integer fdUnused[4]; /* reserved */ + Integer fdComment; /* comment ID */ + LongInt fdPutAway; /* home directory ID */ +} FXInfo; + +typedef struct { + Integer drSigWord; /* volume signature (0x4244 for HFS) */ + LongInt drCrDate; /* date and time of volume creation */ + LongInt drLsMod; /* date and time of last modification */ + Integer drAtrb; /* volume attributes */ + UInteger drNmFls; /* number of files in root directory */ + UInteger drVBMSt; /* first block of volume bit map (always 3) */ + UInteger drAllocPtr; /* start of next allocation search */ + UInteger drNmAlBlks; /* number of allocation blocks in volume */ + ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */ + ULongInt drClpSiz; /* default clump size */ + UInteger drAlBlSt; /* first allocation block in volume */ + LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */ + UInteger drFreeBks; /* number of unused allocation blocks */ + char drVN[28]; /* volume name (1-27 chars) */ + LongInt drVolBkUp; /* date and time of last backup */ + Integer drVSeqNum; /* volume backup sequence number */ + ULongInt drWrCnt; /* volume write count */ + ULongInt drXTClpSiz; /* clump size for extents overflow file */ + ULongInt drCTClpSiz; /* clump size for catalog file */ + UInteger drNmRtDirs; /* number of directories in root directory */ + ULongInt drFilCnt; /* number of files in volume */ + ULongInt drDirCnt; /* number of directories in volume */ + LongInt drFndrInfo[8]; /* information used by the Finder */ + UInteger drEmbedSigWord; /* type of embedded volume */ + ExtDescriptor drEmbedExtent; /* location of embedded volume */ + ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */ + ExtDataRec drXTExtRec; /* first extent record for extents file */ + ULongInt drCTFlSize; /* size (in bytes) of catalog file */ + ExtDataRec drCTExtRec; /* first extent record for catalog file */ +} MDB; + +typedef enum { + cdrDirRec = 1, + cdrFilRec = 2, + cdrThdRec = 3, + cdrFThdRec = 4 +} CatDataType; + +typedef struct { + SignedByte cdrType; /* record type */ + SignedByte cdrResrv2; /* reserved */ + union { + struct { /* cdrDirRec */ + Integer dirFlags; /* directory flags */ + UInteger dirVal; /* directory valence */ + ULongInt dirDirID; /* directory ID */ + LongInt dirCrDat; /* date and time of creation */ + LongInt dirMdDat; /* date and time of last modification */ + LongInt dirBkDat; /* date and time of last backup */ + DInfo dirUsrInfo; /* Finder information */ + DXInfo dirFndrInfo; /* additional Finder information */ + LongInt dirResrv[4]; /* reserved */ + } dir; + struct { /* cdrFilRec */ + SignedByte + filFlags; /* file flags */ + SignedByte + filTyp; /* file type */ + FInfo filUsrWds; /* Finder information */ + ULongInt filFlNum; /* file ID */ + UInteger filStBlk; /* first alloc block of data fork */ + ULongInt filLgLen; /* logical EOF of data fork */ + ULongInt filPyLen; /* physical EOF of data fork */ + UInteger filRStBlk; /* first alloc block of resource fork */ + ULongInt filRLgLen; /* logical EOF of resource fork */ + ULongInt filRPyLen; /* physical EOF of resource fork */ + LongInt filCrDat; /* date and time of creation */ + LongInt filMdDat; /* date and time of last modification */ + LongInt filBkDat; /* date and time of last backup */ + FXInfo filFndrInfo; /* additional Finder information */ + UInteger filClpSize; /* file clump size */ + ExtDataRec + filExtRec; /* first data fork extent record */ + ExtDataRec + filRExtRec; /* first resource fork extent record */ + LongInt filResrv; /* reserved */ + } fil; + struct { /* cdrThdRec */ + LongInt thdResrv[2]; /* reserved */ + ULongInt thdParID; /* parent ID for this directory */ + Str31 thdCName; /* name of this directory */ + } dthd; + struct { /* cdrFThdRec */ + LongInt fthdResrv[2]; /* reserved */ + ULongInt fthdParID; /* parent ID for this file */ + Str31 fthdCName; /* name of this file */ + } fthd; + } u; +} CatDataRec; + +typedef struct { + ULongInt ndFLink; /* forward link */ + ULongInt ndBLink; /* backward link */ + SignedByte ndType; /* node type */ + SignedByte ndNHeight; /* node level */ + UInteger ndNRecs; /* number of records in node */ + Integer ndResv2; /* reserved */ +} NodeDescriptor; + +enum { + ndIndxNode = (SignedByte) 0x00, + ndHdrNode = (SignedByte) 0x01, + ndMapNode = (SignedByte) 0x02, + ndLeafNode = (SignedByte) 0xff +}; + +typedef struct { + UInteger bthDepth; /* current depth of tree */ + ULongInt bthRoot; /* number of root node */ + ULongInt bthNRecs; /* number of leaf records in tree */ + ULongInt bthFNode; /* number of first leaf node */ + ULongInt bthLNode; /* number of last leaf node */ + UInteger bthNodeSize; /* size of a node */ + UInteger bthKeyLen; /* maximum length of a key */ + ULongInt bthNNodes; /* total number of nodes in tree */ + ULongInt bthFree; /* number of free nodes */ + SignedByte bthResv[76]; /* reserved */ +} BTHdrRec; diff --git a/libhfs/block.c b/libhfs/block.c new file mode 100644 index 0000000..2a002fc --- /dev/null +++ b/libhfs/block.c @@ -0,0 +1,807 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: block.c,v 1.11 1998/11/02 22:08:52 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "libhfs.h" +# include "volume.h" +# include "block.h" +# include "os.h" + +# define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE) +# define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY) + +/* + * NAME: block->init() + * DESCRIPTION: initialize a volume's block cache + */ +int b_init(hfsvol *vol) +{ + bcache *cache; + int i; + + ASSERT(vol->cache == 0); + + cache = ALLOC(bcache, 1); + if (cache == 0) + ERROR(ENOMEM, 0); + + vol->cache = cache; + + cache->vol = vol; + cache->tail = &cache->chain[HFS_CACHESZ - 1]; + + cache->hits = 0; + cache->misses = 0; + + for (i = 0; i < HFS_CACHESZ; ++i) + { + bucket *b = &cache->chain[i]; + + b->flags = 0; + b->count = 0; + + b->bnum = 0; + b->data = &cache->pool[i]; + + b->cnext = b + 1; + b->cprev = b - 1; + + b->hnext = 0; + b->hprev = 0; + } + + cache->chain[0].cprev = cache->tail; + cache->tail->cnext = &cache->chain[0]; + + for (i = 0; i < HFS_HASHSZ; ++i) + cache->hash[i] = 0; + + return 0; + +fail: + return -1; +} + +# ifdef DEBUG +/* + * NAME: block->showstats() + * DESCRIPTION: output cache hit/miss ratio + */ +void b_showstats(const bcache *cache) +{ + fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n", + (unsigned long) cache->vol, cache->vol->mdb.drVN, + (float) cache->hits / (float) cache->misses); +} + +/* + * NAME: block->dumpcache() + * DESCRIPTION: dump the cache tables for a volume + */ +void b_dumpcache(const bcache *cache) +{ + const bucket *b; + int i; + + fprintf(stderr, "BLOCK CACHE DUMP:\n"); + + for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext) + { + if (INUSE(b)) + { + fprintf(stderr, "\t %lu", b->bnum); + if (DIRTY(b)) + fprintf(stderr, "*"); + + fprintf(stderr, ":%u", b->count); + } + } + + fprintf(stderr, "\n"); + + fprintf(stderr, "BLOCK HASH DUMP:\n"); + + for (i = 0; i < HFS_HASHSZ; ++i) + { + int seen = 0; + + for (b = cache->hash[i]; b; b = b->hnext) + { + if (! seen) + fprintf(stderr, " %d:", i); + + if (INUSE(b)) + { + fprintf(stderr, " %lu", b->bnum); + if (DIRTY(b)) + fprintf(stderr, "*"); + + fprintf(stderr, ":%u", b->count); + } + + seen = 1; + } + + if (seen) + fprintf(stderr, "\n"); + } +} +# endif + +/* + * NAME: fillchain() + * DESCRIPTION: fill a chain of bucket buffers with a single read + */ +static +int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count) +{ + bucket *blist[HFS_BLOCKBUFSZ], **start = bptr; + unsigned long bnum; + unsigned int len, i; + + for (len = 0; len < HFS_BLOCKBUFSZ && + (unsigned int) (bptr - start) < *count; ++bptr) + { + if (INUSE(*bptr)) + continue; + + if (len > 0 && (*bptr)->bnum != bnum) + break; + + blist[len++] = *bptr; + bnum = (*bptr)->bnum + 1; + } + + *count = bptr - start; + + if (len == 0) + goto done; + else if (len == 1) + { + if (b_readpb(vol, vol->vstart + blist[0]->bnum, + blist[0]->data, 1) == -1) + goto fail; + } + else + { + block buffer[HFS_BLOCKBUFSZ]; + + if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1) + goto fail; + + for (i = 0; i < len; ++i) + memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ); + } + + for (i = 0; i < len; ++i) + { + blist[i]->flags |= HFS_BUCKET_INUSE; + blist[i]->flags &= ~HFS_BUCKET_DIRTY; + } + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: flushchain() + * DESCRIPTION: store a chain of bucket buffers with a single write + */ +static +int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count) +{ + bucket *blist[HFS_BLOCKBUFSZ], **start = bptr; + unsigned long bnum; + unsigned int len, i; + + for (len = 0; len < HFS_BLOCKBUFSZ && + (unsigned int) (bptr - start) < *count; ++bptr) + { + if (! INUSE(*bptr) || ! DIRTY(*bptr)) + continue; + + if (len > 0 && (*bptr)->bnum != bnum) + break; + + blist[len++] = *bptr; + bnum = (*bptr)->bnum + 1; + } + + *count = bptr - start; + + if (len == 0) + goto done; + else if (len == 1) + { + if (b_writepb(vol, vol->vstart + blist[0]->bnum, + blist[0]->data, 1) == -1) + goto fail; + } + else + { + block buffer[HFS_BLOCKBUFSZ]; + + for (i = 0; i < len; ++i) + memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ); + + if (b_writepb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1) + goto fail; + } + + for (i = 0; i < len; ++i) + blist[i]->flags &= ~HFS_BUCKET_DIRTY; + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: compare() + * DESCRIPTION: comparison function for qsort of cache bucket pointers + */ +static +int compare(const bucket **b1, const bucket **b2) +{ + long diff; + + diff = (*b1)->bnum - (*b2)->bnum; + + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else + return 0; +} + +/* + * NAME: dobuckets() + * DESCRIPTION: fill or flush an array of cache buckets to a volume + */ +static +int dobuckets(hfsvol *vol, bucket **chain, unsigned int len, + int (*func)(hfsvol *, bucket **, unsigned int *)) +{ + unsigned int count, i; + int result = 0; + + qsort(chain, len, sizeof(*chain), + (int (*)(const void *, const void *)) compare); + + for (i = 0; i < len; i += count) + { + count = len - i; + if (func(vol, chain + i, &count) == -1) + result = -1; + } + + return result; +} + +# define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain) +# define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain) + +/* + * NAME: block->flush() + * DESCRIPTION: commit dirty cache blocks to a volume + */ +int b_flush(hfsvol *vol) +{ + bcache *cache = vol->cache; + bucket *chain[HFS_CACHESZ]; + int i; + + if (cache == 0 || (vol->flags & HFS_VOL_READONLY)) + goto done; + + for (i = 0; i < HFS_CACHESZ; ++i) + chain[i] = &cache->chain[i]; + + if (flushbuckets(vol, chain, HFS_CACHESZ) == -1) + goto fail; + +done: +# ifdef DEBUG + if (cache) + b_showstats(cache); +# endif + + return 0; + +fail: + return -1; +} + +/* + * NAME: block->finish() + * DESCRIPTION: commit and free a volume's block cache + */ +int b_finish(hfsvol *vol) +{ + int result = 0; + + if (vol->cache == 0) + goto done; + +# ifdef DEBUG + b_dumpcache(vol->cache); +# endif + + result = b_flush(vol); + + FREE(vol->cache); + vol->cache = 0; + +done: + return result; +} + +/* + * NAME: findbucket() + * DESCRIPTION: locate a bucket in the cache, and/or its hash slot + */ +static +bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot) +{ + bucket *b; + + *hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)]; + + for (b = **hslot; b; b = b->hnext) + { + if (INUSE(b) && b->bnum == bnum) + break; + } + + return b; +} + +/* + * NAME: reuse() + * DESCRIPTION: free a bucket for reuse, flushing if necessary + */ +static +int reuse(bcache *cache, bucket *b, unsigned long bnum) +{ + bucket *chain[HFS_BLOCKBUFSZ], *bptr; + int i; + +# ifdef DEBUG + if (INUSE(b)) + fprintf(stderr, "BLOCK: CACHE reusing bucket containing " + "vol 0x%lx block %lu:%u\n", + (unsigned long) cache->vol, b->bnum, b->count); +# endif + + if (INUSE(b) && DIRTY(b)) + { + /* flush most recently unused buckets */ + + for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i) + { + chain[i] = bptr; + bptr = bptr->cprev; + } + + if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1) + goto fail; + } + + b->flags &= ~HFS_BUCKET_INUSE; + b->count = 1; + b->bnum = bnum; + + return 0; + +fail: + return -1; +} + +/* + * NAME: cplace() + * DESCRIPTION: move a bucket to an appropriate place near head of the chain + */ +static +void cplace(bcache *cache, bucket *b) +{ + bucket *p; + + for (p = cache->tail->cnext; p->count > 1; p = p->cnext) + --p->count; + + b->cnext->cprev = b->cprev; + b->cprev->cnext = b->cnext; + + if (cache->tail == b) + cache->tail = b->cprev; + + b->cprev = p->cprev; + b->cnext = p; + + p->cprev->cnext = b; + p->cprev = b; +} + +/* + * NAME: hplace() + * DESCRIPTION: move a bucket to the head of its hash slot + */ +static +void hplace(bucket **hslot, bucket *b) +{ + if (*hslot != b) + { + if (b->hprev) + *b->hprev = b->hnext; + if (b->hnext) + b->hnext->hprev = b->hprev; + + b->hprev = hslot; + b->hnext = *hslot; + + if (*hslot) + (*hslot)->hprev = &b->hnext; + + *hslot = b; + } +} + +/* + * NAME: getbucket() + * DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled + */ +static +bucket *getbucket(bcache *cache, unsigned long bnum, int fill) +{ + bucket **hslot, *b, *p, *bptr, + *chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ]; + + b = findbucket(cache, bnum, &hslot); + + if (b) + { + /* cache hit; move towards head of cache chain */ + + ++cache->hits; + + if (++b->count > b->cprev->count && + b != cache->tail->cnext) + { + p = b->cprev; + + p->cprev->cnext = b; + b->cnext->cprev = p; + + p->cnext = b->cnext; + b->cprev = p->cprev; + + p->cprev = b; + b->cnext = p; + + if (cache->tail == b) + cache->tail = p; + } + } + else + { + /* cache miss; reuse least-used cache bucket */ + + ++cache->misses; + + b = cache->tail; + + if (reuse(cache, b, bnum) == -1) + goto fail; + + if (fill) + { + unsigned int len = 0; + + chain[len] = b; + slots[len++] = hslot; + + for (bptr = b->cprev; + len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen; + bptr = bptr->cprev) + { + if (findbucket(cache, bnum, &hslot)) + break; + + if (reuse(cache, bptr, bnum) == -1) + goto fail; + + chain[len] = bptr; + slots[len++] = hslot; + } + + if (fillbuckets(cache->vol, chain, len) == -1) + goto fail; + + while (--len) + { + cplace(cache, chain[len]); + hplace(slots[len], chain[len]); + } + + hslot = slots[0]; + } + + /* move bucket to appropriate place in chain */ + + cplace(cache, b); + } + + /* insert at front of hash chain */ + + hplace(hslot, b); + + return b; + +fail: + return 0; +} + +/* + * NAME: block->readpb() + * DESCRIPTION: read blocks from the physical medium (bypassing cache) + */ +int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen) +{ + unsigned long nblocks; + +# ifdef DEBUG + fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu", + (unsigned long) vol, bnum); + if (blen > 1) + fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1); + else + fprintf(stderr, "\n"); +# endif + + nblocks = os_seek(&vol->priv, bnum); + if (nblocks == (unsigned long) -1) + goto fail; + + if (nblocks != bnum) + ERROR(EIO, "block seek failed for read"); + + nblocks = os_read(&vol->priv, bp, blen); + if (nblocks == (unsigned long) -1) + goto fail; + + if (nblocks != blen) + ERROR(EIO, "incomplete block read"); + + return 0; + +fail: + return -1; +} + +/* + * NAME: block->writepb() + * DESCRIPTION: write blocks to the physical medium (bypassing cache) + */ +int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp, + unsigned int blen) +{ + unsigned long nblocks; + +# ifdef DEBUG + fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu", + (unsigned long) vol, bnum); + if (blen > 1) + fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1); + else + fprintf(stderr, "\n"); +# endif + + nblocks = os_seek(&vol->priv, bnum); + if (nblocks == (unsigned long) -1) + goto fail; + + if (nblocks != bnum) + ERROR(EIO, "block seek failed for write"); + + nblocks = os_write(&vol->priv, bp, blen); + if (nblocks == (unsigned long) -1) + goto fail; + + if (nblocks != blen) + ERROR(EIO, "incomplete block write"); + + return 0; + +fail: + return -1; +} + +/* + * NAME: block->readlb() + * DESCRIPTION: read a logical block from a volume (or from the cache) + */ +int b_readlb(hfsvol *vol, unsigned long bnum, block *bp) +{ + if (vol->vlen > 0 && bnum >= vol->vlen) + ERROR(EIO, "read nonexistent logical block"); + + if (vol->cache) + { + bucket *b; + + b = getbucket(vol->cache, bnum, 1); + if (b == 0) + goto fail; + + memcpy(bp, b->data, HFS_BLOCKSZ); + } + else + { + if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1) + goto fail; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: block->writelb() + * DESCRIPTION: write a logical block to a volume (or to the cache) + */ +int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp) +{ + if (vol->vlen > 0 && bnum >= vol->vlen) + ERROR(EIO, "write nonexistent logical block"); + + if (vol->cache) + { + bucket *b; + + b = getbucket(vol->cache, bnum, 0); + if (b == 0) + goto fail; + + if (! INUSE(b) || + memcmp(b->data, bp, HFS_BLOCKSZ) != 0) + { + memcpy(b->data, bp, HFS_BLOCKSZ); + b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY; + } + } + else + { + if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1) + goto fail; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: block->readab() + * DESCRIPTION: read a block from an allocation block from a volume + */ +int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp) +{ + /* verify the allocation block exists and is marked as in-use */ + + if (anum >= vol->mdb.drNmAlBlks) + ERROR(EIO, "read nonexistent allocation block"); + else if (vol->vbm && ! BMTST(vol->vbm, anum)) + ERROR(EIO, "read unallocated block"); + + return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp); + +fail: + return -1; +} + +/* + * NAME: block->writeab() + * DESCRIPTION: write a block to an allocation block to a volume + */ +int b_writeab(hfsvol *vol, + unsigned int anum, unsigned int index, const block *bp) +{ + /* verify the allocation block exists and is marked as in-use */ + + if (anum >= vol->mdb.drNmAlBlks) + ERROR(EIO, "write nonexistent allocation block"); + else if (vol->vbm && ! BMTST(vol->vbm, anum)) + ERROR(EIO, "write unallocated block"); + + if (v_dirty(vol) == -1) + goto fail; + + return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp); + +fail: + return -1; +} + +/* + * NAME: block->size() + * DESCRIPTION: return the number of physical blocks on a volume's medium + */ +unsigned long b_size(hfsvol *vol) +{ + unsigned long low, high, mid; + block b; + + high = os_seek(&vol->priv, -1); + + if (high != (unsigned long) -1 && high > 0) + return high; + + /* manual size detection: first check there is at least 1 block in medium */ + + if (b_readpb(vol, 0, &b, 1) == -1) + ERROR(EIO, "size of medium indeterminable or empty"); + + for (low = 0, high = 2880; + high > 0 && b_readpb(vol, high - 1, &b, 1) != -1; + high <<= 1) + low = high - 1; + + if (high == 0) + ERROR(EIO, "size of medium indeterminable or too large"); + + /* common case: 1440K floppy */ + + if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1) + return 2880; + + /* binary search for other sizes */ + + while (low < high - 1) + { + mid = (low + high) >> 1; + + if (b_readpb(vol, mid, &b, 1) == -1) + high = mid; + else + low = mid; + } + + return low + 1; + +fail: + return 0; +} diff --git a/libhfs/block.h b/libhfs/block.h new file mode 100644 index 0000000..fdbc099 --- /dev/null +++ b/libhfs/block.h @@ -0,0 +1,40 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: block.h,v 1.10 1998/11/02 22:08:53 rob Exp $ + */ + +int b_init(hfsvol *); +int b_flush(hfsvol *); +int b_finish(hfsvol *); + +int b_readpb(hfsvol *, unsigned long, block *, unsigned int); +int b_writepb(hfsvol *, unsigned long, const block *, unsigned int); + +int b_readlb(hfsvol *, unsigned long, block *); +int b_writelb(hfsvol *, unsigned long, const block *); + +int b_readab(hfsvol *, unsigned int, unsigned int, block *); +int b_writeab(hfsvol *, unsigned int, unsigned int, const block *); + +unsigned long b_size(hfsvol *); + +# ifdef DEBUG +void b_showstats(const bcache *); +void b_dumpcache(const bcache *); +# endif diff --git a/libhfs/btree.c b/libhfs/btree.c new file mode 100644 index 0000000..d7d1916 --- /dev/null +++ b/libhfs/btree.c @@ -0,0 +1,700 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: btree.c,v 1.10 1998/11/02 22:08:54 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "libhfs.h" +# include "btree.h" +# include "data.h" +# include "file.h" +# include "block.h" +# include "node.h" + +/* + * NAME: btree->getnode() + * DESCRIPTION: retrieve a numbered node from a B*-tree file + */ +int bt_getnode(node *np, btree *bt, unsigned long nnum) +{ + block *bp = &np->data; + const byte *ptr; + int i; + + np->bt = bt; + np->nnum = nnum; + +# if 0 + fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n", + bt->f.vol->mdb.drVN, bt->f.name, np->nnum); +# endif + + /* verify the node exists and is marked as in-use */ + + if (nnum > 0 && nnum >= bt->hdr.bthNNodes) + ERROR(EIO, "read nonexistent b*-tree node"); + else if (bt->map && ! BMTST(bt->map, nnum)) + ERROR(EIO, "read unallocated b*-tree node"); + + if (f_getblock(&bt->f, nnum, bp) == -1) + goto fail; + + ptr = *bp; + + d_fetchul(&ptr, &np->nd.ndFLink); + d_fetchul(&ptr, &np->nd.ndBLink); + d_fetchsb(&ptr, &np->nd.ndType); + d_fetchsb(&ptr, &np->nd.ndNHeight); + d_fetchuw(&ptr, &np->nd.ndNRecs); + d_fetchsw(&ptr, &np->nd.ndResv2); + + if (np->nd.ndNRecs > HFS_MAX_NRECS) + ERROR(EIO, "too many b*-tree node records"); + + i = np->nd.ndNRecs + 1; + + ptr = *bp + HFS_BLOCKSZ - (2 * i); + + while (i--) + d_fetchuw(&ptr, &np->roff[i]); + + return 0; + +fail: + return -1; +} + +/* + * NAME: btree->putnode() + * DESCRIPTION: store a numbered node into a B*-tree file + */ +int bt_putnode(node *np) +{ + btree *bt = np->bt; + block *bp = &np->data; + byte *ptr; + int i; + +# if 0 + fprintf(stderr, "BTREE: PUT vol \"%s\" btree \"%s\" node %lu\n", + bt->f.vol->mdb.drVN, bt->f.name, np->nnum); +# endif + + /* verify the node exists and is marked as in-use */ + + if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes) + ERROR(EIO, "write nonexistent b*-tree node"); + else if (bt->map && ! BMTST(bt->map, np->nnum)) + ERROR(EIO, "write unallocated b*-tree node"); + + ptr = *bp; + + d_storeul(&ptr, np->nd.ndFLink); + d_storeul(&ptr, np->nd.ndBLink); + d_storesb(&ptr, np->nd.ndType); + d_storesb(&ptr, np->nd.ndNHeight); + d_storeuw(&ptr, np->nd.ndNRecs); + d_storesw(&ptr, np->nd.ndResv2); + + if (np->nd.ndNRecs > HFS_MAX_NRECS) + ERROR(EIO, "too many b*-tree node records"); + + i = np->nd.ndNRecs + 1; + + ptr = *bp + HFS_BLOCKSZ - (2 * i); + + while (i--) + d_storeuw(&ptr, np->roff[i]); + + return f_putblock(&bt->f, np->nnum, bp); + +fail: + return -1; +} + +/* + * NAME: btree->readhdr() + * DESCRIPTION: read the header node of a B*-tree + */ +int bt_readhdr(btree *bt) +{ + const byte *ptr; + byte *map = 0; + int i; + unsigned long nnum; + + if (bt_getnode(&bt->hdrnd, bt, 0) == -1) + goto fail; + + if (bt->hdrnd.nd.ndType != ndHdrNode || + bt->hdrnd.nd.ndNRecs != 3 || + bt->hdrnd.roff[0] != 0x00e || + bt->hdrnd.roff[1] != 0x078 || + bt->hdrnd.roff[2] != 0x0f8 || + bt->hdrnd.roff[3] != 0x1f8) + ERROR(EIO, "malformed b*-tree header node"); + + /* read header record */ + + ptr = HFS_NODEREC(bt->hdrnd, 0); + + d_fetchuw(&ptr, &bt->hdr.bthDepth); + d_fetchul(&ptr, &bt->hdr.bthRoot); + d_fetchul(&ptr, &bt->hdr.bthNRecs); + d_fetchul(&ptr, &bt->hdr.bthFNode); + d_fetchul(&ptr, &bt->hdr.bthLNode); + d_fetchuw(&ptr, &bt->hdr.bthNodeSize); + d_fetchuw(&ptr, &bt->hdr.bthKeyLen); + d_fetchul(&ptr, &bt->hdr.bthNNodes); + d_fetchul(&ptr, &bt->hdr.bthFree); + + for (i = 0; i < 76; ++i) + d_fetchsb(&ptr, &bt->hdr.bthResv[i]); + + if (bt->hdr.bthNodeSize != HFS_BLOCKSZ) + ERROR(EINVAL, "unsupported b*-tree node size"); + + /* read map record; construct btree bitmap */ + /* don't set bt->map until we're done, since getnode() checks it */ + + map = ALLOC(byte, HFS_MAP1SZ); + if (map == 0) + ERROR(ENOMEM, 0); + + memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ); + bt->mapsz = HFS_MAP1SZ; + + /* read continuation map records, if any */ + + nnum = bt->hdrnd.nd.ndFLink; + + while (nnum) + { + node n; + byte *newmap; + + if (bt_getnode(&n, bt, nnum) == -1) + goto fail; + + if (n.nd.ndType != ndMapNode || + n.nd.ndNRecs != 1 || + n.roff[0] != 0x00e || + n.roff[1] != 0x1fa) + ERROR(EIO, "malformed b*-tree map node"); + + newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ); + if (newmap == 0) + ERROR(ENOMEM, 0); + + map = newmap; + + memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ); + bt->mapsz += HFS_MAPXSZ; + + nnum = n.nd.ndFLink; + } + + bt->map = map; + + return 0; + +fail: + FREE(map); + return -1; +} + +/* + * NAME: btree->writehdr() + * DESCRIPTION: write the header node of a B*-tree + */ +int bt_writehdr(btree *bt) +{ + byte *ptr, *map; + unsigned long mapsz, nnum; + int i; + + ASSERT(bt->hdrnd.bt == bt && + bt->hdrnd.nnum == 0 && + bt->hdrnd.nd.ndType == ndHdrNode && + bt->hdrnd.nd.ndNRecs == 3); + + ptr = HFS_NODEREC(bt->hdrnd, 0); + + d_storeuw(&ptr, bt->hdr.bthDepth); + d_storeul(&ptr, bt->hdr.bthRoot); + d_storeul(&ptr, bt->hdr.bthNRecs); + d_storeul(&ptr, bt->hdr.bthFNode); + d_storeul(&ptr, bt->hdr.bthLNode); + d_storeuw(&ptr, bt->hdr.bthNodeSize); + d_storeuw(&ptr, bt->hdr.bthKeyLen); + d_storeul(&ptr, bt->hdr.bthNNodes); + d_storeul(&ptr, bt->hdr.bthFree); + + for (i = 0; i < 76; ++i) + d_storesb(&ptr, bt->hdr.bthResv[i]); + + memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ); + + if (bt_putnode(&bt->hdrnd) == -1) + goto fail; + + map = bt->map + HFS_MAP1SZ; + mapsz = bt->mapsz - HFS_MAP1SZ; + + nnum = bt->hdrnd.nd.ndFLink; + + while (mapsz) + { + node n; + + if (nnum == 0) + ERROR(EIO, "truncated b*-tree map"); + + if (bt_getnode(&n, bt, nnum) == -1) + goto fail; + + if (n.nd.ndType != ndMapNode || + n.nd.ndNRecs != 1 || + n.roff[0] != 0x00e || + n.roff[1] != 0x1fa) + ERROR(EIO, "malformed b*-tree map node"); + + memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ); + + if (bt_putnode(&n) == -1) + goto fail; + + map += HFS_MAPXSZ; + mapsz -= HFS_MAPXSZ; + + nnum = n.nd.ndFLink; + } + + bt->flags &= ~HFS_BT_UPDATE_HDR; + + return 0; + +fail: + return -1; +} + +/* High-Level B*-Tree Routines ============================================= */ + +/* + * NAME: btree->space() + * DESCRIPTION: assert space for new records, or extend the file + */ +int bt_space(btree *bt, unsigned int nrecs) +{ + unsigned int nnodes; + long space; + + nnodes = nrecs * (bt->hdr.bthDepth + 1); + + if (nnodes <= bt->hdr.bthFree) + goto done; + + /* make sure the extents tree has room too */ + + if (bt != &bt->f.vol->ext) + { + if (bt_space(&bt->f.vol->ext, 1) == -1) + goto fail; + } + + space = f_alloc(&bt->f); + if (space == -1) + goto fail; + + nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize); + + bt->hdr.bthNNodes += nnodes; + bt->hdr.bthFree += nnodes; + + bt->flags |= HFS_BT_UPDATE_HDR; + + bt->f.vol->flags |= HFS_VOL_UPDATE_ALTMDB; + + while (bt->hdr.bthNNodes > bt->mapsz * 8) + { + byte *newmap; + node mapnd; + + /* extend tree map */ + + newmap = REALLOC(bt->map, byte, bt->mapsz + HFS_MAPXSZ); + if (newmap == 0) + ERROR(ENOMEM, 0); + + memset(newmap + bt->mapsz, 0, HFS_MAPXSZ); + + bt->map = newmap; + bt->mapsz += HFS_MAPXSZ; + + n_init(&mapnd, bt, ndMapNode, 0); + if (n_new(&mapnd) == -1) + goto fail; + + mapnd.nd.ndNRecs = 1; + mapnd.roff[1] = 0x1fa; + + /* link the new map node */ + + if (bt->hdrnd.nd.ndFLink == 0) + { + bt->hdrnd.nd.ndFLink = mapnd.nnum; + mapnd.nd.ndBLink = 0; + } + else + { + node n; + unsigned long nnum; + + nnum = bt->hdrnd.nd.ndFLink; + + while (1) + { + if (bt_getnode(&n, bt, nnum) == -1) + goto fail; + + if (n.nd.ndFLink == 0) + break; + + nnum = n.nd.ndFLink; + } + + n.nd.ndFLink = mapnd.nnum; + mapnd.nd.ndBLink = n.nnum; + + if (bt_putnode(&n) == -1) + goto fail; + } + + if (bt_putnode(&mapnd) == -1) + goto fail; + } + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: insertx() + * DESCRIPTION: recursively locate a node and insert a record + */ +static +int insertx(node *np, byte *record, int *reclen) +{ + node child; + byte *rec; + int result = 0; + + if (n_search(np, record)) + ERROR(EIO, "b*-tree record already exists"); + + switch (np->nd.ndType) + { + case ndIndxNode: + if (np->rnum == -1) + rec = HFS_NODEREC(*np, 0); + else + rec = HFS_NODEREC(*np, np->rnum); + + if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 || + insertx(&child, record, reclen) == -1) + goto fail; + + if (np->rnum == -1) + { + n_index(&child, rec, 0); + if (*reclen == 0) + { + result = bt_putnode(np); + goto done; + } + } + + if (*reclen) + result = n_insert(np, record, reclen); + + break; + + case ndLeafNode: + result = n_insert(np, record, reclen); + break; + + default: + ERROR(EIO, "unexpected b*-tree node"); + } + +done: + return result; + +fail: + return -1; +} + +/* + * NAME: btree->insert() + * DESCRIPTION: insert a new node record into a tree + */ +int bt_insert(btree *bt, const byte *record, unsigned int reclen) +{ + node root; + byte newrec[HFS_MAX_RECLEN]; + + if (bt->hdr.bthRoot == 0) + { + /* create root node */ + + n_init(&root, bt, ndLeafNode, 1); + if (n_new(&root) == -1 || + bt_putnode(&root) == -1) + goto fail; + + bt->hdr.bthDepth = 1; + bt->hdr.bthRoot = root.nnum; + bt->hdr.bthFNode = root.nnum; + bt->hdr.bthLNode = root.nnum; + + bt->flags |= HFS_BT_UPDATE_HDR; + } + else if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1) + goto fail; + + memcpy(newrec, record, reclen); + + if (insertx(&root, newrec, &reclen) == -1) + goto fail; + + if (reclen) + { + byte oroot[HFS_MAX_RECLEN]; + unsigned int orootlen; + + /* root node was split; create a new root */ + + n_index(&root, oroot, &orootlen); + + n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1); + if (n_new(&root) == -1) + goto fail; + + ++bt->hdr.bthDepth; + bt->hdr.bthRoot = root.nnum; + + bt->flags |= HFS_BT_UPDATE_HDR; + + /* insert index records for new root */ + + n_search(&root, oroot); + n_insertx(&root, oroot, orootlen); + + n_search(&root, newrec); + n_insertx(&root, newrec, reclen); + + if (bt_putnode(&root) == -1) + goto fail; + } + + ++bt->hdr.bthNRecs; + bt->flags |= HFS_BT_UPDATE_HDR; + + return 0; + +fail: + return -1; +} + +/* + * NAME: deletex() + * DESCRIPTION: recursively locate a node and delete a record + */ +static +int deletex(node *np, const byte *key, byte *record, int *flag) +{ + node child; + byte *rec; + int found, result = 0; + + found = n_search(np, key); + + switch (np->nd.ndType) + { + case ndIndxNode: + if (np->rnum == -1) + ERROR(EIO, "b*-tree record not found"); + + rec = HFS_NODEREC(*np, np->rnum); + + if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 || + deletex(&child, key, rec, flag) == -1) + goto fail; + + if (*flag) + { + *flag = 0; + + if (HFS_RECKEYLEN(rec) == 0) + { + result = n_delete(np, record, flag); + break; + } + + if (np->rnum == 0) + { + /* propagate index record change into parent */ + + n_index(np, record, 0); + *flag = 1; + } + + result = bt_putnode(np); + } + + break; + + case ndLeafNode: + if (found == 0) + ERROR(EIO, "b*-tree record not found"); + + result = n_delete(np, record, flag); + break; + + default: + ERROR(EIO, "unexpected b*-tree node"); + } + + return result; + +fail: + return -1; +} + +/* + * NAME: btree->delete() + * DESCRIPTION: remove a node record from a tree + */ +int bt_delete(btree *bt, const byte *key) +{ + node root; + byte record[HFS_MAX_RECLEN]; + int flag = 0; + + if (bt->hdr.bthRoot == 0) + ERROR(EIO, "empty b*-tree"); + + if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1 || + deletex(&root, key, record, &flag) == -1) + goto fail; + + if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1) + { + const byte *rec; + + /* root only has one record; eliminate it and decrease the tree depth */ + + rec = HFS_NODEREC(root, 0); + + --bt->hdr.bthDepth; + bt->hdr.bthRoot = d_getul(HFS_RECDATA(rec)); + + if (n_free(&root) == -1) + goto fail; + } + else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0) + { + /* root node was deleted */ + + bt->hdr.bthDepth = 0; + bt->hdr.bthRoot = 0; + } + + --bt->hdr.bthNRecs; + bt->flags |= HFS_BT_UPDATE_HDR; + + return 0; + +fail: + return -1; +} + +/* + * NAME: btree->search() + * DESCRIPTION: locate a data record given a search key + */ +int bt_search(btree *bt, const byte *key, node *np) +{ + int found = 0; + unsigned long nnum; + + nnum = bt->hdr.bthRoot; + + if (nnum == 0) + ERROR(ENOENT, 0); + + while (1) + { + const byte *rec; + + if (bt_getnode(np, bt, nnum) == -1) + { + found = -1; + goto fail; + } + + found = n_search(np, key); + + switch (np->nd.ndType) + { + case ndIndxNode: + if (np->rnum == -1) + ERROR(ENOENT, 0); + + rec = HFS_NODEREC(*np, np->rnum); + nnum = d_getul(HFS_RECDATA(rec)); + + break; + + case ndLeafNode: + if (! found) + ERROR(ENOENT, 0); + + goto done; + + default: + found = -1; + ERROR(EIO, "unexpected b*-tree node"); + } + } + +done: +fail: + return found; +} diff --git a/libhfs/btree.h b/libhfs/btree.h new file mode 100644 index 0000000..b3775a1 --- /dev/null +++ b/libhfs/btree.h @@ -0,0 +1,33 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: btree.h,v 1.8 1998/11/02 22:08:55 rob Exp $ + */ + +int bt_getnode(node *, btree *, unsigned long); +int bt_putnode(node *); + +int bt_readhdr(btree *); +int bt_writehdr(btree *); + +int bt_space(btree *, unsigned int); + +int bt_insert(btree *, const byte *, unsigned int); +int bt_delete(btree *, const byte *); + +int bt_search(btree *, const byte *, node *); diff --git a/libhfs/config.h.in b/libhfs/config.h.in new file mode 100644 index 0000000..0a12e3b --- /dev/null +++ b/libhfs/config.h.in @@ -0,0 +1,57 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +/* Define if you have the mktime function. */ +#undef HAVE_MKTIME + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ + +# ifdef DEBUG +# include +# endif diff --git a/libhfs/configure b/libhfs/configure new file mode 100755 index 0000000..403cc5c --- /dev/null +++ b/libhfs/configure @@ -0,0 +1,1719 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --enable-debug enable diagnostic debugging support" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=libhfs.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + : +fi + + +if test "x$enable_debug" = xyes +then + cat >> confdefs.h <<\EOF +#define DEBUG 1 +EOF + +fi + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:543: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:572: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:601: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:649: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:683: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:688: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:712: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:769: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:821: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 +echo "configure:848: checking whether ln -s works" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + rm -f conftestdata +if ln -s X conftestdata 2>/dev/null +then + rm -f conftestdata + ac_cv_prog_LN_S="ln -s" +else + ac_cv_prog_LN_S=ln +fi +fi +LN_S="$ac_cv_prog_LN_S" +if test "$ac_cv_prog_LN_S" = "ln -s"; then + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:870: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:891: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:908: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:932: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:980: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:993: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1060: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in unistd.h fcntl.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1087: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1097: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1126: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:1159: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:1172: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1194: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1248: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + + + +echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6 +echo "configure:1271: checking for 8-bit clean memcmp" >&5 +if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_clean=no +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_func_memcmp_clean=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_memcmp_clean=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6 +test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.o" + +for ac_func in mktime +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1309: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1337: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@LN_S@%$LN_S%g +s%@CPP@%$CPP%g +s%@LIBOBJS@%$LIBOBJS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/libhfs/configure.in b/libhfs/configure.in new file mode 100644 index 0000000..ecd3e6c --- /dev/null +++ b/libhfs/configure.in @@ -0,0 +1,63 @@ +dnl -*- shell-script -*- +dnl +dnl libhfs - library for reading and writing Macintosh HFS volumes +dnl Copyright (C) 1996-1998 Robert Leslie +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +dnl +dnl $Id: configure.in,v 1.5 1998/04/11 08:27:12 rob Exp $ +dnl + +AC_INIT(libhfs.h) +AC_CONFIG_HEADER(config.h) + +AC_ARG_ENABLE(debug, + [ --enable-debug enable diagnostic debugging support]) + +if test "x$enable_debug" = xyes +then + AC_DEFINE(DEBUG) +fi + +dnl Checks for programs. + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB +AC_PROG_LN_S + +AC_PROG_GCC_TRADITIONAL + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h fcntl.h) + +dnl Checks for typedefs, structures, and compiler characteristics. + +AC_TYPE_SIZE_T +AC_STRUCT_TM + +AC_C_CONST + +dnl Checks for library functions. + +AC_FUNC_MEMCMP +AC_CHECK_FUNCS(mktime) + +dnl Create output files. + +AC_OUTPUT(Makefile) diff --git a/libhfs/data.c b/libhfs/data.c new file mode 100644 index 0000000..0dee6bc --- /dev/null +++ b/libhfs/data.c @@ -0,0 +1,485 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: data.c,v 1.7 1998/11/02 22:08:57 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# ifdef TM_IN_SYS_TIME +# include +# endif + +# include "data.h" + +# define TIMEDIFF 2082844800UL + +static +time_t tzdiff = -1; + +const +unsigned char hfs_charorder[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + 0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + + 0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae, + + 0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + + 0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55, + 0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64, + 0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89, + 0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a, + + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85, + + 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26, + 0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87, + 0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8, + 0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +/* + * NAME: data->getsb() + * DESCRIPTION: marshal 1 signed byte into local host format + */ +signed char d_getsb(register const unsigned char *ptr) +{ + return ptr[0]; +} + +/* + * NAME: data->getub() + * DESCRIPTION: marshal 1 unsigned byte into local host format + */ +unsigned char d_getub(register const unsigned char *ptr) +{ + return ptr[0]; +} + +/* + * NAME: data->getsw() + * DESCRIPTION: marshal 2 signed bytes into local host format + */ +signed short d_getsw(register const unsigned char *ptr) +{ + return + ((( signed short) ptr[0] << 8) | + ((unsigned short) ptr[1] << 0)); +} + +/* + * NAME: data->getuw() + * DESCRIPTION: marshal 2 unsigned bytes into local host format + */ +unsigned short d_getuw(register const unsigned char *ptr) +{ + return + (((unsigned short) ptr[0] << 8) | + ((unsigned short) ptr[1] << 0)); +} + +/* + * NAME: data->getsl() + * DESCRIPTION: marshal 4 signed bytes into local host format + */ +signed long d_getsl(register const unsigned char *ptr) +{ + return + ((( signed long) ptr[0] << 24) | + ((unsigned long) ptr[1] << 16) | + ((unsigned long) ptr[2] << 8) | + ((unsigned long) ptr[3] << 0)); +} + +/* + * NAME: data->getul() + * DESCRIPTION: marshal 4 unsigned bytes into local host format + */ +unsigned long d_getul(register const unsigned char *ptr) +{ + return + (((unsigned long) ptr[0] << 24) | + ((unsigned long) ptr[1] << 16) | + ((unsigned long) ptr[2] << 8) | + ((unsigned long) ptr[3] << 0)); +} + +/* + * NAME: data->putsb() + * DESCRIPTION: marshal 1 signed byte out in big-endian format + */ +void d_putsb(register unsigned char *ptr, + register signed char data) +{ + *ptr = data; +} + +/* + * NAME: data->putub() + * DESCRIPTION: marshal 1 unsigned byte out in big-endian format + */ +void d_putub(register unsigned char *ptr, + register unsigned char data) +{ + *ptr = data; +} + +/* + * NAME: data->putsw() + * DESCRIPTION: marshal 2 signed bytes out in big-endian format + */ +void d_putsw(register unsigned char *ptr, + register signed short data) +{ + *ptr++ = ((unsigned short) data & 0xff00) >> 8; + *ptr = ((unsigned short) data & 0x00ff) >> 0; +} + +/* + * NAME: data->putuw() + * DESCRIPTION: marshal 2 unsigned bytes out in big-endian format + */ +void d_putuw(register unsigned char *ptr, + register unsigned short data) +{ + *ptr++ = (data & 0xff00) >> 8; + *ptr = (data & 0x00ff) >> 0; +} + +/* + * NAME: data->putsl() + * DESCRIPTION: marshal 4 signed bytes out in big-endian format + */ +void d_putsl(register unsigned char *ptr, + register signed long data) +{ + *ptr++ = ((unsigned long) data & 0xff000000UL) >> 24; + *ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16; + *ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8; + *ptr = ((unsigned long) data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->putul() + * DESCRIPTION: marshal 4 unsigned bytes out in big-endian format + */ +void d_putul(register unsigned char *ptr, + register unsigned long data) +{ + *ptr++ = (data & 0xff000000UL) >> 24; + *ptr++ = (data & 0x00ff0000UL) >> 16; + *ptr++ = (data & 0x0000ff00UL) >> 8; + *ptr = (data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->fetchsb() + * DESCRIPTION: incrementally retrieve a signed byte of data + */ +void d_fetchsb(register const unsigned char **ptr, + register signed char *dest) +{ + *dest = *(*ptr)++; +} + +/* + * NAME: data->fetchub() + * DESCRIPTION: incrementally retrieve an unsigned byte of data + */ +void d_fetchub(register const unsigned char **ptr, + register unsigned char *dest) +{ + *dest = *(*ptr)++; +} + +/* + * NAME: data->fetchsw() + * DESCRIPTION: incrementally retrieve a signed word of data + */ +void d_fetchsw(register const unsigned char **ptr, + register signed short *dest) +{ + *dest = + ((( signed short) (*ptr)[0] << 8) | + ((unsigned short) (*ptr)[1] << 0)); + *ptr += 2; +} + +/* + * NAME: data->fetchuw() + * DESCRIPTION: incrementally retrieve an unsigned word of data + */ +void d_fetchuw(register const unsigned char **ptr, + register unsigned short *dest) +{ + *dest = + (((unsigned short) (*ptr)[0] << 8) | + ((unsigned short) (*ptr)[1] << 0)); + *ptr += 2; +} + +/* + * NAME: data->fetchsl() + * DESCRIPTION: incrementally retrieve a signed long word of data + */ +void d_fetchsl(register const unsigned char **ptr, + register signed long *dest) +{ + *dest = + ((( signed long) (*ptr)[0] << 24) | + ((unsigned long) (*ptr)[1] << 16) | + ((unsigned long) (*ptr)[2] << 8) | + ((unsigned long) (*ptr)[3] << 0)); + *ptr += 4; +} + +/* + * NAME: data->fetchul() + * DESCRIPTION: incrementally retrieve an unsigned long word of data + */ +void d_fetchul(register const unsigned char **ptr, + register unsigned long *dest) +{ + *dest = + (((unsigned long) (*ptr)[0] << 24) | + ((unsigned long) (*ptr)[1] << 16) | + ((unsigned long) (*ptr)[2] << 8) | + ((unsigned long) (*ptr)[3] << 0)); + *ptr += 4; +} + +/* + * NAME: data->storesb() + * DESCRIPTION: incrementally store a signed byte of data + */ +void d_storesb(register unsigned char **ptr, + register signed char data) +{ + *(*ptr)++ = data; +} + +/* + * NAME: data->storeub() + * DESCRIPTION: incrementally store an unsigned byte of data + */ +void d_storeub(register unsigned char **ptr, + register unsigned char data) +{ + *(*ptr)++ = data; +} + +/* + * NAME: data->storesw() + * DESCRIPTION: incrementally store a signed word of data + */ +void d_storesw(register unsigned char **ptr, + register signed short data) +{ + *(*ptr)++ = ((unsigned short) data & 0xff00) >> 8; + *(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0; +} + +/* + * NAME: data->storeuw() + * DESCRIPTION: incrementally store an unsigned word of data + */ +void d_storeuw(register unsigned char **ptr, + register unsigned short data) +{ + *(*ptr)++ = (data & 0xff00) >> 8; + *(*ptr)++ = (data & 0x00ff) >> 0; +} + +/* + * NAME: data->storesl() + * DESCRIPTION: incrementally store a signed long word of data + */ +void d_storesl(register unsigned char **ptr, + register signed long data) +{ + *(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24; + *(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16; + *(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8; + *(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->storeul() + * DESCRIPTION: incrementally store an unsigned long word of data + */ +void d_storeul(register unsigned char **ptr, + register unsigned long data) +{ + *(*ptr)++ = (data & 0xff000000UL) >> 24; + *(*ptr)++ = (data & 0x00ff0000UL) >> 16; + *(*ptr)++ = (data & 0x0000ff00UL) >> 8; + *(*ptr)++ = (data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->fetchstr() + * DESCRIPTION: incrementally retrieve a string + */ +void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size) +{ + unsigned len; + + len = d_getub(*ptr); + + if (len > 0 && len < size) + memcpy(dest, *ptr + 1, len); + else + len = 0; + + dest[len] = 0; + + *ptr += size; +} + +/* + * NAME: data->storestr() + * DESCRIPTION: incrementally store a string + */ +void d_storestr(unsigned char **ptr, const char *src, unsigned size) +{ + unsigned len; + + len = strlen(src); + if (len > --size) + len = 0; + + d_storeub(ptr, len); + + memcpy(*ptr, src, len); + memset(*ptr + len, 0, size - len); + + *ptr += size; +} + +/* + * NAME: data->relstring() + * DESCRIPTION: compare two strings as per MacOS for HFS + */ +int d_relstring(const char *str1, const char *str2) +{ + register int diff; + + while (*str1 && *str2) + { + diff = hfs_charorder[(unsigned char) *str1] - + hfs_charorder[(unsigned char) *str2]; + + if (diff) + return diff; + + ++str1, ++str2; + } + + if (! *str1 && *str2) + return -1; + else if (*str1 && ! *str2) + return 1; + + return 0; +} + +/* + * NAME: calctzdiff() + * DESCRIPTION: calculate the timezone difference between local time and UTC + */ +static +void calctzdiff(void) +{ +# ifdef HAVE_MKTIME + + time_t t; + int isdst; + struct tm tm; + const struct tm *tmp; + + time(&t); + isdst = localtime(&t)->tm_isdst; + + tmp = gmtime(&t); + if (tmp) + { + tm = *tmp; + tm.tm_isdst = isdst; + + tzdiff = t - mktime(&tm); + } + else + tzdiff = 0; + +# else + + tzdiff = 0; + +# endif +} + +/* + * NAME: data->ltime() + * DESCRIPTION: convert MacOS time to local time + */ +time_t d_ltime(unsigned long mtime) +{ + if (tzdiff == -1) + calctzdiff(); + + return (time_t) (mtime - TIMEDIFF) - tzdiff; +} + +/* + * NAME: data->mtime() + * DESCRIPTION: convert local time to MacOS time + */ +unsigned long d_mtime(time_t ltime) +{ + if (tzdiff == -1) + calctzdiff(); + + return (unsigned long) (ltime + tzdiff) + TIMEDIFF; +} diff --git a/libhfs/data.h b/libhfs/data.h new file mode 100644 index 0000000..fd38e75 --- /dev/null +++ b/libhfs/data.h @@ -0,0 +1,58 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: data.h,v 1.7 1998/11/02 22:08:58 rob Exp $ + */ + +extern const unsigned char hfs_charorder[]; + + signed char d_getsb(register const unsigned char *); +unsigned char d_getub(register const unsigned char *); + signed short d_getsw(register const unsigned char *); +unsigned short d_getuw(register const unsigned char *); + signed long d_getsl(register const unsigned char *); +unsigned long d_getul(register const unsigned char *); + +void d_putsb(register unsigned char *, register signed char); +void d_putub(register unsigned char *, register unsigned char); +void d_putsw(register unsigned char *, register signed short); +void d_putuw(register unsigned char *, register unsigned short); +void d_putsl(register unsigned char *, register signed long); +void d_putul(register unsigned char *, register unsigned long); + +void d_fetchsb(register const unsigned char **, register signed char *); +void d_fetchub(register const unsigned char **, register unsigned char *); +void d_fetchsw(register const unsigned char **, register signed short *); +void d_fetchuw(register const unsigned char **, register unsigned short *); +void d_fetchsl(register const unsigned char **, register signed long *); +void d_fetchul(register const unsigned char **, register unsigned long *); + +void d_storesb(register unsigned char **, register signed char); +void d_storeub(register unsigned char **, register unsigned char); +void d_storesw(register unsigned char **, register signed short); +void d_storeuw(register unsigned char **, register unsigned short); +void d_storesl(register unsigned char **, register signed long); +void d_storeul(register unsigned char **, register unsigned long); + +void d_fetchstr(const unsigned char **, char *, unsigned); +void d_storestr(unsigned char **, const char *, unsigned); + +int d_relstring(const char *, const char *); + +time_t d_ltime(unsigned long); +unsigned long d_mtime(time_t); diff --git a/libhfs/file.c b/libhfs/file.c new file mode 100644 index 0000000..3fcc435 --- /dev/null +++ b/libhfs/file.c @@ -0,0 +1,520 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: file.c,v 1.9 1998/11/02 22:08:59 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# include "libhfs.h" +# include "file.h" +# include "btree.h" +# include "record.h" +# include "volume.h" + +/* + * NAME: file->init() + * DESCRIPTION: initialize file structure + */ +void f_init(hfsfile *file, hfsvol *vol, long cnid, const char *name) +{ + int i; + + file->vol = vol; + file->parid = 0; + + strcpy(file->name, name); + + file->cat.cdrType = cdrFilRec; + file->cat.cdrResrv2 = 0; + + file->cat.u.fil.filFlags = 0; + file->cat.u.fil.filTyp = 0; + + file->cat.u.fil.filUsrWds.fdType = 0; + file->cat.u.fil.filUsrWds.fdCreator = 0; + file->cat.u.fil.filUsrWds.fdFlags = 0; + file->cat.u.fil.filUsrWds.fdLocation.v = 0; + file->cat.u.fil.filUsrWds.fdLocation.h = 0; + file->cat.u.fil.filUsrWds.fdFldr = 0; + + file->cat.u.fil.filFlNum = cnid; + file->cat.u.fil.filStBlk = 0; + file->cat.u.fil.filLgLen = 0; + file->cat.u.fil.filPyLen = 0; + file->cat.u.fil.filRStBlk = 0; + file->cat.u.fil.filRLgLen = 0; + file->cat.u.fil.filRPyLen = 0; + file->cat.u.fil.filCrDat = 0; + file->cat.u.fil.filMdDat = 0; + file->cat.u.fil.filBkDat = 0; + + file->cat.u.fil.filFndrInfo.fdIconID = 0; + for (i = 0; i < 4; ++i) + file->cat.u.fil.filFndrInfo.fdUnused[i] = 0; + file->cat.u.fil.filFndrInfo.fdComment = 0; + file->cat.u.fil.filFndrInfo.fdPutAway = 0; + + file->cat.u.fil.filClpSize = 0; + + for (i = 0; i < 3; ++i) + { + file->cat.u.fil.filExtRec[i].xdrStABN = 0; + file->cat.u.fil.filExtRec[i].xdrNumABlks = 0; + + file->cat.u.fil.filRExtRec[i].xdrStABN = 0; + file->cat.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + + file->cat.u.fil.filResrv = 0; + + f_selectfork(file, fkData); + + file->flags = 0; + + file->prev = 0; + file->next = 0; +} + +/* + * NAME: file->selectfork() + * DESCRIPTION: choose a fork for file operations + */ +void f_selectfork(hfsfile *file, int fork) +{ + file->fork = fork; + + memcpy(&file->ext, fork == fkData ? + &file->cat.u.fil.filExtRec : &file->cat.u.fil.filRExtRec, + sizeof(ExtDataRec)); + + file->fabn = 0; + file->pos = 0; +} + +/* + * NAME: file->getptrs() + * DESCRIPTION: make pointers to the current fork's lengths and extents + */ +void f_getptrs(hfsfile *file, ExtDataRec **extrec, + unsigned long **lglen, unsigned long **pylen) +{ + if (file->fork == fkData) + { + if (extrec) + *extrec = &file->cat.u.fil.filExtRec; + if (lglen) + *lglen = &file->cat.u.fil.filLgLen; + if (pylen) + *pylen = &file->cat.u.fil.filPyLen; + } + else + { + if (extrec) + *extrec = &file->cat.u.fil.filRExtRec; + if (lglen) + *lglen = &file->cat.u.fil.filRLgLen; + if (pylen) + *pylen = &file->cat.u.fil.filRPyLen; + } +} + +/* + * NAME: file->doblock() + * DESCRIPTION: read or write a numbered block from a file + */ +int f_doblock(hfsfile *file, unsigned long num, block *bp, + int (*func)(hfsvol *, unsigned int, unsigned int, block *)) +{ + unsigned int abnum; + unsigned int blnum; + unsigned int fabn; + int i; + + abnum = num / file->vol->lpa; + blnum = num % file->vol->lpa; + + /* locate the appropriate extent record */ + + fabn = file->fabn; + + if (abnum < fabn) + { + ExtDataRec *extrec; + + f_getptrs(file, &extrec, 0, 0); + + fabn = file->fabn = 0; + memcpy(&file->ext, extrec, sizeof(ExtDataRec)); + } + else + abnum -= fabn; + + while (1) + { + unsigned int n; + + for (i = 0; i < 3; ++i) + { + n = file->ext[i].xdrNumABlks; + + if (abnum < n) + return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp); + + fabn += n; + abnum -= n; + } + + if (v_extsearch(file, fabn, &file->ext, 0) <= 0) + goto fail; + + file->fabn = fabn; + } + +fail: + return -1; +} + +/* + * NAME: file->addextent() + * DESCRIPTION: add an extent to a file + */ +int f_addextent(hfsfile *file, ExtDescriptor *blocks) +{ + hfsvol *vol = file->vol; + ExtDataRec *extrec; + unsigned long *pylen; + unsigned int start, end; + node n; + int i; + + f_getptrs(file, &extrec, 0, &pylen); + + start = file->fabn; + end = *pylen / vol->mdb.drAlBlkSiz; + + n.nnum = 0; + i = -1; + + while (start < end) + { + for (i = 0; i < 3; ++i) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (start == end) + break; + else if (start > end) + ERROR(EIO, "file extents exceed file physical length"); + else if (num == 0) + ERROR(EIO, "empty file extent"); + } + + if (start == end) + break; + + if (v_extsearch(file, start, &file->ext, &n) <= 0) + goto fail; + + file->fabn = start; + } + + if (i >= 0 && + file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks->xdrStABN) + file->ext[i].xdrNumABlks += blocks->xdrNumABlks; + else + { + /* create a new extent descriptor */ + + if (++i < 3) + file->ext[i] = *blocks; + else + { + ExtKeyRec key; + byte record[HFS_MAX_EXTRECLEN]; + unsigned int reclen; + + /* record is full; create a new one */ + + file->ext[0] = *blocks; + + for (i = 1; i < 3; ++i) + { + file->ext[i].xdrStABN = 0; + file->ext[i].xdrNumABlks = 0; + } + + file->fabn = start; + + r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end); + r_packextrec(&key, &file->ext, record, &reclen); + + if (bt_insert(&vol->ext, record, reclen) == -1) + goto fail; + + i = -1; + } + } + + if (i >= 0) + { + /* store the modified extent record */ + + if (file->fabn) + { + if ((n.nnum == 0 && + v_extsearch(file, file->fabn, 0, &n) <= 0) || + v_putextrec(&file->ext, &n) == -1) + goto fail; + } + else + memcpy(extrec, &file->ext, sizeof(ExtDataRec)); + } + + *pylen += blocks->xdrNumABlks * vol->mdb.drAlBlkSiz; + + file->flags |= HFS_FILE_UPDATE_CATREC; + + return 0; + +fail: + return -1; +} + +/* + * NAME: file->alloc() + * DESCRIPTION: reserve allocation blocks for a file + */ +long f_alloc(hfsfile *file) +{ + hfsvol *vol = file->vol; + unsigned long clumpsz; + ExtDescriptor blocks; + + clumpsz = file->cat.u.fil.filClpSize; + if (clumpsz == 0) + { + if (file == &vol->ext.f) + clumpsz = vol->mdb.drXTClpSiz; + else if (file == &vol->cat.f) + clumpsz = vol->mdb.drCTClpSiz; + else + clumpsz = vol->mdb.drClpSiz; + } + + blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz; + + if (v_allocblocks(vol, &blocks) == -1) + goto fail; + + if (f_addextent(file, &blocks) == -1) + { + v_freeblocks(vol, &blocks); + goto fail; + } + + return blocks.xdrNumABlks; + +fail: + return -1; +} + +/* + * NAME: file->trunc() + * DESCRIPTION: release allocation blocks unneeded by a file + */ +int f_trunc(hfsfile *file) +{ + hfsvol *vol = file->vol; + ExtDataRec *extrec; + unsigned long *lglen, *pylen, alblksz, newpylen; + unsigned int dlen, start, end; + node n; + int i; + + if (vol->flags & HFS_VOL_READONLY) + goto done; + + f_getptrs(file, &extrec, &lglen, &pylen); + + alblksz = vol->mdb.drAlBlkSiz; + newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz; + + if (newpylen > *pylen) + ERROR(EIO, "file size exceeds physical length"); + else if (newpylen == *pylen) + goto done; + + dlen = (*pylen - newpylen) / alblksz; + + start = file->fabn; + end = newpylen / alblksz; + + if (start >= end) + { + start = file->fabn = 0; + memcpy(&file->ext, extrec, sizeof(ExtDataRec)); + } + + n.nnum = 0; + i = -1; + + while (start < end) + { + for (i = 0; i < 3; ++i) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (start >= end) + break; + else if (num == 0) + ERROR(EIO, "empty file extent"); + } + + if (start >= end) + break; + + if (v_extsearch(file, start, &file->ext, &n) <= 0) + goto fail; + + file->fabn = start; + } + + if (start > end) + { + ExtDescriptor blocks; + + file->ext[i].xdrNumABlks -= start - end; + dlen -= start - end; + + blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks; + blocks.xdrNumABlks = start - end; + + if (v_freeblocks(vol, &blocks) == -1) + goto fail; + } + + *pylen = newpylen; + + file->flags |= HFS_FILE_UPDATE_CATREC; + + do + { + while (dlen && ++i < 3) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (num == 0) + ERROR(EIO, "empty file extent"); + else if (num > dlen) + ERROR(EIO, "file extents exceed physical size"); + + dlen -= num; + + if (v_freeblocks(vol, &file->ext[i]) == -1) + goto fail; + + file->ext[i].xdrStABN = 0; + file->ext[i].xdrNumABlks = 0; + } + + if (file->fabn) + { + if (n.nnum == 0 && + v_extsearch(file, file->fabn, 0, &n) <= 0) + goto fail; + + if (file->ext[0].xdrNumABlks) + { + if (v_putextrec(&file->ext, &n) == -1) + goto fail; + } + else + { + if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1) + goto fail; + + n.nnum = 0; + } + } + else + memcpy(extrec, &file->ext, sizeof(ExtDataRec)); + + if (dlen) + { + if (v_extsearch(file, start, &file->ext, &n) <= 0) + goto fail; + + file->fabn = start; + i = -1; + } + } + while (dlen); + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: file->flush() + * DESCRIPTION: flush all pending changes to an open file + */ +int f_flush(hfsfile *file) +{ + hfsvol *vol = file->vol; + + if (vol->flags & HFS_VOL_READONLY) + goto done; + + if (file->flags & HFS_FILE_UPDATE_CATREC) + { + node n; + + file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; + file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; + + if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 || + v_putcatrec(&file->cat, &n) == -1) + goto fail; + + file->flags &= ~HFS_FILE_UPDATE_CATREC; + } + +done: + return 0; + +fail: + return -1; +} diff --git a/libhfs/file.h b/libhfs/file.h new file mode 100644 index 0000000..8bd66bb --- /dev/null +++ b/libhfs/file.h @@ -0,0 +1,45 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: file.h,v 1.6 1998/04/11 08:27:12 rob Exp $ + */ + +enum { + fkData = 0x00, + fkRsrc = 0xff +}; + +void f_init(hfsfile *, hfsvol *, long, const char *); +void f_selectfork(hfsfile *, int); +void f_getptrs(hfsfile *, ExtDataRec **, unsigned long **, unsigned long **); + +int f_doblock(hfsfile *, unsigned long, block *, + int (*)(hfsvol *, unsigned int, unsigned int, block *)); + +# define f_getblock(file, num, bp) \ + f_doblock((file), (num), (bp), b_readab) +# define f_putblock(file, num, bp) \ + f_doblock((file), (num), (bp), \ + (int (*)(hfsvol *, unsigned int, unsigned int, block *)) \ + b_writeab) + +int f_addextent(hfsfile *, ExtDescriptor *); +long f_alloc(hfsfile *); + +int f_trunc(hfsfile *); +int f_flush(hfsfile *); diff --git a/libhfs/hfs.c b/libhfs/hfs.c new file mode 100644 index 0000000..f674cce --- /dev/null +++ b/libhfs/hfs.c @@ -0,0 +1,1848 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include +# include + +# include "libhfs.h" +# include "data.h" +# include "block.h" +# include "medium.h" +# include "file.h" +# include "btree.h" +# include "node.h" +# include "record.h" +# include "volume.h" + +const char *hfs_error = "no error"; /* static error string */ + +hfsvol *hfs_mounts; /* linked list of mounted volumes */ + +static +hfsvol *curvol; /* current volume */ + +/* + * NAME: validvname() + * DESCRIPTION: return true if parameter is a valid volume name + */ +static +int validvname(const char *name) +{ + int len; + + len = strlen(name); + if (len < 1) + ERROR(EINVAL, "volume name cannot be empty"); + else if (len > HFS_MAX_VLEN) + ERROR(ENAMETOOLONG, + "volume name can be at most " STR(HFS_MAX_VLEN) " chars"); + + if (strchr(name, ':')) + ERROR(EINVAL, "volume name may not contain colons"); + + return 1; + +fail: + return 0; +} + +/* + * NAME: getvol() + * DESCRIPTION: validate a volume reference + */ +static +int getvol(hfsvol **vol) +{ + if (*vol == 0) + { + if (curvol == 0) + ERROR(EINVAL, "no volume is current"); + + *vol = curvol; + } + + return 0; + +fail: + return -1; +} + +/* High-Level Volume Routines ============================================== */ + +/* + * NAME: hfs->mount() + * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) + */ +hfsvol *hfs_mount(const char *path, int pnum, int mode) +{ + hfsvol *vol, *check; + + /* see if the volume is already mounted */ + + for (check = hfs_mounts; check; check = check->next) + { + if (check->pnum == pnum && v_same(check, path) == 1) + { + /* verify compatible read/write mode */ + + if (((check->flags & HFS_VOL_READONLY) && + ! (mode & HFS_MODE_RDWR)) || + (! (check->flags & HFS_VOL_READONLY) && + (mode & (HFS_MODE_RDWR | HFS_MODE_ANY)))) + { + vol = check; + goto done; + } + } + } + + vol = ALLOC(hfsvol, 1); + if (vol == 0) + ERROR(ENOMEM, 0); + + v_init(vol, mode); + + /* open the medium */ + + switch (mode & HFS_MODE_MASK) + { + case HFS_MODE_RDWR: + case HFS_MODE_ANY: + if (v_open(vol, path, HFS_MODE_RDWR) != -1) + break; + + if ((mode & HFS_MODE_MASK) == HFS_MODE_RDWR) + goto fail; + + case HFS_MODE_RDONLY: + default: + vol->flags |= HFS_VOL_READONLY; + + if (v_open(vol, path, HFS_MODE_RDONLY) == -1) + goto fail; + } + + /* mount the volume */ + + if (v_geometry(vol, pnum) == -1 || + v_mount(vol) == -1) + goto fail; + + /* add to linked list of volumes */ + + vol->prev = 0; + vol->next = hfs_mounts; + + if (hfs_mounts) + hfs_mounts->prev = vol; + + hfs_mounts = vol; + +done: + ++vol->refs; + curvol = vol; + + return vol; + +fail: + if (vol) + { + v_close(vol); + FREE(vol); + } + + return 0; +} + +/* + * NAME: hfs->flush() + * DESCRIPTION: flush all pending changes to an HFS volume + */ +int hfs_flush(hfsvol *vol) +{ + hfsfile *file; + + if (getvol(&vol) == -1) + goto fail; + + for (file = vol->files; file; file = file->next) + { + if (f_flush(file) == -1) + goto fail; + } + + if (v_flush(vol) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->flushall() + * DESCRIPTION: flush all pending changes to all mounted HFS volumes + */ +void hfs_flushall(void) +{ + hfsvol *vol; + + for (vol = hfs_mounts; vol; vol = vol->next) + hfs_flush(vol); +} + +/* + * NAME: hfs->umount() + * DESCRIPTION: close an HFS volume + */ +int hfs_umount(hfsvol *vol) +{ + int result = 0; + + if (getvol(&vol) == -1) + goto fail; + + if (--vol->refs) + { + result = v_flush(vol); + goto done; + } + + /* close all open files and directories */ + + while (vol->files) + { + if (hfs_close(vol->files) == -1) + result = -1; + } + + while (vol->dirs) + { + if (hfs_closedir(vol->dirs) == -1) + result = -1; + } + + /* close medium */ + + if (v_close(vol) == -1) + result = -1; + + /* remove from linked list of volumes */ + + if (vol->prev) + vol->prev->next = vol->next; + if (vol->next) + vol->next->prev = vol->prev; + + if (vol == hfs_mounts) + hfs_mounts = vol->next; + if (vol == curvol) + curvol = 0; + + FREE(vol); + +done: + return result; + +fail: + return -1; +} + +/* + * NAME: hfs->umountall() + * DESCRIPTION: unmount all mounted volumes + */ +void hfs_umountall(void) +{ + while (hfs_mounts) + hfs_umount(hfs_mounts); +} + +/* + * NAME: hfs->getvol() + * DESCRIPTION: return a pointer to a mounted volume + */ +hfsvol *hfs_getvol(const char *name) +{ + hfsvol *vol; + + if (name == 0) + return curvol; + + for (vol = hfs_mounts; vol; vol = vol->next) + { + if (d_relstring(name, vol->mdb.drVN) == 0) + return vol; + } + + return 0; +} + +/* + * NAME: hfs->setvol() + * DESCRIPTION: change the current volume + */ +void hfs_setvol(hfsvol *vol) +{ + curvol = vol; +} + +/* + * NAME: hfs->vstat() + * DESCRIPTION: return volume statistics + */ +int hfs_vstat(hfsvol *vol, hfsvolent *ent) +{ + if (getvol(&vol) == -1) + goto fail; + + strcpy(ent->name, vol->mdb.drVN); + + ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0; + + ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; + ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; + + ent->alblocksz = vol->mdb.drAlBlkSiz; + ent->clumpsz = vol->mdb.drClpSiz; + + ent->numfiles = vol->mdb.drFilCnt; + ent->numdirs = vol->mdb.drDirCnt; + + ent->crdate = d_ltime(vol->mdb.drCrDate); + ent->mddate = d_ltime(vol->mdb.drLsMod); + ent->bkdate = d_ltime(vol->mdb.drVolBkUp); + + ent->blessed = vol->mdb.drFndrInfo[0]; + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->vsetattr() + * DESCRIPTION: change volume attributes + */ +int hfs_vsetattr(hfsvol *vol, hfsvolent *ent) +{ + if (getvol(&vol) == -1) + goto fail; + + if (ent->clumpsz % vol->mdb.drAlBlkSiz != 0) + ERROR(EINVAL, "illegal clump size"); + + /* make sure "blessed" folder exists */ + + if (ent->blessed && + v_getdthread(vol, ent->blessed, 0, 0) <= 0) + ERROR(EINVAL, "illegal blessed folder"); + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + vol->mdb.drClpSiz = ent->clumpsz; + + vol->mdb.drCrDate = d_mtime(ent->crdate); + vol->mdb.drLsMod = d_mtime(ent->mddate); + vol->mdb.drVolBkUp = d_mtime(ent->bkdate); + + vol->mdb.drFndrInfo[0] = ent->blessed; + + vol->flags |= HFS_VOL_UPDATE_MDB; + + return 0; + +fail: + return -1; +} + +/* High-Level Directory Routines =========================================== */ + +/* + * NAME: hfs->chdir() + * DESCRIPTION: change current HFS directory + */ +int hfs_chdir(hfsvol *vol, const char *path) +{ + CatDataRec data; + + if (getvol(&vol) == -1 || + v_resolve(&vol, path, &data, 0, 0, 0) <= 0) + goto fail; + + if (data.cdrType != cdrDirRec) + ERROR(ENOTDIR, 0); + + vol->cwd = data.u.dir.dirDirID; + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->getcwd() + * DESCRIPTION: return the current working directory ID + */ +unsigned long hfs_getcwd(hfsvol *vol) +{ + if (getvol(&vol) == -1) + return 0; + + return vol->cwd; +} + +/* + * NAME: hfs->setcwd() + * DESCRIPTION: set the current working directory ID + */ +int hfs_setcwd(hfsvol *vol, unsigned long id) +{ + if (getvol(&vol) == -1) + goto fail; + + if (id == vol->cwd) + goto done; + + /* make sure the directory exists */ + + if (v_getdthread(vol, id, 0, 0) <= 0) + goto fail; + + vol->cwd = id; + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->dirinfo() + * DESCRIPTION: given a directory ID, return its (name and) parent ID + */ +int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name) +{ + CatDataRec thread; + + if (getvol(&vol) == -1 || + v_getdthread(vol, *id, &thread, 0) <= 0) + goto fail; + + *id = thread.u.dthd.thdParID; + + if (name) + strcpy(name, thread.u.dthd.thdCName); + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->opendir() + * DESCRIPTION: prepare to read the contents of a directory + */ +hfsdir *hfs_opendir(hfsvol *vol, const char *path) +{ + hfsdir *dir = 0; + CatKeyRec key; + CatDataRec data; + byte pkey[HFS_CATKEYLEN]; + + if (getvol(&vol) == -1) + goto fail; + + dir = ALLOC(hfsdir, 1); + if (dir == 0) + ERROR(ENOMEM, 0); + + dir->vol = vol; + + if (*path == 0) + { + /* meta-directory containing root dirs from all mounted volumes */ + + dir->dirid = 0; + dir->vptr = hfs_mounts; + } + else + { + if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) + goto fail; + + if (data.cdrType != cdrDirRec) + ERROR(ENOTDIR, 0); + + dir->dirid = data.u.dir.dirDirID; + dir->vptr = 0; + + r_makecatkey(&key, dir->dirid, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_search(&vol->cat, pkey, &dir->n) <= 0) + goto fail; + } + + dir->prev = 0; + dir->next = vol->dirs; + + if (vol->dirs) + vol->dirs->prev = dir; + + vol->dirs = dir; + + return dir; + +fail: + FREE(dir); + return 0; +} + +/* + * NAME: hfs->readdir() + * DESCRIPTION: return the next entry in the directory + */ +int hfs_readdir(hfsdir *dir, hfsdirent *ent) +{ + CatKeyRec key; + CatDataRec data; + const byte *ptr; + + if (dir->dirid == 0) + { + hfsvol *vol; + char cname[HFS_MAX_FLEN + 1]; + + for (vol = hfs_mounts; vol; vol = vol->next) + { + if (vol == dir->vptr) + break; + } + + if (vol == 0) + ERROR(ENOENT, "no more entries"); + + if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 || + v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, + &data, cname, 0) <= 0) + goto fail; + + r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); + + dir->vptr = vol->next; + + goto done; + } + + if (dir->n.rnum == -1) + ERROR(ENOENT, "no more entries"); + + while (1) + { + ++dir->n.rnum; + + while (dir->n.rnum >= dir->n.nd.ndNRecs) + { + if (dir->n.nd.ndFLink == 0) + { + dir->n.rnum = -1; + ERROR(ENOENT, "no more entries"); + } + + if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1) + { + dir->n.rnum = -1; + goto fail; + } + + dir->n.rnum = 0; + } + + ptr = HFS_NODEREC(dir->n, dir->n.rnum); + + r_unpackcatkey(ptr, &key); + + if (key.ckrParID != dir->dirid) + { + dir->n.rnum = -1; + ERROR(ENOENT, "no more entries"); + } + + r_unpackcatdata(HFS_RECDATA(ptr), &data); + + switch (data.cdrType) + { + case cdrDirRec: + case cdrFilRec: + r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); + goto done; + + case cdrThdRec: + case cdrFThdRec: + break; + + default: + dir->n.rnum = -1; + ERROR(EIO, "unexpected directory entry found"); + } + } + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->closedir() + * DESCRIPTION: stop reading a directory + */ +int hfs_closedir(hfsdir *dir) +{ + hfsvol *vol = dir->vol; + + if (dir->prev) + dir->prev->next = dir->next; + if (dir->next) + dir->next->prev = dir->prev; + if (dir == vol->dirs) + vol->dirs = dir->next; + + FREE(dir); + + return 0; +} + +/* High-Level File Routines ================================================ */ + +/* + * NAME: hfs->create() + * DESCRIPTION: create and open a new file + */ +hfsfile *hfs_create(hfsvol *vol, const char *path, + const char *type, const char *creator) +{ + hfsfile *file = 0; + unsigned long parid; + char name[HFS_MAX_FLEN + 1]; + CatKeyRec key; + byte record[HFS_MAX_CATRECLEN]; + unsigned reclen; + int found; + + if (getvol(&vol) == -1) + goto fail; + + file = ALLOC(hfsfile, 1); + if (file == 0) + ERROR(ENOMEM, 0); + + found = v_resolve(&vol, path, &file->cat, &parid, name, 0); + if (found == -1 || parid == 0) + goto fail; + + if (found) + ERROR(EEXIST, 0); + + if (parid == HFS_CNID_ROOTPAR) + ERROR(EINVAL, 0); + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + /* create file `name' in parent `parid' */ + + if (bt_space(&vol->cat, 1) == -1) + goto fail; + + f_init(file, vol, vol->mdb.drNxtCNID++, name); + vol->flags |= HFS_VOL_UPDATE_MDB; + + file->parid = parid; + + /* create catalog record */ + + file->cat.u.fil.filUsrWds.fdType = + d_getsl((const unsigned char *) type); + file->cat.u.fil.filUsrWds.fdCreator = + d_getsl((const unsigned char *) creator); + + file->cat.u.fil.filCrDat = d_mtime(time(0)); + file->cat.u.fil.filMdDat = file->cat.u.fil.filCrDat; + + r_makecatkey(&key, file->parid, file->name); + r_packcatrec(&key, &file->cat, record, &reclen); + + if (bt_insert(&vol->cat, record, reclen) == -1 || + v_adjvalence(vol, file->parid, 0, 1) == -1) + goto fail; + + /* package file handle for user */ + + file->next = vol->files; + + if (vol->files) + vol->files->prev = file; + + vol->files = file; + + return file; + +fail: + FREE(file); + return 0; +} + +/* + * NAME: hfs->open() + * DESCRIPTION: prepare a file for I/O + */ +hfsfile *hfs_open(hfsvol *vol, const char *path) +{ + hfsfile *file = 0; + + if (getvol(&vol) == -1) + goto fail; + + file = ALLOC(hfsfile, 1); + if (file == 0) + ERROR(ENOMEM, 0); + + if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) + goto fail; + + if (file->cat.cdrType != cdrFilRec) + ERROR(EISDIR, 0); + + /* package file handle for user */ + + file->vol = vol; + file->flags = 0; + + f_selectfork(file, fkData); + + file->prev = 0; + file->next = vol->files; + + if (vol->files) + vol->files->prev = file; + + vol->files = file; + + return file; + +fail: + FREE(file); + return 0; +} + +/* + * NAME: hfs->setfork() + * DESCRIPTION: select file fork for I/O operations + */ +int hfs_setfork(hfsfile *file, int fork) +{ + int result = 0; + + if (f_trunc(file) == -1) + result = -1; + + f_selectfork(file, fork ? fkRsrc : fkData); + + return result; +} + +/* + * NAME: hfs->getfork() + * DESCRIPTION: return the current fork for I/O operations + */ +int hfs_getfork(hfsfile *file) +{ + return file->fork != fkData; +} + +/* + * NAME: hfs->read() + * DESCRIPTION: read from an open file + */ +unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len) +{ + unsigned long *lglen, count; + byte *ptr = buf; + + f_getptrs(file, 0, &lglen, 0); + + if (file->pos + len > *lglen) + len = *lglen - file->pos; + + count = len; + while (count) + { + unsigned long bnum, offs, chunk; + + bnum = file->pos >> HFS_BLOCKSZ_BITS; + offs = file->pos & (HFS_BLOCKSZ - 1); + + chunk = HFS_BLOCKSZ - offs; + if (chunk > count) + chunk = count; + + if (offs == 0 && chunk == HFS_BLOCKSZ) + { + if (f_getblock(file, bnum, (block *) ptr) == -1) + goto fail; + } + else + { + block b; + + if (f_getblock(file, bnum, &b) == -1) + goto fail; + + memcpy(ptr, b + offs, chunk); + } + + ptr += chunk; + + file->pos += chunk; + count -= chunk; + } + + return len; + +fail: + return -1; +} + +/* + * NAME: hfs->write() + * DESCRIPTION: write to an open file + */ +unsigned long hfs_write(hfsfile *file, const void *buf, unsigned long len) +{ + unsigned long *lglen, *pylen, count; + const byte *ptr = buf; + + if (file->vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + f_getptrs(file, 0, &lglen, &pylen); + + count = len; + + /* set flag to update (at least) the modification time */ + + if (count) + { + file->cat.u.fil.filMdDat = d_mtime(time(0)); + file->flags |= HFS_FILE_UPDATE_CATREC; + } + + while (count) + { + unsigned long bnum, offs, chunk; + + bnum = file->pos >> HFS_BLOCKSZ_BITS; + offs = file->pos & (HFS_BLOCKSZ - 1); + + chunk = HFS_BLOCKSZ - offs; + if (chunk > count) + chunk = count; + + if (file->pos + chunk > *pylen) + { + if (bt_space(&file->vol->ext, 1) == -1 || + f_alloc(file) == -1) + goto fail; + } + + if (offs == 0 && chunk == HFS_BLOCKSZ) + { + if (f_putblock(file, bnum, (block *) ptr) == -1) + goto fail; + } + else + { + block b; + + if (f_getblock(file, bnum, &b) == -1) + goto fail; + + memcpy(b + offs, ptr, chunk); + + if (f_putblock(file, bnum, &b) == -1) + goto fail; + } + + ptr += chunk; + + file->pos += chunk; + count -= chunk; + + if (file->pos > *lglen) + *lglen = file->pos; + } + + return len; + +fail: + return -1; +} + +/* + * NAME: hfs->truncate() + * DESCRIPTION: truncate an open file + */ +int hfs_truncate(hfsfile *file, unsigned long len) +{ + unsigned long *lglen; + + f_getptrs(file, 0, &lglen, 0); + + if (*lglen > len) + { + if (file->vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + *lglen = len; + + file->cat.u.fil.filMdDat = d_mtime(time(0)); + file->flags |= HFS_FILE_UPDATE_CATREC; + + if (file->pos > len) + file->pos = len; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->seek() + * DESCRIPTION: change file seek pointer + */ +unsigned long hfs_seek(hfsfile *file, long offset, int from) +{ + unsigned long *lglen, newpos; + + f_getptrs(file, 0, &lglen, 0); + + switch (from) + { + case HFS_SEEK_SET: + newpos = (offset < 0) ? 0 : offset; + break; + + case HFS_SEEK_CUR: + if (offset < 0 && (unsigned long) -offset > file->pos) + newpos = 0; + else + newpos = file->pos + offset; + break; + + case HFS_SEEK_END: + if (offset < 0 && (unsigned long) -offset > *lglen) + newpos = 0; + else + newpos = *lglen + offset; + break; + + default: + ERROR(EINVAL, 0); + } + + if (newpos > *lglen) + newpos = *lglen; + + file->pos = newpos; + + return newpos; + +fail: + return -1; +} + +/* + * NAME: hfs->close() + * DESCRIPTION: close a file + */ +int hfs_close(hfsfile *file) +{ + hfsvol *vol = file->vol; + int result = 0; + + if (f_trunc(file) == -1 || + f_flush(file) == -1) + result = -1; + + if (file->prev) + file->prev->next = file->next; + if (file->next) + file->next->prev = file->prev; + if (file == vol->files) + vol->files = file->next; + + FREE(file); + + return result; +} + +/* High-Level Catalog Routines ============================================= */ + +/* + * NAME: hfs->stat() + * DESCRIPTION: return catalog information for an arbitrary path + */ +int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent) +{ + CatDataRec data; + unsigned long parid; + char name[HFS_MAX_FLEN + 1]; + + if (getvol(&vol) == -1 || + v_resolve(&vol, path, &data, &parid, name, 0) <= 0) + goto fail; + + r_unpackdirent(parid, name, &data, ent); + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->fstat() + * DESCRIPTION: return catalog information for an open file + */ +int hfs_fstat(hfsfile *file, hfsdirent *ent) +{ + r_unpackdirent(file->parid, file->name, &file->cat, ent); + + return 0; +} + +/* + * NAME: hfs->setattr() + * DESCRIPTION: change a file's attributes + */ +int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent) +{ + CatDataRec data; + node n; + + if (getvol(&vol) == -1 || + v_resolve(&vol, path, &data, 0, 0, &n) <= 0) + goto fail; + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + r_packdirent(&data, ent); + + return v_putcatrec(&data, &n); + +fail: + return -1; +} + +/* + * NAME: hfs->fsetattr() + * DESCRIPTION: change an open file's attributes + */ +int hfs_fsetattr(hfsfile *file, const hfsdirent *ent) +{ + if (file->vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + r_packdirent(&file->cat, ent); + + file->flags |= HFS_FILE_UPDATE_CATREC; + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->mkdir() + * DESCRIPTION: create a new directory + */ +int hfs_mkdir(hfsvol *vol, const char *path) +{ + CatDataRec data; + unsigned long parid; + char name[HFS_MAX_FLEN + 1]; + int found; + + if (getvol(&vol) == -1) + goto fail; + + found = v_resolve(&vol, path, &data, &parid, name, 0); + if (found == -1 || parid == 0) + goto fail; + + if (found) + ERROR(EEXIST, 0); + + if (parid == HFS_CNID_ROOTPAR) + ERROR(EINVAL, 0); + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + return v_mkdir(vol, parid, name); + +fail: + return -1; +} + +/* + * NAME: hfs->rmdir() + * DESCRIPTION: delete an empty directory + */ +int hfs_rmdir(hfsvol *vol, const char *path) +{ + CatKeyRec key; + CatDataRec data; + unsigned long parid; + char name[HFS_MAX_FLEN + 1]; + byte pkey[HFS_CATKEYLEN]; + + if (getvol(&vol) == -1 || + v_resolve(&vol, path, &data, &parid, name, 0) <= 0) + goto fail; + + if (data.cdrType != cdrDirRec) + ERROR(ENOTDIR, 0); + + if (data.u.dir.dirVal != 0) + ERROR(ENOTEMPTY, 0); + + if (parid == HFS_CNID_ROOTPAR) + ERROR(EINVAL, 0); + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + /* delete directory record */ + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) == -1) + goto fail; + + /* delete thread record */ + + r_makecatkey(&key, data.u.dir.dirDirID, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) == -1 || + v_adjvalence(vol, parid, 1, -1) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->delete() + * DESCRIPTION: remove both forks of a file + */ +int hfs_delete(hfsvol *vol, const char *path) +{ + hfsfile file; + CatKeyRec key; + byte pkey[HFS_CATKEYLEN]; + int found; + + if (getvol(&vol) == -1 || + v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) + goto fail; + + if (file.cat.cdrType != cdrFilRec) + ERROR(EISDIR, 0); + + if (file.parid == HFS_CNID_ROOTPAR) + ERROR(EINVAL, 0); + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + /* free allocation blocks */ + + file.vol = vol; + file.flags = 0; + + file.cat.u.fil.filLgLen = 0; + file.cat.u.fil.filRLgLen = 0; + + f_selectfork(&file, fkData); + if (f_trunc(&file) == -1) + goto fail; + + f_selectfork(&file, fkRsrc); + if (f_trunc(&file) == -1) + goto fail; + + /* delete file record */ + + r_makecatkey(&key, file.parid, file.name); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) == -1 || + v_adjvalence(vol, file.parid, 0, -1) == -1) + goto fail; + + /* delete file thread, if any */ + + found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); + if (found == -1) + goto fail; + + if (found) + { + r_makecatkey(&key, file.cat.u.fil.filFlNum, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) == -1) + goto fail; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: hfs->rename() + * DESCRIPTION: change the name of and/or move a file or directory + */ +int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath) +{ + hfsvol *srcvol; + CatDataRec src, dst; + unsigned long srcid, dstid; + CatKeyRec key; + char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1]; + byte record[HFS_MAX_CATRECLEN]; + unsigned int reclen; + int found, isdir, moving; + node n; + + if (getvol(&vol) == -1 || + v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0) + goto fail; + + isdir = (src.cdrType == cdrDirRec); + srcvol = vol; + + found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0); + if (found == -1) + goto fail; + + if (vol != srcvol) + ERROR(EINVAL, "can't move across volumes"); + + if (dstid == 0) + ERROR(ENOENT, "bad destination path"); + + if (found && + dst.cdrType == cdrDirRec && + dst.u.dir.dirDirID != src.u.dir.dirDirID) + { + dstid = dst.u.dir.dirDirID; + strcpy(dstname, srcname); + + found = v_catsearch(vol, dstid, dstname, 0, 0, 0); + if (found == -1) + goto fail; + } + + moving = (srcid != dstid); + + if (found) + { + const char *ptr; + + ptr = strrchr(dstpath, ':'); + if (ptr == 0) + ptr = dstpath; + else + ++ptr; + + if (*ptr) + strcpy(dstname, ptr); + + if (! moving && strcmp(srcname, dstname) == 0) + goto done; /* source and destination are identical */ + + if (moving || d_relstring(srcname, dstname)) + ERROR(EEXIST, "can't use destination name"); + } + + /* can't move anything into the root directory's parent */ + + if (moving && dstid == HFS_CNID_ROOTPAR) + ERROR(EINVAL, "can't move above root directory"); + + if (moving && isdir) + { + unsigned long id; + + /* can't move root directory anywhere */ + + if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR) + ERROR(EINVAL, "can't move root directory"); + + /* make sure we aren't trying to move a directory inside itself */ + + for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID) + { + if (id == src.u.dir.dirDirID) + ERROR(EINVAL, "can't move directory inside itself"); + + if (v_getdthread(vol, id, &dst, 0) <= 0) + goto fail; + } + } + + if (vol->flags & HFS_VOL_READONLY) + ERROR(EROFS, 0); + + /* change volume name */ + + if (dstid == HFS_CNID_ROOTPAR) + { + if (! validvname(dstname)) + goto fail; + + strcpy(vol->mdb.drVN, dstname); + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* remove source record */ + + r_makecatkey(&key, srcid, srcname); + r_packcatkey(&key, record, 0); + + if (bt_delete(&vol->cat, record) == -1) + goto fail; + + /* insert destination record */ + + r_makecatkey(&key, dstid, dstname); + r_packcatrec(&key, &src, record, &reclen); + + if (bt_insert(&vol->cat, record, reclen) == -1) + goto fail; + + /* update thread record */ + + if (isdir) + { + if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0) + goto fail; + + dst.u.dthd.thdParID = dstid; + strcpy(dst.u.dthd.thdCName, dstname); + + if (v_putcatrec(&dst, &n) == -1) + goto fail; + } + else + { + found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n); + if (found == -1) + goto fail; + + if (found) + { + dst.u.fthd.fthdParID = dstid; + strcpy(dst.u.fthd.fthdCName, dstname); + + if (v_putcatrec(&dst, &n) == -1) + goto fail; + } + } + + /* update directory valences */ + + if (moving) + { + if (v_adjvalence(vol, srcid, isdir, -1) == -1 || + v_adjvalence(vol, dstid, isdir, 1) == -1) + goto fail; + } + +done: + return 0; + +fail: + return -1; +} + +/* High-Level Media Routines =============================================== */ + +/* + * NAME: hfs->zero() + * DESCRIPTION: initialize medium with new/empty DDR and partition map + */ +int hfs_zero(const char *path, unsigned int maxparts, unsigned long *blocks) +{ + hfsvol vol; + + v_init(&vol, HFS_OPT_NOCACHE); + + if (maxparts < 1) + ERROR(EINVAL, "must allow at least 1 partition"); + + if (v_open(&vol, path, HFS_MODE_RDWR) == -1 || + v_geometry(&vol, 0) == -1) + goto fail; + + if (m_zeroddr(&vol) == -1 || + m_zeropm(&vol, 1 + maxparts) == -1) + goto fail; + + if (blocks) + { + Partition map; + int found; + + found = m_findpmentry(&vol, "Apple_Free", &map, 0); + if (found == -1) + goto fail; + + if (! found) + ERROR(EIO, "unable to determine free partition space"); + + *blocks = map.pmPartBlkCnt; + } + + if (v_close(&vol) == -1) + goto fail; + + return 0; + +fail: + v_close(&vol); + return -1; +} + +/* + * NAME: hfs->mkpart() + * DESCRIPTION: create a new HFS partition + */ +int hfs_mkpart(const char *path, unsigned long len) +{ + hfsvol vol; + + v_init(&vol, HFS_OPT_NOCACHE); + + if (v_open(&vol, path, HFS_MODE_RDWR) == -1) + goto fail; + + if (m_mkpart(&vol, "MacOS", "Apple_HFS", len) == -1) + goto fail; + + if (v_close(&vol) == -1) + goto fail; + + return 0; + +fail: + v_close(&vol); + return -1; +} + +/* + * NAME: hfs->nparts() + * DESCRIPTION: return the number of HFS partitions in the medium + */ +int hfs_nparts(const char *path) +{ + hfsvol vol; + int nparts, found; + Partition map; + unsigned long bnum = 0; + + v_init(&vol, HFS_OPT_NOCACHE); + + if (v_open(&vol, path, HFS_MODE_RDONLY) == -1) + goto fail; + + nparts = 0; + while (1) + { + found = m_findpmentry(&vol, "Apple_HFS", &map, &bnum); + if (found == -1) + goto fail; + + if (! found) + break; + + ++nparts; + } + + if (v_close(&vol) == -1) + goto fail; + + return nparts; + +fail: + v_close(&vol); + return -1; +} + +/* + * NAME: compare() + * DESCRIPTION: comparison function for qsort of blocks to be spared + */ +static +int compare(const unsigned int *n1, const unsigned int *n2) +{ + return *n1 - *n2; +} + +/* + * NAME: hfs->format() + * DESCRIPTION: write a new filesystem + */ +int hfs_format(const char *path, int pnum, int mode, const char *vname, + unsigned int nbadblocks, const unsigned long badblocks[]) +{ + hfsvol vol; + btree *ext = &vol.ext; + btree *cat = &vol.cat; + unsigned int i, *badalloc = 0; + + v_init(&vol, mode); + + if (! validvname(vname)) + goto fail; + + if (v_open(&vol, path, HFS_MODE_RDWR) == -1 || + v_geometry(&vol, pnum) == -1) + goto fail; + + /* initialize volume geometry */ + + vol.lpa = 1 + ((vol.vlen - 6) >> 16); + + if (vol.flags & HFS_OPT_2048) + vol.lpa = (vol.lpa + 3) & ~3; + + vol.vbmsz = (vol.vlen / vol.lpa + 0x0fff) >> 12; + + vol.mdb.drSigWord = HFS_SIGWORD; + vol.mdb.drCrDate = d_mtime(time(0)); + vol.mdb.drLsMod = vol.mdb.drCrDate; + vol.mdb.drAtrb = 0; + vol.mdb.drNmFls = 0; + vol.mdb.drVBMSt = 3; + vol.mdb.drAllocPtr = 0; + + vol.mdb.drAlBlkSiz = vol.lpa << HFS_BLOCKSZ_BITS; + vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz << 2; + vol.mdb.drAlBlSt = vol.mdb.drVBMSt + vol.vbmsz; + + if (vol.flags & HFS_OPT_2048) + vol.mdb.drAlBlSt = ((vol.vstart & 3) + vol.mdb.drAlBlSt + 3) & ~3; + + vol.mdb.drNmAlBlks = (vol.vlen - 2 - vol.mdb.drAlBlSt) / vol.lpa; + + vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */ + vol.mdb.drFreeBks = vol.mdb.drNmAlBlks; + + strcpy(vol.mdb.drVN, vname); + + vol.mdb.drVolBkUp = 0; + vol.mdb.drVSeqNum = 0; + vol.mdb.drWrCnt = 0; + + vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz; + vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; + + vol.mdb.drNmRtDirs = 0; + vol.mdb.drFilCnt = 0; + vol.mdb.drDirCnt = -1; /* incremented when root directory is created */ + + for (i = 0; i < 8; ++i) + vol.mdb.drFndrInfo[i] = 0; + + vol.mdb.drEmbedSigWord = 0x0000; + vol.mdb.drEmbedExtent.xdrStABN = 0; + vol.mdb.drEmbedExtent.xdrNumABlks = 0; + + /* vol.mdb.drXTFlSize */ + /* vol.mdb.drCTFlSize */ + + /* vol.mdb.drXTExtRec[0..2] */ + /* vol.mdb.drCTExtRec[0..2] */ + + vol.flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB; + + /* initialize volume bitmap */ + + vol.vbm = ALLOC(block, vol.vbmsz); + if (vol.vbm == 0) + ERROR(ENOMEM, 0); + + memset(vol.vbm, 0, vol.vbmsz << HFS_BLOCKSZ_BITS); + + vol.flags |= HFS_VOL_UPDATE_VBM; + + /* perform initial bad block sparing */ + + if (nbadblocks > 0) + { + if (nbadblocks * 4 > vol.vlen) + ERROR(EINVAL, "volume contains too many bad blocks"); + + badalloc = ALLOC(unsigned int, nbadblocks); + if (badalloc == 0) + ERROR(ENOMEM, 0); + + if (vol.mdb.drNmAlBlks == 1594) + vol.mdb.drFreeBks = --vol.mdb.drNmAlBlks; + + for (i = 0; i < nbadblocks; ++i) + { + unsigned long bnum; + unsigned int anum; + + bnum = badblocks[i]; + + if (bnum < vol.mdb.drAlBlSt || bnum == vol.vlen - 2) + ERROR(EINVAL, "can't spare critical bad block"); + else if (bnum >= vol.vlen) + ERROR(EINVAL, "bad block not in volume"); + + anum = (bnum - vol.mdb.drAlBlSt) / vol.lpa; + + if (anum < vol.mdb.drNmAlBlks) + BMSET(vol.vbm, anum); + + badalloc[i] = anum; + } + + vol.mdb.drAtrb |= HFS_ATRB_BBSPARED; + } + + /* create extents overflow file */ + + n_init(&ext->hdrnd, ext, ndHdrNode, 0); + + ext->hdrnd.nnum = 0; + ext->hdrnd.nd.ndNRecs = 3; + ext->hdrnd.roff[1] = 0x078; + ext->hdrnd.roff[2] = 0x0f8; + ext->hdrnd.roff[3] = 0x1f8; + + memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128); + + ext->hdr.bthDepth = 0; + ext->hdr.bthRoot = 0; + ext->hdr.bthNRecs = 0; + ext->hdr.bthFNode = 0; + ext->hdr.bthLNode = 0; + ext->hdr.bthNodeSize = HFS_BLOCKSZ; + ext->hdr.bthKeyLen = 0x07; + ext->hdr.bthNNodes = 0; + ext->hdr.bthFree = 0; + for (i = 0; i < 76; ++i) + ext->hdr.bthResv[i] = 0; + + ext->map = ALLOC(byte, HFS_MAP1SZ); + if (ext->map == 0) + ERROR(ENOMEM, 0); + + memset(ext->map, 0, HFS_MAP1SZ); + BMSET(ext->map, 0); + + ext->mapsz = HFS_MAP1SZ; + ext->flags = HFS_BT_UPDATE_HDR; + + /* create catalog file */ + + n_init(&cat->hdrnd, cat, ndHdrNode, 0); + + cat->hdrnd.nnum = 0; + cat->hdrnd.nd.ndNRecs = 3; + cat->hdrnd.roff[1] = 0x078; + cat->hdrnd.roff[2] = 0x0f8; + cat->hdrnd.roff[3] = 0x1f8; + + memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128); + + cat->hdr.bthDepth = 0; + cat->hdr.bthRoot = 0; + cat->hdr.bthNRecs = 0; + cat->hdr.bthFNode = 0; + cat->hdr.bthLNode = 0; + cat->hdr.bthNodeSize = HFS_BLOCKSZ; + cat->hdr.bthKeyLen = 0x25; + cat->hdr.bthNNodes = 0; + cat->hdr.bthFree = 0; + for (i = 0; i < 76; ++i) + cat->hdr.bthResv[i] = 0; + + cat->map = ALLOC(byte, HFS_MAP1SZ); + if (cat->map == 0) + ERROR(ENOMEM, 0); + + memset(cat->map, 0, HFS_MAP1SZ); + BMSET(cat->map, 0); + + cat->mapsz = HFS_MAP1SZ; + cat->flags = HFS_BT_UPDATE_HDR; + + /* allocate space for header nodes (and initial extents) */ + + if (bt_space(ext, 1) == -1 || + bt_space(cat, 1) == -1) + goto fail; + + --ext->hdr.bthFree; + --cat->hdr.bthFree; + + /* create extent records for bad blocks */ + + if (nbadblocks > 0) + { + hfsfile bbfile; + ExtDescriptor extent; + ExtDataRec *extrec; + ExtKeyRec key; + byte record[HFS_MAX_EXTRECLEN]; + unsigned int reclen; + + f_init(&bbfile, &vol, HFS_CNID_BADALLOC, "bad blocks"); + + qsort(badalloc, nbadblocks, sizeof(*badalloc), + (int (*)(const void *, const void *)) compare); + + for (i = 0; i < nbadblocks; ++i) + { + if (i == 0 || badalloc[i] != extent.xdrStABN) + { + extent.xdrStABN = badalloc[i]; + extent.xdrNumABlks = 1; + + if (extent.xdrStABN < vol.mdb.drNmAlBlks && + f_addextent(&bbfile, &extent) == -1) + goto fail; + } + } + + /* flush local extents into extents overflow file */ + + f_getptrs(&bbfile, &extrec, 0, 0); + + r_makeextkey(&key, bbfile.fork, bbfile.cat.u.fil.filFlNum, 0); + r_packextrec(&key, extrec, record, &reclen); + + if (bt_insert(&vol.ext, record, reclen) == -1) + goto fail; + } + + vol.flags |= HFS_VOL_MOUNTED; + + /* create root directory */ + + if (v_mkdir(&vol, HFS_CNID_ROOTPAR, vname) == -1) + goto fail; + + vol.mdb.drNxtCNID = 16; /* first CNID not reserved by Apple */ + + /* write boot blocks */ + + if (m_zerobb(&vol) == -1) + goto fail; + + /* zero other unused space, if requested */ + + if (vol.flags & HFS_OPT_ZERO) + { + block b; + unsigned long bnum; + + memset(&b, 0, sizeof(b)); + + /* between MDB and VBM (never) */ + + for (bnum = 3; bnum < vol.mdb.drVBMSt; ++bnum) + b_writelb(&vol, bnum, &b); + + /* between VBM and first allocation block (sometimes if HFS_OPT_2048) */ + + for (bnum = vol.mdb.drVBMSt + vol.vbmsz; bnum < vol.mdb.drAlBlSt; ++bnum) + b_writelb(&vol, bnum, &b); + + /* between last allocation block and alternate MDB (sometimes) */ + + for (bnum = vol.mdb.drAlBlSt + vol.mdb.drNmAlBlks * vol.lpa; + bnum < vol.vlen - 2; ++bnum) + b_writelb(&vol, bnum, &b); + + /* final block (always) */ + + b_writelb(&vol, vol.vlen - 1, &b); + } + + /* flush remaining state and close volume */ + + if (v_close(&vol) == -1) + goto fail; + + FREE(badalloc); + + return 0; + +fail: + v_close(&vol); + + FREE(badalloc); + + return -1; +} diff --git a/libhfs/hfs.h b/libhfs/hfs.h new file mode 100644 index 0000000..90a5e9d --- /dev/null +++ b/libhfs/hfs.h @@ -0,0 +1,180 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hfs.h,v 1.11 1998/11/02 22:09:01 rob Exp $ + */ + +# include + +# define HFS_BLOCKSZ 512 +# define HFS_BLOCKSZ_BITS 9 + +# define HFS_MAX_FLEN 31 +# define HFS_MAX_VLEN 27 + +typedef struct _hfsvol_ hfsvol; +typedef struct _hfsfile_ hfsfile; +typedef struct _hfsdir_ hfsdir; + +typedef struct { + char name[HFS_MAX_VLEN + 1]; /* name of volume (MacOS Standard Roman) */ + int flags; /* volume flags */ + + unsigned long totbytes; /* total bytes on volume */ + unsigned long freebytes; /* free bytes on volume */ + + unsigned long alblocksz; /* volume allocation block size */ + unsigned long clumpsz; /* default file clump size */ + + unsigned long numfiles; /* number of files in volume */ + unsigned long numdirs; /* number of directories in volume */ + + time_t crdate; /* volume creation date */ + time_t mddate; /* last volume modification date */ + time_t bkdate; /* last volume backup date */ + + unsigned long blessed; /* CNID of MacOS System Folder */ +} hfsvolent; + +typedef struct { + char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */ + int flags; /* bit flags */ + unsigned long cnid; /* catalog node id (CNID) */ + unsigned long parid; /* CNID of parent directory */ + + time_t crdate; /* date of creation */ + time_t mddate; /* date of last modification */ + time_t bkdate; /* date of last backup */ + + short fdflags; /* Macintosh Finder flags */ + + struct { + signed short v; /* Finder icon vertical coordinate */ + signed short h; /* horizontal coordinate */ + } fdlocation; + + union { + struct { + unsigned long dsize; /* size of data fork */ + unsigned long rsize; /* size of resource fork */ + + char type[5]; /* file type code (plus null) */ + char creator[5]; /* file creator code (plus null) */ + } file; + + struct { + unsigned short valence; /* number of items in directory */ + + struct { + signed short top; /* top edge of folder's rectangle */ + signed short left; /* left edge */ + signed short bottom; /* bottom edge */ + signed short right; /* right edge */ + } rect; + } dir; + } u; +} hfsdirent; + +# define HFS_ISDIR 0x0001 +# define HFS_ISLOCKED 0x0002 + +# define HFS_CNID_ROOTPAR 1 +# define HFS_CNID_ROOTDIR 2 +# define HFS_CNID_EXT 3 +# define HFS_CNID_CAT 4 +# define HFS_CNID_BADALLOC 5 + +# define HFS_FNDR_ISONDESK (1 << 0) +# define HFS_FNDR_COLOR 0x0e +# define HFS_FNDR_COLORRESERVED (1 << 4) +# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5) +# define HFS_FNDR_ISSHARED (1 << 6) +# define HFS_FNDR_HASNOINITS (1 << 7) +# define HFS_FNDR_HASBEENINITED (1 << 8) +# define HFS_FNDR_RESERVED (1 << 9) +# define HFS_FNDR_HASCUSTOMICON (1 << 10) +# define HFS_FNDR_ISSTATIONERY (1 << 11) +# define HFS_FNDR_NAMELOCKED (1 << 12) +# define HFS_FNDR_HASBUNDLE (1 << 13) +# define HFS_FNDR_ISINVISIBLE (1 << 14) +# define HFS_FNDR_ISALIAS (1 << 15) + +extern const char *hfs_error; +extern const unsigned char hfs_charorder[]; + +# define HFS_MODE_RDONLY 0 +# define HFS_MODE_RDWR 1 +# define HFS_MODE_ANY 2 + +# define HFS_MODE_MASK 0x0003 + +# define HFS_OPT_NOCACHE 0x0100 +# define HFS_OPT_2048 0x0200 +# define HFS_OPT_ZERO 0x0400 + +# define HFS_SEEK_SET 0 +# define HFS_SEEK_CUR 1 +# define HFS_SEEK_END 2 + +hfsvol *hfs_mount(const char *, int, int); +int hfs_flush(hfsvol *); +void hfs_flushall(void); +int hfs_umount(hfsvol *); +void hfs_umountall(void); +hfsvol *hfs_getvol(const char *); +void hfs_setvol(hfsvol *); + +int hfs_vstat(hfsvol *, hfsvolent *); +int hfs_vsetattr(hfsvol *, hfsvolent *); + +int hfs_chdir(hfsvol *, const char *); +unsigned long hfs_getcwd(hfsvol *); +int hfs_setcwd(hfsvol *, unsigned long); +int hfs_dirinfo(hfsvol *, unsigned long *, char *); + +hfsdir *hfs_opendir(hfsvol *, const char *); +int hfs_readdir(hfsdir *, hfsdirent *); +int hfs_closedir(hfsdir *); + +hfsfile *hfs_create(hfsvol *, const char *, const char *, const char *); +hfsfile *hfs_open(hfsvol *, const char *); +int hfs_setfork(hfsfile *, int); +int hfs_getfork(hfsfile *); +unsigned long hfs_read(hfsfile *, void *, unsigned long); +unsigned long hfs_write(hfsfile *, const void *, unsigned long); +int hfs_truncate(hfsfile *, unsigned long); +unsigned long hfs_seek(hfsfile *, long, int); +int hfs_close(hfsfile *); + +int hfs_stat(hfsvol *, const char *, hfsdirent *); +int hfs_fstat(hfsfile *, hfsdirent *); +int hfs_setattr(hfsvol *, const char *, const hfsdirent *); +int hfs_fsetattr(hfsfile *, const hfsdirent *); + +int hfs_mkdir(hfsvol *, const char *); +int hfs_rmdir(hfsvol *, const char *); + +int hfs_delete(hfsvol *, const char *); +int hfs_rename(hfsvol *, const char *, const char *); + +int hfs_zero(const char *, unsigned int, unsigned long *); +int hfs_mkpart(const char *, unsigned long); +int hfs_nparts(const char *); + +int hfs_format(const char *, int, int, + const char *, unsigned int, const unsigned long []); diff --git a/libhfs/libhfs.h b/libhfs/libhfs.h new file mode 100644 index 0000000..a8943cb --- /dev/null +++ b/libhfs/libhfs.h @@ -0,0 +1,226 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: libhfs.h,v 1.7 1998/11/02 22:09:02 rob Exp $ + */ + +# include "hfs.h" +# include "apple.h" + +extern int errno; + +# define ERROR(code, str) \ + do { hfs_error = (str), errno = (code); goto fail; } while (0) + +# ifdef DEBUG +# define ASSERT(cond) do { if (! (cond)) abort(); } while (0) +# else +# define ASSERT(cond) /* nothing */ +# endif + +# define SIZE(type, n) ((size_t) (sizeof(type) * (n))) +# define ALLOC(type, n) ((type *) malloc(SIZE(type, n))) +# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0) +# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0) + +# define REALLOC(ptr, type, n) \ + ((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n)))) +# define REALLOCX(ptr, type, n) \ + ((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0)) + +# define BMTST(bm, num) \ + (((const byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07))) +# define BMSET(bm, num) \ + (((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07))) +# define BMCLR(bm, num) \ + (((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07))) + +# define STRINGIZE(x) #x +# define STR(x) STRINGIZE(x) + +typedef unsigned char byte; +typedef byte block[HFS_BLOCKSZ]; + +typedef struct _bucket_ { + int flags; /* bit flags */ + unsigned int count; /* number of times this block is requested */ + + unsigned long bnum; /* logical block number */ + block *data; /* pointer to block contents */ + + struct _bucket_ *cnext; /* next bucket in cache chain */ + struct _bucket_ *cprev; /* previous bucket in cache chain */ + + struct _bucket_ *hnext; /* next bucket in hash chain */ + struct _bucket_ **hprev; /* previous bucket's pointer to this bucket */ +} bucket; + +# define HFS_BUCKET_INUSE 0x01 +# define HFS_BUCKET_DIRTY 0x02 + +# define HFS_CACHESZ 128 +# define HFS_HASHSZ 32 +# define HFS_BLOCKBUFSZ 16 + +typedef struct { + struct _hfsvol_ *vol; /* volume to which cache belongs */ + bucket *tail; /* end of bucket chain */ + + unsigned int hits; /* number of cache hits */ + unsigned int misses; /* number of cache misses */ + + bucket chain[HFS_CACHESZ]; /* cache bucket chain */ + bucket *hash[HFS_HASHSZ]; /* hash table for bucket chain */ + + block pool[HFS_CACHESZ]; /* physical blocks in cache */ +} bcache; + +# define HFS_MAP1SZ 256 +# define HFS_MAPXSZ 492 + +# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum]) +# define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum]) + +# define HFS_RECKEYLEN(ptr) (*(const byte *) (ptr)) +# define HFS_RECKEYSKIP(ptr) ((size_t) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1)) +# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr)) + +# define HFS_SETKEYLEN(ptr, x) (*(byte *) (ptr) = (x)) + +# define HFS_CATDATALEN sizeof(CatDataRec) +# define HFS_EXTDATALEN sizeof(ExtDataRec) +# define HFS_MAX_DATALEN (HFS_CATDATALEN > HFS_EXTDATALEN ? \ + HFS_CATDATALEN : HFS_EXTDATALEN) + +# define HFS_CATKEYLEN sizeof(CatKeyRec) +# define HFS_EXTKEYLEN sizeof(ExtKeyRec) +# define HFS_MAX_KEYLEN (HFS_CATKEYLEN > HFS_EXTKEYLEN ? \ + HFS_CATKEYLEN : HFS_EXTKEYLEN) + +# define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN) +# define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN) +# define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN) + +# define HFS_SIGWORD 0x4244 +# define HFS_SIGWORD_MFS ((Integer) 0xd2d7) + +# define HFS_ATRB_BUSY (1 << 6) +# define HFS_ATRB_HLOCKED (1 << 7) +# define HFS_ATRB_UMOUNTED (1 << 8) +# define HFS_ATRB_BBSPARED (1 << 9) +# define HFS_ATRB_BVINCONSIS (1 << 11) +# define HFS_ATRB_COPYPROT (1 << 14) +# define HFS_ATRB_SLOCKED (1 << 15) + +struct _hfsfile_ { + struct _hfsvol_ *vol; /* pointer to volume descriptor */ + unsigned long parid; /* parent directory ID of this file */ + char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */ + CatDataRec cat; /* catalog information */ + ExtDataRec ext; /* current extent record */ + unsigned int fabn; /* starting file allocation block number */ + int fork; /* current selected fork for I/O */ + unsigned long pos; /* current file seek pointer */ + int flags; /* bit flags */ + + struct _hfsfile_ *prev; + struct _hfsfile_ *next; +}; + +# define HFS_FILE_UPDATE_CATREC 0x01 + +# define HFS_MAX_NRECS 35 /* maximum based on minimum record size */ + +typedef struct _node_ { + struct _btree_ *bt; /* btree to which this node belongs */ + unsigned long nnum; /* node index */ + NodeDescriptor nd; /* node descriptor */ + int rnum; /* current record index */ + UInteger roff[HFS_MAX_NRECS + 1]; + /* record offsets */ + block data; /* raw contents of node */ +} node; + +struct _hfsdir_ { + struct _hfsvol_ *vol; /* associated volume */ + unsigned long dirid; /* directory ID of interest (or 0) */ + + node n; /* current B*-tree node */ + struct _hfsvol_ *vptr; /* current volume pointer */ + + struct _hfsdir_ *prev; + struct _hfsdir_ *next; +}; + +typedef void (*keyunpackfunc)(const byte *, void *); +typedef int (*keycomparefunc)(const void *, const void *); + +typedef struct _btree_ { + hfsfile f; /* subset file information */ + node hdrnd; /* header node */ + BTHdrRec hdr; /* header record */ + byte *map; /* usage bitmap */ + unsigned long mapsz; /* number of bytes in bitmap */ + int flags; /* bit flags */ + + keyunpackfunc keyunpack; /* key unpacking function */ + keycomparefunc keycompare; /* key comparison function */ +} btree; + +# define HFS_BT_UPDATE_HDR 0x01 + +struct _hfsvol_ { + void *priv; /* OS-dependent private descriptor data */ + int flags; /* bit flags */ + + int pnum; /* ordinal HFS partition number */ + unsigned long vstart; /* logical block offset to start of volume */ + unsigned long vlen; /* number of logical blocks in volume */ + unsigned int lpa; /* number of logical blocks per allocation block */ + + bcache *cache; /* cache of recently used blocks */ + + MDB mdb; /* master directory block */ + block *vbm; /* volume bitmap */ + unsigned short vbmsz; /* number of blocks in bitmap */ + + btree ext; /* B*-tree control block for extents overflow file */ + btree cat; /* B*-tree control block for catalog file */ + + unsigned long cwd; /* directory id of current working directory */ + + int refs; /* number of external references to this volume */ + hfsfile *files; /* list of open files */ + hfsdir *dirs; /* list of open directories */ + + struct _hfsvol_ *prev; + struct _hfsvol_ *next; +}; + +# define HFS_VOL_OPEN 0x0001 +# define HFS_VOL_MOUNTED 0x0002 +# define HFS_VOL_READONLY 0x0004 +# define HFS_VOL_USINGCACHE 0x0008 + +# define HFS_VOL_UPDATE_MDB 0x0010 +# define HFS_VOL_UPDATE_ALTMDB 0x0020 +# define HFS_VOL_UPDATE_VBM 0x0040 + +# define HFS_VOL_OPT_MASK 0xff00 + +extern hfsvol *hfs_mounts; diff --git a/libhfs/low.c b/libhfs/low.c new file mode 100644 index 0000000..f5e2ad1 --- /dev/null +++ b/libhfs/low.c @@ -0,0 +1,470 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: low.c,v 1.8 1998/11/02 22:09:03 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "libhfs.h" +# include "low.h" +# include "data.h" +# include "block.h" +# include "file.h" + +/* + * NAME: low->getddr() + * DESCRIPTION: read a driver descriptor record + */ +int l_getddr(hfsvol *vol, Block0 *ddr) +{ + block b; + const byte *ptr = b; + int i; + + if (b_readpb(vol, 0, &b, 1) == -1) + goto fail; + + d_fetchsw(&ptr, &ddr->sbSig); + d_fetchsw(&ptr, &ddr->sbBlkSize); + d_fetchsl(&ptr, &ddr->sbBlkCount); + d_fetchsw(&ptr, &ddr->sbDevType); + d_fetchsw(&ptr, &ddr->sbDevId); + d_fetchsl(&ptr, &ddr->sbData); + d_fetchsw(&ptr, &ddr->sbDrvrCount); + d_fetchsl(&ptr, &ddr->ddBlock); + d_fetchsw(&ptr, &ddr->ddSize); + d_fetchsw(&ptr, &ddr->ddType); + + for (i = 0; i < 243; ++i) + d_fetchsw(&ptr, &ddr->ddPad[i]); + + ASSERT(ptr - b == HFS_BLOCKSZ); + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->putddr() + * DESCRIPTION: write a driver descriptor record + */ +int l_putddr(hfsvol *vol, const Block0 *ddr) +{ + block b; + byte *ptr = b; + int i; + + d_storesw(&ptr, ddr->sbSig); + d_storesw(&ptr, ddr->sbBlkSize); + d_storesl(&ptr, ddr->sbBlkCount); + d_storesw(&ptr, ddr->sbDevType); + d_storesw(&ptr, ddr->sbDevId); + d_storesl(&ptr, ddr->sbData); + d_storesw(&ptr, ddr->sbDrvrCount); + d_storesl(&ptr, ddr->ddBlock); + d_storesw(&ptr, ddr->ddSize); + d_storesw(&ptr, ddr->ddType); + + for (i = 0; i < 243; ++i) + d_storesw(&ptr, ddr->ddPad[i]); + + ASSERT(ptr - b == HFS_BLOCKSZ); + + if (b_writepb(vol, 0, &b, 1) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->getpmentry() + * DESCRIPTION: read a partition map entry + */ +int l_getpmentry(hfsvol *vol, Partition *map, unsigned long bnum) +{ + block b; + const byte *ptr = b; + int i; + + if (b_readpb(vol, bnum, &b, 1) == -1) + goto fail; + + d_fetchsw(&ptr, &map->pmSig); + d_fetchsw(&ptr, &map->pmSigPad); + d_fetchsl(&ptr, &map->pmMapBlkCnt); + d_fetchsl(&ptr, &map->pmPyPartStart); + d_fetchsl(&ptr, &map->pmPartBlkCnt); + + strncpy((char *) map->pmPartName, (const char *) ptr, 32); + map->pmPartName[32] = 0; + ptr += 32; + + strncpy((char *) map->pmParType, (const char *) ptr, 32); + map->pmParType[32] = 0; + ptr += 32; + + d_fetchsl(&ptr, &map->pmLgDataStart); + d_fetchsl(&ptr, &map->pmDataCnt); + d_fetchsl(&ptr, &map->pmPartStatus); + d_fetchsl(&ptr, &map->pmLgBootStart); + d_fetchsl(&ptr, &map->pmBootSize); + d_fetchsl(&ptr, &map->pmBootAddr); + d_fetchsl(&ptr, &map->pmBootAddr2); + d_fetchsl(&ptr, &map->pmBootEntry); + d_fetchsl(&ptr, &map->pmBootEntry2); + d_fetchsl(&ptr, &map->pmBootCksum); + + strncpy((char *) map->pmProcessor, (const char *) ptr, 16); + map->pmProcessor[16] = 0; + ptr += 16; + + for (i = 0; i < 188; ++i) + d_fetchsw(&ptr, &map->pmPad[i]); + + ASSERT(ptr - b == HFS_BLOCKSZ); + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->putpmentry() + * DESCRIPTION: write a partition map entry + */ +int l_putpmentry(hfsvol *vol, const Partition *map, unsigned long bnum) +{ + block b; + byte *ptr = b; + int i; + + d_storesw(&ptr, map->pmSig); + d_storesw(&ptr, map->pmSigPad); + d_storesl(&ptr, map->pmMapBlkCnt); + d_storesl(&ptr, map->pmPyPartStart); + d_storesl(&ptr, map->pmPartBlkCnt); + + memset(ptr, 0, 32); + strncpy((char *) ptr, (const char *) map->pmPartName, 32); + ptr += 32; + + memset(ptr, 0, 32); + strncpy((char *) ptr, (const char *) map->pmParType, 32); + ptr += 32; + + d_storesl(&ptr, map->pmLgDataStart); + d_storesl(&ptr, map->pmDataCnt); + d_storesl(&ptr, map->pmPartStatus); + d_storesl(&ptr, map->pmLgBootStart); + d_storesl(&ptr, map->pmBootSize); + d_storesl(&ptr, map->pmBootAddr); + d_storesl(&ptr, map->pmBootAddr2); + d_storesl(&ptr, map->pmBootEntry); + d_storesl(&ptr, map->pmBootEntry2); + d_storesl(&ptr, map->pmBootCksum); + + memset(ptr, 0, 16); + strncpy((char *) ptr, (const char *) map->pmProcessor, 16); + ptr += 16; + + for (i = 0; i < 188; ++i) + d_storesw(&ptr, map->pmPad[i]); + + ASSERT(ptr - b == HFS_BLOCKSZ); + + if (b_writepb(vol, bnum, &b, 1) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->getbb() + * DESCRIPTION: read a volume's boot blocks + */ +int l_getbb(hfsvol *vol, BootBlkHdr *bb, byte *bootcode) +{ + block b; + const byte *ptr = b; + + if (b_readlb(vol, 0, &b) == -1) + goto fail; + + d_fetchsw(&ptr, &bb->bbID); + d_fetchsl(&ptr, &bb->bbEntry); + d_fetchsw(&ptr, &bb->bbVersion); + d_fetchsw(&ptr, &bb->bbPageFlags); + + d_fetchstr(&ptr, bb->bbSysName, sizeof(bb->bbSysName)); + d_fetchstr(&ptr, bb->bbShellName, sizeof(bb->bbShellName)); + d_fetchstr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name)); + d_fetchstr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name)); + d_fetchstr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName)); + d_fetchstr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName)); + d_fetchstr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName)); + + d_fetchsw(&ptr, &bb->bbCntFCBs); + d_fetchsw(&ptr, &bb->bbCntEvts); + d_fetchsl(&ptr, &bb->bb128KSHeap); + d_fetchsl(&ptr, &bb->bb256KSHeap); + d_fetchsl(&ptr, &bb->bbSysHeapSize); + d_fetchsw(&ptr, &bb->filler); + d_fetchsl(&ptr, &bb->bbSysHeapExtra); + d_fetchsl(&ptr, &bb->bbSysHeapFract); + + ASSERT(ptr - b == 148); + + if (bootcode) + { + memcpy(bootcode, ptr, HFS_BOOTCODE1LEN); + + if (b_readlb(vol, 1, &b) == -1) + goto fail; + + memcpy(bootcode + HFS_BOOTCODE1LEN, b, HFS_BOOTCODE2LEN); + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->putbb() + * DESCRIPTION: write a volume's boot blocks + */ +int l_putbb(hfsvol *vol, const BootBlkHdr *bb, const byte *bootcode) +{ + block b; + byte *ptr = b; + + d_storesw(&ptr, bb->bbID); + d_storesl(&ptr, bb->bbEntry); + d_storesw(&ptr, bb->bbVersion); + d_storesw(&ptr, bb->bbPageFlags); + + d_storestr(&ptr, bb->bbSysName, sizeof(bb->bbSysName)); + d_storestr(&ptr, bb->bbShellName, sizeof(bb->bbShellName)); + d_storestr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name)); + d_storestr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name)); + d_storestr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName)); + d_storestr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName)); + d_storestr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName)); + + d_storesw(&ptr, bb->bbCntFCBs); + d_storesw(&ptr, bb->bbCntEvts); + d_storesl(&ptr, bb->bb128KSHeap); + d_storesl(&ptr, bb->bb256KSHeap); + d_storesl(&ptr, bb->bbSysHeapSize); + d_storesw(&ptr, bb->filler); + d_storesl(&ptr, bb->bbSysHeapExtra); + d_storesl(&ptr, bb->bbSysHeapFract); + + ASSERT(ptr - b == 148); + + if (bootcode) + memcpy(ptr, bootcode, HFS_BOOTCODE1LEN); + else + memset(ptr, 0, HFS_BOOTCODE1LEN); + + if (b_writelb(vol, 0, &b) == -1) + goto fail; + + if (bootcode) + memcpy(&b, bootcode + HFS_BOOTCODE1LEN, HFS_BOOTCODE2LEN); + else + memset(&b, 0, HFS_BOOTCODE2LEN); + + if (b_writelb(vol, 1, &b) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->getmdb() + * DESCRIPTION: read a master directory block + */ +int l_getmdb(hfsvol *vol, MDB *mdb, int backup) +{ + block b; + const byte *ptr = b; + int i; + + if (b_readlb(vol, backup ? vol->vlen - 2 : 2, &b) == -1) + goto fail; + + d_fetchsw(&ptr, &mdb->drSigWord); + d_fetchsl(&ptr, &mdb->drCrDate); + d_fetchsl(&ptr, &mdb->drLsMod); + d_fetchsw(&ptr, &mdb->drAtrb); + d_fetchuw(&ptr, &mdb->drNmFls); + d_fetchuw(&ptr, &mdb->drVBMSt); + d_fetchuw(&ptr, &mdb->drAllocPtr); + d_fetchuw(&ptr, &mdb->drNmAlBlks); + d_fetchul(&ptr, &mdb->drAlBlkSiz); + d_fetchul(&ptr, &mdb->drClpSiz); + d_fetchuw(&ptr, &mdb->drAlBlSt); + d_fetchsl(&ptr, &mdb->drNxtCNID); + d_fetchuw(&ptr, &mdb->drFreeBks); + + d_fetchstr(&ptr, mdb->drVN, sizeof(mdb->drVN)); + + ASSERT(ptr - b == 64); + + d_fetchsl(&ptr, &mdb->drVolBkUp); + d_fetchsw(&ptr, &mdb->drVSeqNum); + d_fetchul(&ptr, &mdb->drWrCnt); + d_fetchul(&ptr, &mdb->drXTClpSiz); + d_fetchul(&ptr, &mdb->drCTClpSiz); + d_fetchuw(&ptr, &mdb->drNmRtDirs); + d_fetchul(&ptr, &mdb->drFilCnt); + d_fetchul(&ptr, &mdb->drDirCnt); + + for (i = 0; i < 8; ++i) + d_fetchsl(&ptr, &mdb->drFndrInfo[i]); + + ASSERT(ptr - b == 124); + + d_fetchuw(&ptr, &mdb->drEmbedSigWord); + d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrStABN); + d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrNumABlks); + + d_fetchul(&ptr, &mdb->drXTFlSize); + + for (i = 0; i < 3; ++i) + { + d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrStABN); + d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrNumABlks); + } + + ASSERT(ptr - b == 146); + + d_fetchul(&ptr, &mdb->drCTFlSize); + + for (i = 0; i < 3; ++i) + { + d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrStABN); + d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrNumABlks); + } + + ASSERT(ptr - b == 162); + + return 0; + +fail: + return -1; +} + +/* + * NAME: low->putmdb() + * DESCRIPTION: write master directory block(s) + */ +int l_putmdb(hfsvol *vol, const MDB *mdb, int backup) +{ + block b; + byte *ptr = b; + int i; + + d_storesw(&ptr, mdb->drSigWord); + d_storesl(&ptr, mdb->drCrDate); + d_storesl(&ptr, mdb->drLsMod); + d_storesw(&ptr, mdb->drAtrb); + d_storeuw(&ptr, mdb->drNmFls); + d_storeuw(&ptr, mdb->drVBMSt); + d_storeuw(&ptr, mdb->drAllocPtr); + d_storeuw(&ptr, mdb->drNmAlBlks); + d_storeul(&ptr, mdb->drAlBlkSiz); + d_storeul(&ptr, mdb->drClpSiz); + d_storeuw(&ptr, mdb->drAlBlSt); + d_storesl(&ptr, mdb->drNxtCNID); + d_storeuw(&ptr, mdb->drFreeBks); + + d_storestr(&ptr, mdb->drVN, sizeof(mdb->drVN)); + + ASSERT(ptr - b == 64); + + d_storesl(&ptr, mdb->drVolBkUp); + d_storesw(&ptr, mdb->drVSeqNum); + d_storeul(&ptr, mdb->drWrCnt); + d_storeul(&ptr, mdb->drXTClpSiz); + d_storeul(&ptr, mdb->drCTClpSiz); + d_storeuw(&ptr, mdb->drNmRtDirs); + d_storeul(&ptr, mdb->drFilCnt); + d_storeul(&ptr, mdb->drDirCnt); + + for (i = 0; i < 8; ++i) + d_storesl(&ptr, mdb->drFndrInfo[i]); + + ASSERT(ptr - b == 124); + + d_storeuw(&ptr, mdb->drEmbedSigWord); + d_storeuw(&ptr, mdb->drEmbedExtent.xdrStABN); + d_storeuw(&ptr, mdb->drEmbedExtent.xdrNumABlks); + + d_storeul(&ptr, mdb->drXTFlSize); + + for (i = 0; i < 3; ++i) + { + d_storeuw(&ptr, mdb->drXTExtRec[i].xdrStABN); + d_storeuw(&ptr, mdb->drXTExtRec[i].xdrNumABlks); + } + + ASSERT(ptr - b == 146); + + d_storeul(&ptr, mdb->drCTFlSize); + + for (i = 0; i < 3; ++i) + { + d_storeuw(&ptr, mdb->drCTExtRec[i].xdrStABN); + d_storeuw(&ptr, mdb->drCTExtRec[i].xdrNumABlks); + } + + ASSERT(ptr - b == 162); + + memset(ptr, 0, HFS_BLOCKSZ - (ptr - b)); + + if (b_writelb(vol, 2, &b) == -1 || + (backup && b_writelb(vol, vol->vlen - 2, &b) == -1)) + goto fail; + + return 0; + +fail: + return -1; +} diff --git a/libhfs/low.h b/libhfs/low.h new file mode 100644 index 0000000..03db9bb --- /dev/null +++ b/libhfs/low.h @@ -0,0 +1,44 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: low.h,v 1.6 1998/04/11 08:27:13 rob Exp $ + */ + +# define HFS_DDR_SIGWORD 0x4552 + +# define HFS_PM_SIGWORD 0x504d +# define HFS_PM_SIGWORD_OLD 0x5453 + +# define HFS_BB_SIGWORD 0x4c4b + +# define HFS_BOOTCODE1LEN (HFS_BLOCKSZ - 148) +# define HFS_BOOTCODE2LEN HFS_BLOCKSZ + +# define HFS_BOOTCODELEN (HFS_BOOTCODE1LEN + HFS_BOOTCODE2LEN) + +int l_getddr(hfsvol *, Block0 *); +int l_putddr(hfsvol *, const Block0 *); + +int l_getpmentry(hfsvol *, Partition *, unsigned long); +int l_putpmentry(hfsvol *, const Partition *, unsigned long); + +int l_getbb(hfsvol *, BootBlkHdr *, byte *); +int l_putbb(hfsvol *, const BootBlkHdr *, const byte *); + +int l_getmdb(hfsvol *, MDB *, int); +int l_putmdb(hfsvol *, const MDB *, int); diff --git a/libhfs/medium.c b/libhfs/medium.c new file mode 100644 index 0000000..baa5a3c --- /dev/null +++ b/libhfs/medium.c @@ -0,0 +1,318 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: medium.c,v 1.4 1998/11/02 22:09:04 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "libhfs.h" +# include "block.h" +# include "low.h" +# include "medium.h" + +/* Driver Descriptor Record Routines ======================================= */ + +/* + * NAME: medium->zeroddr() + * DESCRIPTION: write a new/empty driver descriptor record + */ +int m_zeroddr(hfsvol *vol) +{ + Block0 ddr; + int i; + + ASSERT(vol->pnum == 0 && vol->vlen != 0); + + ddr.sbSig = HFS_DDR_SIGWORD; + ddr.sbBlkSize = HFS_BLOCKSZ; + ddr.sbBlkCount = vol->vlen; + + ddr.sbDevType = 0; + ddr.sbDevId = 0; + ddr.sbData = 0; + + ddr.sbDrvrCount = 0; + + ddr.ddBlock = 0; + ddr.ddSize = 0; + ddr.ddType = 0; + + for (i = 0; i < 243; ++i) + ddr.ddPad[i] = 0; + + return l_putddr(vol, &ddr); +} + +/* Partition Map Routines ================================================== */ + +/* + * NAME: medium->zeropm() + * DESCRIPTION: write new/empty partition map + */ +int m_zeropm(hfsvol *vol, unsigned int maxparts) +{ + Partition map; + unsigned int i; + + ASSERT(vol->pnum == 0 && vol->vlen != 0); + + if (maxparts < 2) + ERROR(EINVAL, "must allow at least 2 partitions"); + + /* first entry: partition map itself */ + + map.pmSig = HFS_PM_SIGWORD; + map.pmSigPad = 0; + map.pmMapBlkCnt = 2; + + map.pmPyPartStart = 1; + map.pmPartBlkCnt = maxparts; + + strcpy((char *) map.pmPartName, "Apple"); + strcpy((char *) map.pmParType, "Apple_partition_map"); + + map.pmLgDataStart = 0; + map.pmDataCnt = map.pmPartBlkCnt; + + map.pmPartStatus = 0; + + map.pmLgBootStart = 0; + map.pmBootSize = 0; + map.pmBootAddr = 0; + map.pmBootAddr2 = 0; + map.pmBootEntry = 0; + map.pmBootEntry2 = 0; + map.pmBootCksum = 0; + + strcpy((char *) map.pmProcessor, ""); + + for (i = 0; i < 188; ++i) + map.pmPad[i] = 0; + + if (l_putpmentry(vol, &map, 1) == -1) + goto fail; + + /* second entry: rest of medium */ + + map.pmPyPartStart = 1 + maxparts; + map.pmPartBlkCnt = vol->vlen - 1 - maxparts; + + strcpy((char *) map.pmPartName, "Extra"); + strcpy((char *) map.pmParType, "Apple_Free"); + + map.pmDataCnt = map.pmPartBlkCnt; + + if (l_putpmentry(vol, &map, 2) == -1) + goto fail; + + /* zero rest of partition map's partition */ + + if (maxparts > 2) + { + block b; + + memset(&b, 0, sizeof(b)); + + for (i = 3; i <= maxparts; ++i) + { + if (b_writepb(vol, i, &b, 1) == -1) + goto fail; + } + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: medium->findpmentry() + * DESCRIPTION: locate a partition map entry + */ +int m_findpmentry(hfsvol *vol, const char *type, + Partition *map, unsigned long *start) +{ + unsigned long bnum; + int found = 0; + + if (start && *start > 0) + { + bnum = *start; + + if (bnum++ >= (unsigned long) map->pmMapBlkCnt) + ERROR(EINVAL, "partition not found"); + } + else + bnum = 1; + + while (1) + { + if (l_getpmentry(vol, map, bnum) == -1) + { + found = -1; + goto fail; + } + + if (map->pmSig != HFS_PM_SIGWORD) + { + found = -1; + + if (map->pmSig == HFS_PM_SIGWORD_OLD) + ERROR(EINVAL, "old partition map format not supported"); + else + ERROR(EINVAL, "invalid partition map"); + } + + if (strcmp((char *) map->pmParType, type) == 0) + { + found = 1; + goto done; + } + + if (bnum++ >= (unsigned long) map->pmMapBlkCnt) + ERROR(EINVAL, "partition not found"); + } + +done: + if (start) + *start = bnum; + +fail: + return found; +} + +/* + * NAME: medium->mkpart() + * DESCRIPTION: create a new partition from available free space + */ +int m_mkpart(hfsvol *vol, + const char *name, const char *type, unsigned long len) +{ + Partition map; + unsigned int nparts, maxparts; + unsigned long bnum, start, remain; + int found; + + if (strlen(name) > 32 || + strlen(type) > 32) + ERROR(EINVAL, "partition name/type can each be at most 32 chars"); + + if (len == 0) + ERROR(EINVAL, "partition length must be > 0"); + + found = m_findpmentry(vol, "Apple_partition_map", &map, 0); + if (found == -1) + goto fail; + + if (! found) + ERROR(EIO, "cannot find partition map's partition"); + + nparts = map.pmMapBlkCnt; + maxparts = map.pmPartBlkCnt; + + bnum = 0; + do + { + found = m_findpmentry(vol, "Apple_Free", &map, &bnum); + if (found == -1) + goto fail; + + if (! found) + ERROR(ENOSPC, "no available partitions"); + } + while (len > (unsigned long) map.pmPartBlkCnt); + + start = (unsigned long) map.pmPyPartStart + len; + remain = (unsigned long) map.pmPartBlkCnt - len; + + if (remain && nparts >= maxparts) + ERROR(EINVAL, "must allocate all blocks in free space"); + + map.pmPartBlkCnt = len; + + strcpy((char *) map.pmPartName, name); + strcpy((char *) map.pmParType, type); + + map.pmLgDataStart = 0; + map.pmDataCnt = len; + + map.pmPartStatus = 0; + + if (l_putpmentry(vol, &map, bnum) == -1) + goto fail; + + if (remain) + { + map.pmPyPartStart = start; + map.pmPartBlkCnt = remain; + + strcpy((char *) map.pmPartName, "Extra"); + strcpy((char *) map.pmParType, "Apple_Free"); + + map.pmDataCnt = remain; + + if (l_putpmentry(vol, &map, ++nparts) == -1) + goto fail; + + for (bnum = 1; bnum <= nparts; ++bnum) + { + if (l_getpmentry(vol, &map, bnum) == -1) + goto fail; + + map.pmMapBlkCnt = nparts; + + if (l_putpmentry(vol, &map, bnum) == -1) + goto fail; + } + } + + return 0; + +fail: + return -1; +} + +/* Boot Blocks Routines ==================================================== */ + +/* + * NAME: medium->zerobb() + * DESCRIPTION: write new/empty volume boot blocks + */ +int m_zerobb(hfsvol *vol) +{ + block b; + + memset(&b, 0, sizeof(b)); + + if (b_writelb(vol, 0, &b) == -1 || + b_writelb(vol, 1, &b) == -1) + goto fail; + + return 0; + +fail: + return -1; +} diff --git a/libhfs/medium.h b/libhfs/medium.h new file mode 100644 index 0000000..5580a9e --- /dev/null +++ b/libhfs/medium.h @@ -0,0 +1,42 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: medium.h,v 1.3 1998/04/11 08:27:13 rob Exp $ + */ + +/* + * Partition Types: + * + * "Apple_partition_map" partition map + * "Apple_Driver" device driver + * "Apple_Driver43" SCSI Manager 4.3 device driver + * "Apple_MFS" Macintosh 64K ROM filesystem + * "Apple_HFS" Macintosh hierarchical filesystem + * "Apple_Unix_SVR2" Unix filesystem + * "Apple_PRODOS" ProDOS filesystem + * "Apple_Free" unused + * "Apple_Scratch" empty + */ + +int m_zeroddr(hfsvol *); + +int m_zeropm(hfsvol *, unsigned int); +int m_findpmentry(hfsvol *, const char *, Partition *, unsigned long *); +int m_mkpart(hfsvol *, const char *, const char *, unsigned long); + +int m_zerobb(hfsvol *); diff --git a/libhfs/memcmp.c b/libhfs/memcmp.c new file mode 100644 index 0000000..f1816e4 --- /dev/null +++ b/libhfs/memcmp.c @@ -0,0 +1,50 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: memcmp.c,v 1.6 1998/04/11 16:22:48 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include + +/* + * NAME: memcmp() + * DESCRIPTION: compare memory areas + */ +int memcmp(const void *s1, const void *s2, size_t n) +{ + register const unsigned char *c1, *c2; + + c1 = s1; + c2 = s2; + + while (n--) + { + register int diff; + + diff = *c1++ - *c2++; + + if (diff) + return diff; + } + + return 0; +} diff --git a/libhfs/node.c b/libhfs/node.c new file mode 100644 index 0000000..90268e0 --- /dev/null +++ b/libhfs/node.c @@ -0,0 +1,473 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: node.c,v 1.9 1998/11/02 22:09:05 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# include "libhfs.h" +# include "node.h" +# include "data.h" +# include "btree.h" + +/* total bytes used by records (NOT including record offsets) */ + +# define NODEUSED(n) \ + ((size_t) ((n).roff[(n).nd.ndNRecs] - (n).roff[0])) + +/* total bytes available for new records (INCLUDING record offsets) */ + +# define NODEFREE(n) \ + ((size_t) (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - \ + 2 * ((n).nd.ndNRecs + 1))) + +/* + * NAME: node->init() + * DESCRIPTION: construct an empty node + */ +void n_init(node *np, btree *bt, int type, int height) +{ + np->bt = bt; + np->nnum = (unsigned long) -1; + + np->nd.ndFLink = 0; + np->nd.ndBLink = 0; + np->nd.ndType = type; + np->nd.ndNHeight = height; + np->nd.ndNRecs = 0; + np->nd.ndResv2 = 0; + + np->rnum = -1; + np->roff[0] = 0x00e; + + memset(&np->data, 0, sizeof(np->data)); +} + +/* + * NAME: node->new() + * DESCRIPTION: allocate a new b*-tree node + */ +int n_new(node *np) +{ + btree *bt = np->bt; + unsigned long num; + + if (bt->hdr.bthFree == 0) + ERROR(EIO, "b*-tree full"); + + num = 0; + while (num < bt->hdr.bthNNodes && BMTST(bt->map, num)) + ++num; + + if (num == bt->hdr.bthNNodes) + ERROR(EIO, "free b*-tree node not found"); + + np->nnum = num; + + BMSET(bt->map, num); + --bt->hdr.bthFree; + + bt->flags |= HFS_BT_UPDATE_HDR; + + return 0; + +fail: + return -1; +} + +/* + * NAME: node->free() + * DESCRIPTION: deallocate and remove a b*-tree node + */ +int n_free(node *np) +{ + btree *bt = np->bt; + node sib; + + if (bt->hdr.bthFNode == np->nnum) + bt->hdr.bthFNode = np->nd.ndFLink; + + if (bt->hdr.bthLNode == np->nnum) + bt->hdr.bthLNode = np->nd.ndBLink; + + if (np->nd.ndFLink > 0) + { + if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1) + goto fail; + + sib.nd.ndBLink = np->nd.ndBLink; + + if (bt_putnode(&sib) == -1) + goto fail; + } + + if (np->nd.ndBLink > 0) + { + if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1) + goto fail; + + sib.nd.ndFLink = np->nd.ndFLink; + + if (bt_putnode(&sib) == -1) + goto fail; + } + + BMCLR(bt->map, np->nnum); + ++bt->hdr.bthFree; + + bt->flags |= HFS_BT_UPDATE_HDR; + + return 0; + +fail: + return -1; +} + +/* + * NAME: compact() + * DESCRIPTION: clean up a node, removing deleted records + */ +static +void compact(node *np) +{ + byte *ptr; + int offset, nrecs, i; + + offset = 0x00e; + ptr = np->data + offset; + nrecs = 0; + + for (i = 0; i < np->nd.ndNRecs; ++i) + { + const byte *rec; + int reclen; + + rec = HFS_NODEREC(*np, i); + reclen = HFS_RECLEN(*np, i); + + if (HFS_RECKEYLEN(rec) > 0) + { + np->roff[nrecs++] = offset; + offset += reclen; + + if (ptr == rec) + ptr += reclen; + else + { + while (reclen--) + *ptr++ = *rec++; + } + } + } + + np->roff[nrecs] = offset; + np->nd.ndNRecs = nrecs; +} + +/* + * NAME: node->search() + * DESCRIPTION: locate a record in a node, or the record it should follow + */ +int n_search(node *np, const byte *pkey) +{ + const btree *bt = np->bt; + byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN]; + int i, comp = -1; + + bt->keyunpack(pkey, key2); + + for (i = np->nd.ndNRecs; i--; ) + { + const byte *rec; + + rec = HFS_NODEREC(*np, i); + + if (HFS_RECKEYLEN(rec) == 0) + continue; /* deleted record */ + + bt->keyunpack(rec, key1); + comp = bt->keycompare(key1, key2); + + if (comp <= 0) + break; + } + + np->rnum = i; + + return comp == 0; +} + +/* + * NAME: node->index() + * DESCRIPTION: create an index record from a key and node pointer + */ +void n_index(const node *np, byte *record, unsigned int *reclen) +{ + const byte *key = HFS_NODEREC(*np, 0); + + if (np->bt == &np->bt->f.vol->cat) + { + /* force the key length to be 0x25 */ + + HFS_SETKEYLEN(record, 0x25); + memset(record + 1, 0, 0x25); + memcpy(record + 1, key + 1, HFS_RECKEYLEN(key)); + } + else + memcpy(record, key, HFS_RECKEYSKIP(key)); + + d_putul(HFS_RECDATA(record), np->nnum); + + if (reclen) + *reclen = HFS_RECKEYSKIP(record) + 4; +} + +/* + * NAME: split() + * DESCRIPTION: divide a node into two and insert a record + */ +static +int split(node *left, byte *record, unsigned int *reclen) +{ + btree *bt = left->bt; + node n, *right = &n, *side = 0; + int mark, i; + + /* create a second node by cloning the first */ + + *right = *left; + + if (n_new(right) == -1) + goto fail; + + left->nd.ndFLink = right->nnum; + right->nd.ndBLink = left->nnum; + + /* divide all records evenly between the two nodes */ + + mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1; + + if (left->rnum == -1) + { + side = left; + mark -= *reclen + 2; + } + + for (i = 0; i < left->nd.ndNRecs; ++i) + { + node *np; + byte *rec; + + np = (mark > 0) ? right : left; + rec = HFS_NODEREC(*np, i); + + mark -= HFS_RECLEN(*np, i) + 2; + + HFS_SETKEYLEN(rec, 0); + + if (left->rnum == i) + { + side = (mark > 0) ? left : right; + mark -= *reclen + 2; + } + } + + compact(left); + compact(right); + + /* insert the new record and store the modified nodes */ + + ASSERT(side); + + n_search(side, record); + n_insertx(side, record, *reclen); + + if (bt_putnode(left) == -1 || + bt_putnode(right) == -1) + goto fail; + + /* create an index record in the parent for the new node */ + + n_index(right, record, reclen); + + /* update link pointers */ + + if (bt->hdr.bthLNode == left->nnum) + { + bt->hdr.bthLNode = right->nnum; + bt->flags |= HFS_BT_UPDATE_HDR; + } + + if (right->nd.ndFLink > 0) + { + node sib; + + if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1) + goto fail; + + sib.nd.ndBLink = right->nnum; + + if (bt_putnode(&sib) == -1) + goto fail; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: node->insertx() + * DESCRIPTION: insert a record into a node (which must already have room) + */ +void n_insertx(node *np, const byte *record, unsigned int reclen) +{ + int rnum, i; + byte *ptr; + + rnum = np->rnum + 1; + + /* push other records down to make room */ + + for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen; + ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr) + *(ptr - 1) = *(ptr - 1 - reclen); + + ++np->nd.ndNRecs; + + for (i = np->nd.ndNRecs; i > rnum; --i) + np->roff[i] = np->roff[i - 1] + reclen; + + /* write the new record */ + + memcpy(HFS_NODEREC(*np, rnum), record, reclen); +} + +/* + * NAME: node->insert() + * DESCRIPTION: insert a new record into a node; return a record for parent + */ +int n_insert(node *np, byte *record, unsigned int *reclen) +{ + /* check for free space */ + + if (np->nd.ndNRecs >= HFS_MAX_NRECS || + *reclen + 2 > NODEFREE(*np)) + return split(np, record, reclen); + + n_insertx(np, record, *reclen); + *reclen = 0; + + return bt_putnode(np); +} + +/* + * NAME: join() + * DESCRIPTION: combine two nodes into a single node + */ +static +int join(node *left, node *right, byte *record, int *flag) +{ + int i, offset; + + /* copy records and offsets */ + + memcpy(HFS_NODEREC(*left, left->nd.ndNRecs), + HFS_NODEREC(*right, 0), NODEUSED(*right)); + + offset = left->roff[left->nd.ndNRecs] - right->roff[0]; + + for (i = 1; i <= right->nd.ndNRecs; ++i) + left->roff[++left->nd.ndNRecs] = offset + right->roff[i]; + + if (bt_putnode(left) == -1) + goto fail; + + /* eliminate node and update link pointers */ + + if (n_free(right) == -1) + goto fail; + + HFS_SETKEYLEN(record, 0); + *flag = 1; + + return 0; + +fail: + return -1; +} + +/* + * NAME: node->delete() + * DESCRIPTION: remove a record from a node + */ +int n_delete(node *np, byte *record, int *flag) +{ + byte *rec; + + rec = HFS_NODEREC(*np, np->rnum); + + HFS_SETKEYLEN(rec, 0); + compact(np); + + if (np->nd.ndNRecs == 0) + { + if (n_free(np) == -1) + goto fail; + + HFS_SETKEYLEN(record, 0); + *flag = 1; + + return 0; + } + + /* see if we can join with our left sibling */ + + if (np->nd.ndBLink > 0) + { + node left; + + if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1) + goto fail; + + if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS && + NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left)) + return join(&left, np, record, flag); + } + + if (np->rnum == 0) + { + /* special case: first record changed; update parent record key */ + + n_index(np, record, 0); + *flag = 1; + } + + return bt_putnode(np); + +fail: + return -1; +} diff --git a/libhfs/node.h b/libhfs/node.h new file mode 100644 index 0000000..91287fd --- /dev/null +++ b/libhfs/node.h @@ -0,0 +1,34 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: node.h,v 1.7 1998/11/02 22:09:06 rob Exp $ + */ + +void n_init(node *, btree *, int, int); + +int n_new(node *); +int n_free(node *); + +int n_search(node *, const byte *); + +void n_index(const node *, byte *, unsigned int *); + +void n_insertx(node *, const byte *, unsigned int); +int n_insert(node *, byte *, unsigned int *); + +int n_delete(node *, byte *, int *); diff --git a/libhfs/os.h b/libhfs/os.h new file mode 100644 index 0000000..5747dde --- /dev/null +++ b/libhfs/os.h @@ -0,0 +1,29 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: os.h,v 1.6 1998/09/15 19:21:05 rob Exp $ + */ + +int os_open(void **, const char *, int); +int os_close(void **); + +int os_same(void **, const char *); + +unsigned long os_seek(void **, unsigned long); +unsigned long os_read(void **, void *, unsigned long); +unsigned long os_write(void **, const void *, unsigned long); diff --git a/libhfs/os/unix.c b/libhfs/os/unix.c new file mode 100644 index 0000000..736243d --- /dev/null +++ b/libhfs/os/unix.c @@ -0,0 +1,199 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: unix.c,v 1.8 1998/11/02 22:09:13 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_FCNTL_H +# include +# else +int open(const char *, int, ...); +int fcntl(int, int, ...); +# endif + +# ifdef HAVE_UNISTD_H +# include +# else +int close(int); +off_t lseek(int, off_t, int); +ssize_t read(int, void *, size_t); +ssize_t write(int, const char *, size_t); +int stat(const char *, struct stat *); +int fstat(int, struct stat *); +# endif + +# include +# include + +# include "libhfs.h" +# include "os.h" + +/* + * NAME: os->open() + * DESCRIPTION: open and lock a new descriptor from the given path and mode + */ +int os_open(void **priv, const char *path, int mode) +{ + int fd; + struct flock lock; + + switch (mode) + { + case HFS_MODE_RDONLY: + mode = O_RDONLY; + break; + + case HFS_MODE_RDWR: + default: + mode = O_RDWR; + break; + } + + fd = open(path, mode); + if (fd == -1) + ERROR(errno, "error opening medium"); + + /* lock descriptor against concurrent access */ + + lock.l_type = (mode == O_RDONLY) ? F_RDLCK : F_WRLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + if (fcntl(fd, F_SETLK, &lock) == -1 && + (errno == EACCES || errno == EAGAIN)) + ERROR(EAGAIN, "unable to obtain lock for medium"); + + *priv = (void *) fd; + + return 0; + +fail: + if (fd != -1) + close(fd); + + return -1; +} + +/* + * NAME: os->close() + * DESCRIPTION: close an open descriptor + */ +int os_close(void **priv) +{ + int fd = (int) *priv; + + *priv = (void *) -1; + + if (close(fd) == -1) + ERROR(errno, "error closing medium"); + + return 0; + +fail: + return -1; +} + +/* + * NAME: os->same() + * DESCRIPTION: return 1 iff path is same as the open descriptor + */ +int os_same(void **priv, const char *path) +{ + int fd = (int) *priv; + struct stat fdev, dev; + + if (fstat(fd, &fdev) == -1 || + stat(path, &dev) == -1) + ERROR(errno, "can't get path information"); + + return fdev.st_dev == dev.st_dev && + fdev.st_ino == dev.st_ino; + +fail: + return -1; +} + +/* + * NAME: os->seek() + * DESCRIPTION: set a descriptor's seek pointer (offset in blocks) + */ +unsigned long os_seek(void **priv, unsigned long offset) +{ + int fd = (int) *priv; + off_t result; + + /* offset == -1 special; seek to last block of device */ + + if (offset == (unsigned long) -1) + result = lseek(fd, 0, SEEK_END); + else + result = lseek(fd, offset << HFS_BLOCKSZ_BITS, SEEK_SET); + + if (result == -1) + ERROR(errno, "error seeking medium"); + + return (unsigned long) result >> HFS_BLOCKSZ_BITS; + +fail: + return -1; +} + +/* + * NAME: os->read() + * DESCRIPTION: read blocks from an open descriptor + */ +unsigned long os_read(void **priv, void *buf, unsigned long len) +{ + int fd = (int) *priv; + ssize_t result; + + result = read(fd, buf, len << HFS_BLOCKSZ_BITS); + + if (result == -1) + ERROR(errno, "error reading from medium"); + + return (unsigned long) result >> HFS_BLOCKSZ_BITS; + +fail: + return -1; +} + +/* + * NAME: os->write() + * DESCRIPTION: write blocks to an open descriptor + */ +unsigned long os_write(void **priv, const void *buf, unsigned long len) +{ + int fd = (int) *priv; + ssize_t result; + + result = write(fd, buf, len << HFS_BLOCKSZ_BITS); + + if (result == -1) + ERROR(errno, "error writing to medium"); + + return (unsigned long) result >> HFS_BLOCKSZ_BITS; + +fail: + return -1; +} diff --git a/libhfs/record.c b/libhfs/record.c new file mode 100644 index 0000000..6ad183c --- /dev/null +++ b/libhfs/record.c @@ -0,0 +1,557 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: record.c,v 1.9 1998/11/02 22:09:07 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include + +# include "libhfs.h" +# include "record.h" +# include "data.h" + +/* + * NAME: record->packcatkey() + * DESCRIPTION: pack a catalog record key + */ +void r_packcatkey(const CatKeyRec *key, byte *pkey, unsigned int *len) +{ + const byte *start = pkey; + + d_storesb(&pkey, key->ckrKeyLen); + d_storesb(&pkey, key->ckrResrv1); + d_storeul(&pkey, key->ckrParID); + + d_storestr(&pkey, key->ckrCName, sizeof(key->ckrCName)); + + if (len) + *len = HFS_RECKEYSKIP(start); +} + +/* + * NAME: record->unpackcatkey() + * DESCRIPTION: unpack a catalog record key + */ +void r_unpackcatkey(const byte *pkey, CatKeyRec *key) +{ + d_fetchsb(&pkey, &key->ckrKeyLen); + d_fetchsb(&pkey, &key->ckrResrv1); + d_fetchul(&pkey, &key->ckrParID); + + d_fetchstr(&pkey, key->ckrCName, sizeof(key->ckrCName)); +} + +/* + * NAME: record->packextkey() + * DESCRIPTION: pack an extents record key + */ +void r_packextkey(const ExtKeyRec *key, byte *pkey, unsigned int *len) +{ + const byte *start = pkey; + + d_storesb(&pkey, key->xkrKeyLen); + d_storesb(&pkey, key->xkrFkType); + d_storeul(&pkey, key->xkrFNum); + d_storeuw(&pkey, key->xkrFABN); + + if (len) + *len = HFS_RECKEYSKIP(start); +} + +/* + * NAME: record->unpackextkey() + * DESCRIPTION: unpack an extents record key + */ +void r_unpackextkey(const byte *pkey, ExtKeyRec *key) +{ + d_fetchsb(&pkey, &key->xkrKeyLen); + d_fetchsb(&pkey, &key->xkrFkType); + d_fetchul(&pkey, &key->xkrFNum); + d_fetchuw(&pkey, &key->xkrFABN); +} + +/* + * NAME: record->comparecatkeys() + * DESCRIPTION: compare two (packed) catalog record keys + */ +int r_comparecatkeys(const CatKeyRec *key1, const CatKeyRec *key2) +{ + int diff; + + diff = key1->ckrParID - key2->ckrParID; + if (diff) + return diff; + + return d_relstring(key1->ckrCName, key2->ckrCName); +} + +/* + * NAME: record->compareextkeys() + * DESCRIPTION: compare two (packed) extents record keys + */ +int r_compareextkeys(const ExtKeyRec *key1, const ExtKeyRec *key2) +{ + int diff; + + diff = key1->xkrFNum - key2->xkrFNum; + if (diff) + return diff; + + diff = (unsigned char) key1->xkrFkType - + (unsigned char) key2->xkrFkType; + if (diff) + return diff; + + return key1->xkrFABN - key2->xkrFABN; +} + +/* + * NAME: record->packcatdata() + * DESCRIPTION: pack catalog record data + */ +void r_packcatdata(const CatDataRec *data, byte *pdata, unsigned int *len) +{ + const byte *start = pdata; + int i; + + d_storesb(&pdata, data->cdrType); + d_storesb(&pdata, data->cdrResrv2); + + switch (data->cdrType) + { + case cdrDirRec: + d_storesw(&pdata, data->u.dir.dirFlags); + d_storeuw(&pdata, data->u.dir.dirVal); + d_storeul(&pdata, data->u.dir.dirDirID); + d_storesl(&pdata, data->u.dir.dirCrDat); + d_storesl(&pdata, data->u.dir.dirMdDat); + d_storesl(&pdata, data->u.dir.dirBkDat); + + d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.top); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.left); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.bottom); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.right); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frFlags); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.v); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.h); + d_storesw(&pdata, data->u.dir.dirUsrInfo.frView); + + d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.v); + d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.h); + d_storesl(&pdata, data->u.dir.dirFndrInfo.frOpenChain); + d_storesw(&pdata, data->u.dir.dirFndrInfo.frUnused); + d_storesw(&pdata, data->u.dir.dirFndrInfo.frComment); + d_storesl(&pdata, data->u.dir.dirFndrInfo.frPutAway); + + for (i = 0; i < 4; ++i) + d_storesl(&pdata, data->u.dir.dirResrv[i]); + + break; + + case cdrFilRec: + d_storesb(&pdata, data->u.fil.filFlags); + d_storesb(&pdata, data->u.fil.filTyp); + + d_storesl(&pdata, data->u.fil.filUsrWds.fdType); + d_storesl(&pdata, data->u.fil.filUsrWds.fdCreator); + d_storesw(&pdata, data->u.fil.filUsrWds.fdFlags); + d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.v); + d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.h); + d_storesw(&pdata, data->u.fil.filUsrWds.fdFldr); + + d_storeul(&pdata, data->u.fil.filFlNum); + + d_storeuw(&pdata, data->u.fil.filStBlk); + d_storeul(&pdata, data->u.fil.filLgLen); + d_storeul(&pdata, data->u.fil.filPyLen); + + d_storeuw(&pdata, data->u.fil.filRStBlk); + d_storeul(&pdata, data->u.fil.filRLgLen); + d_storeul(&pdata, data->u.fil.filRPyLen); + + d_storesl(&pdata, data->u.fil.filCrDat); + d_storesl(&pdata, data->u.fil.filMdDat); + d_storesl(&pdata, data->u.fil.filBkDat); + + d_storesw(&pdata, data->u.fil.filFndrInfo.fdIconID); + for (i = 0; i < 4; ++i) + d_storesw(&pdata, data->u.fil.filFndrInfo.fdUnused[i]); + d_storesw(&pdata, data->u.fil.filFndrInfo.fdComment); + d_storesl(&pdata, data->u.fil.filFndrInfo.fdPutAway); + + d_storeuw(&pdata, data->u.fil.filClpSize); + + for (i = 0; i < 3; ++i) + { + d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrStABN); + d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrNumABlks); + } + + for (i = 0; i < 3; ++i) + { + d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrStABN); + d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks); + } + + d_storesl(&pdata, data->u.fil.filResrv); + + break; + + case cdrThdRec: + for (i = 0; i < 2; ++i) + d_storesl(&pdata, data->u.dthd.thdResrv[i]); + + d_storeul(&pdata, data->u.dthd.thdParID); + + d_storestr(&pdata, data->u.dthd.thdCName, + sizeof(data->u.dthd.thdCName)); + + break; + + case cdrFThdRec: + for (i = 0; i < 2; ++i) + d_storesl(&pdata, data->u.fthd.fthdResrv[i]); + + d_storeul(&pdata, data->u.fthd.fthdParID); + + d_storestr(&pdata, data->u.fthd.fthdCName, + sizeof(data->u.fthd.fthdCName)); + + break; + + default: + ASSERT(0); + } + + if (len) + *len += pdata - start; +} + +/* + * NAME: record->unpackcatdata() + * DESCRIPTION: unpack catalog record data + */ +void r_unpackcatdata(const byte *pdata, CatDataRec *data) +{ + int i; + + d_fetchsb(&pdata, &data->cdrType); + d_fetchsb(&pdata, &data->cdrResrv2); + + switch (data->cdrType) + { + case cdrDirRec: + d_fetchsw(&pdata, &data->u.dir.dirFlags); + d_fetchuw(&pdata, &data->u.dir.dirVal); + d_fetchul(&pdata, &data->u.dir.dirDirID); + d_fetchsl(&pdata, &data->u.dir.dirCrDat); + d_fetchsl(&pdata, &data->u.dir.dirMdDat); + d_fetchsl(&pdata, &data->u.dir.dirBkDat); + + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.top); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.left); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.right); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frFlags); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h); + d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frView); + + d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v); + d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h); + d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain); + d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frUnused); + d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frComment); + d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frPutAway); + + for (i = 0; i < 4; ++i) + d_fetchsl(&pdata, &data->u.dir.dirResrv[i]); + + break; + + case cdrFilRec: + d_fetchsb(&pdata, &data->u.fil.filFlags); + d_fetchsb(&pdata, &data->u.fil.filTyp); + + d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdType); + d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdCreator); + d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFlags); + d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.v); + d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.h); + d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFldr); + + d_fetchul(&pdata, &data->u.fil.filFlNum); + + d_fetchuw(&pdata, &data->u.fil.filStBlk); + d_fetchul(&pdata, &data->u.fil.filLgLen); + d_fetchul(&pdata, &data->u.fil.filPyLen); + + d_fetchuw(&pdata, &data->u.fil.filRStBlk); + d_fetchul(&pdata, &data->u.fil.filRLgLen); + d_fetchul(&pdata, &data->u.fil.filRPyLen); + + d_fetchsl(&pdata, &data->u.fil.filCrDat); + d_fetchsl(&pdata, &data->u.fil.filMdDat); + d_fetchsl(&pdata, &data->u.fil.filBkDat); + + d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdIconID); + for (i = 0; i < 4; ++i) + d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]); + d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdComment); + d_fetchsl(&pdata, &data->u.fil.filFndrInfo.fdPutAway); + + d_fetchuw(&pdata, &data->u.fil.filClpSize); + + for (i = 0; i < 3; ++i) + { + d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrStABN); + d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrNumABlks); + } + + for (i = 0; i < 3; ++i) + { + d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrStABN); + d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrNumABlks); + } + + d_fetchsl(&pdata, &data->u.fil.filResrv); + + break; + + case cdrThdRec: + for (i = 0; i < 2; ++i) + d_fetchsl(&pdata, &data->u.dthd.thdResrv[i]); + + d_fetchul(&pdata, &data->u.dthd.thdParID); + + d_fetchstr(&pdata, data->u.dthd.thdCName, + sizeof(data->u.dthd.thdCName)); + + break; + + case cdrFThdRec: + for (i = 0; i < 2; ++i) + d_fetchsl(&pdata, &data->u.fthd.fthdResrv[i]); + + d_fetchul(&pdata, &data->u.fthd.fthdParID); + + d_fetchstr(&pdata, data->u.fthd.fthdCName, + sizeof(data->u.fthd.fthdCName)); + + break; + + default: + ASSERT(0); + } +} + +/* + * NAME: record->packextdata() + * DESCRIPTION: pack extent record data + */ +void r_packextdata(const ExtDataRec *data, byte *pdata, unsigned int *len) +{ + const byte *start = pdata; + int i; + + for (i = 0; i < 3; ++i) + { + d_storeuw(&pdata, (*data)[i].xdrStABN); + d_storeuw(&pdata, (*data)[i].xdrNumABlks); + } + + if (len) + *len += pdata - start; +} + +/* + * NAME: record->unpackextdata() + * DESCRIPTION: unpack extent record data + */ +void r_unpackextdata(const byte *pdata, ExtDataRec *data) +{ + int i; + + for (i = 0; i < 3; ++i) + { + d_fetchuw(&pdata, &(*data)[i].xdrStABN); + d_fetchuw(&pdata, &(*data)[i].xdrNumABlks); + } +} + +/* + * NAME: record->makecatkey() + * DESCRIPTION: construct a catalog record key + */ +void r_makecatkey(CatKeyRec *key, unsigned long parid, const char *name) +{ + int len; + + len = strlen(name) + 1; + + key->ckrKeyLen = 0x05 + len + (len & 1); + key->ckrResrv1 = 0; + key->ckrParID = parid; + + strcpy(key->ckrCName, name); +} + +/* + * NAME: record->makeextkey() + * DESCRIPTION: construct an extents record key + */ +void r_makeextkey(ExtKeyRec *key, + int fork, unsigned long fnum, unsigned int fabn) +{ + key->xkrKeyLen = 0x07; + key->xkrFkType = fork; + key->xkrFNum = fnum; + key->xkrFABN = fabn; +} + +/* + * NAME: record->packcatrec() + * DESCRIPTION: create a packed catalog record + */ +void r_packcatrec(const CatKeyRec *key, const CatDataRec *data, + byte *precord, unsigned int *len) +{ + r_packcatkey(key, precord, len); + r_packcatdata(data, HFS_RECDATA(precord), len); +} + +/* + * NAME: record->packextrec() + * DESCRIPTION: create a packed extents record + */ +void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data, + byte *precord, unsigned int *len) +{ + r_packextkey(key, precord, len); + r_packextdata(data, HFS_RECDATA(precord), len); +} + +/* + * NAME: record->packdirent() + * DESCRIPTION: make changes to a catalog record + */ +void r_packdirent(CatDataRec *data, const hfsdirent *ent) +{ + switch (data->cdrType) + { + case cdrDirRec: + data->u.dir.dirCrDat = d_mtime(ent->crdate); + data->u.dir.dirMdDat = d_mtime(ent->mddate); + data->u.dir.dirBkDat = d_mtime(ent->bkdate); + + data->u.dir.dirUsrInfo.frFlags = ent->fdflags; + data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v; + data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h; + + data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top; + data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left; + data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom; + data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right; + + break; + + case cdrFilRec: + if (ent->flags & HFS_ISLOCKED) + data->u.fil.filFlags |= (1 << 0); + else + data->u.fil.filFlags &= ~(1 << 0); + + data->u.fil.filCrDat = d_mtime(ent->crdate); + data->u.fil.filMdDat = d_mtime(ent->mddate); + data->u.fil.filBkDat = d_mtime(ent->bkdate); + + data->u.fil.filUsrWds.fdFlags = ent->fdflags; + data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v; + data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h; + + data->u.fil.filUsrWds.fdType = + d_getsl((const unsigned char *) ent->u.file.type); + data->u.fil.filUsrWds.fdCreator = + d_getsl((const unsigned char *) ent->u.file.creator); + + break; + } +} + +/* + * NAME: record->unpackdirent() + * DESCRIPTION: unpack catalog information into hfsdirent structure + */ +void r_unpackdirent(unsigned long parid, const char *name, + const CatDataRec *data, hfsdirent *ent) +{ + strcpy(ent->name, name); + ent->parid = parid; + + switch (data->cdrType) + { + case cdrDirRec: + ent->flags = HFS_ISDIR; + ent->cnid = data->u.dir.dirDirID; + + ent->crdate = d_ltime(data->u.dir.dirCrDat); + ent->mddate = d_ltime(data->u.dir.dirMdDat); + ent->bkdate = d_ltime(data->u.dir.dirBkDat); + + ent->fdflags = data->u.dir.dirUsrInfo.frFlags; + ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v; + ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h; + + ent->u.dir.valence = data->u.dir.dirVal; + + ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top; + ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left; + ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom; + ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right; + + break; + + case cdrFilRec: + ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0; + ent->cnid = data->u.fil.filFlNum; + + ent->crdate = d_ltime(data->u.fil.filCrDat); + ent->mddate = d_ltime(data->u.fil.filMdDat); + ent->bkdate = d_ltime(data->u.fil.filBkDat); + + ent->fdflags = data->u.fil.filUsrWds.fdFlags; + ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v; + ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h; + + ent->u.file.dsize = data->u.fil.filLgLen; + ent->u.file.rsize = data->u.fil.filRLgLen; + + d_putsl((unsigned char *) ent->u.file.type, + data->u.fil.filUsrWds.fdType); + d_putsl((unsigned char *) ent->u.file.creator, + data->u.fil.filUsrWds.fdCreator); + + ent->u.file.type[4] = ent->u.file.creator[4] = 0; + + break; + } +} diff --git a/libhfs/record.h b/libhfs/record.h new file mode 100644 index 0000000..4959dc8 --- /dev/null +++ b/libhfs/record.h @@ -0,0 +1,47 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: record.h,v 1.7 1998/11/02 22:09:08 rob Exp $ + */ + +void r_packcatkey(const CatKeyRec *, byte *, unsigned int *); +void r_unpackcatkey(const byte *, CatKeyRec *); + +void r_packextkey(const ExtKeyRec *, byte *, unsigned int *); +void r_unpackextkey(const byte *, ExtKeyRec *); + +int r_comparecatkeys(const CatKeyRec *, const CatKeyRec *); +int r_compareextkeys(const ExtKeyRec *, const ExtKeyRec *); + +void r_packcatdata(const CatDataRec *, byte *, unsigned int *); +void r_unpackcatdata(const byte *, CatDataRec *); + +void r_packextdata(const ExtDataRec *, byte *, unsigned int *); +void r_unpackextdata(const byte *, ExtDataRec *); + +void r_makecatkey(CatKeyRec *, unsigned long, const char *); +void r_makeextkey(ExtKeyRec *, int, unsigned long, unsigned int); + +void r_packcatrec(const CatKeyRec *, const CatDataRec *, + byte *, unsigned int *); +void r_packextrec(const ExtKeyRec *, const ExtDataRec *, + byte *, unsigned int *); + +void r_packdirent(CatDataRec *, const hfsdirent *); +void r_unpackdirent(unsigned long, const char *, + const CatDataRec *, hfsdirent *); diff --git a/libhfs/version.c b/libhfs/version.c new file mode 100644 index 0000000..20fd093 --- /dev/null +++ b/libhfs/version.c @@ -0,0 +1,29 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $ + */ + +# include "version.h" + +const char libhfs_rcsid[] = + "$Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $"; + +const char libhfs_version[] = "libhfs version 3.2.6"; +const char libhfs_copyright[] = "Copyright (C) 1996-1998 Robert Leslie"; +const char libhfs_author[] = "Robert Leslie "; diff --git a/libhfs/version.h b/libhfs/version.h new file mode 100644 index 0000000..ae6a1d3 --- /dev/null +++ b/libhfs/version.h @@ -0,0 +1,26 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.h,v 1.6 1998/09/18 22:56:38 rob Exp $ + */ + +extern const char libhfs_rcsid[]; + +extern const char libhfs_version[]; +extern const char libhfs_copyright[]; +extern const char libhfs_author[]; diff --git a/libhfs/volume.c b/libhfs/volume.c new file mode 100644 index 0000000..37c41d7 --- /dev/null +++ b/libhfs/volume.c @@ -0,0 +1,1202 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include +# include + +# include "libhfs.h" +# include "volume.h" +# include "data.h" +# include "block.h" +# include "low.h" +# include "medium.h" +# include "file.h" +# include "btree.h" +# include "record.h" +# include "os.h" + +/* + * NAME: vol->init() + * DESCRIPTION: initialize volume structure + */ +void v_init(hfsvol *vol, int flags) +{ + btree *ext = &vol->ext; + btree *cat = &vol->cat; + + vol->priv = 0; + vol->flags = flags & HFS_VOL_OPT_MASK; + + vol->pnum = -1; + vol->vstart = 0; + vol->vlen = 0; + vol->lpa = 0; + + vol->cache = 0; + + vol->vbm = 0; + vol->vbmsz = 0; + + f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow"); + + ext->map = 0; + ext->mapsz = 0; + ext->flags = 0; + + ext->keyunpack = (keyunpackfunc) r_unpackextkey; + ext->keycompare = (keycomparefunc) r_compareextkeys; + + f_init(&cat->f, vol, HFS_CNID_CAT, "catalog"); + + cat->map = 0; + cat->mapsz = 0; + cat->flags = 0; + + cat->keyunpack = (keyunpackfunc) r_unpackcatkey; + cat->keycompare = (keycomparefunc) r_comparecatkeys; + + vol->cwd = HFS_CNID_ROOTDIR; + + vol->refs = 0; + vol->files = 0; + vol->dirs = 0; + + vol->prev = 0; + vol->next = 0; +} + +/* + * NAME: vol->open() + * DESCRIPTION: open volume source and lock against concurrent updates + */ +int v_open(hfsvol *vol, const char *path, int mode) +{ + if (vol->flags & HFS_VOL_OPEN) + ERROR(EINVAL, "volume already open"); + + if (os_open(&vol->priv, path, mode) == -1) + goto fail; + + vol->flags |= HFS_VOL_OPEN; + + /* initialize volume block cache (OK to fail) */ + + if (! (vol->flags & HFS_OPT_NOCACHE) && + b_init(vol) != -1) + vol->flags |= HFS_VOL_USINGCACHE; + + return 0; + +fail: + return -1; +} + +/* + * NAME: flushvol() + * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to volume + */ +static +int flushvol(hfsvol *vol, int umount) +{ + if (vol->flags & HFS_VOL_READONLY) + goto done; + + if ((vol->ext.flags & HFS_BT_UPDATE_HDR) && + bt_writehdr(&vol->ext) == -1) + goto fail; + + if ((vol->cat.flags & HFS_BT_UPDATE_HDR) && + bt_writehdr(&vol->cat) == -1) + goto fail; + + if ((vol->flags & HFS_VOL_UPDATE_VBM) && + v_writevbm(vol) == -1) + goto fail; + + if (umount && ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) + { + vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + if ((vol->flags & (HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB)) && + v_writemdb(vol) == -1) + goto fail; + +done: + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->flush() + * DESCRIPTION: commit all pending changes to volume device + */ +int v_flush(hfsvol *vol) +{ + if (flushvol(vol, 0) == -1) + goto fail; + + if ((vol->flags & HFS_VOL_USINGCACHE) && + b_flush(vol) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->close() + * DESCRIPTION: close access path to volume source + */ +int v_close(hfsvol *vol) +{ + int result = 0; + + if (! (vol->flags & HFS_VOL_OPEN)) + goto done; + + if ((vol->flags & HFS_VOL_MOUNTED) && + flushvol(vol, 1) == -1) + result = -1; + + if ((vol->flags & HFS_VOL_USINGCACHE) && + b_finish(vol) == -1) + result = -1; + + if (os_close(&vol->priv) == -1) + result = -1; + + vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE); + + /* free dynamically allocated structures */ + + FREE(vol->vbm); + + vol->vbm = 0; + vol->vbmsz = 0; + + FREE(vol->ext.map); + FREE(vol->cat.map); + + vol->ext.map = 0; + vol->cat.map = 0; + +done: + return result; +} + +/* + * NAME: vol->same() + * DESCRIPTION: return 1 iff path is same as open volume + */ +int v_same(hfsvol *vol, const char *path) +{ + return os_same(&vol->priv, path); +} + +/* + * NAME: vol->geometry() + * DESCRIPTION: determine volume location and size (possibly in a partition) + */ +int v_geometry(hfsvol *vol, int pnum) +{ + Partition map; + unsigned long bnum = 0; + int found; + + vol->pnum = pnum; + + if (pnum == 0) + { + vol->vstart = 0; + vol->vlen = b_size(vol); + + if (vol->vlen == 0) + goto fail; + } + else + { + while (pnum--) + { + found = m_findpmentry(vol, "Apple_HFS", &map, &bnum); + if (found == -1 || ! found) + goto fail; + } + + vol->vstart = map.pmPyPartStart; + vol->vlen = map.pmPartBlkCnt; + + if (map.pmDataCnt) + { + if ((unsigned long) map.pmLgDataStart + + (unsigned long) map.pmDataCnt > vol->vlen) + ERROR(EINVAL, "partition data overflows partition"); + + vol->vstart += (unsigned long) map.pmLgDataStart; + vol->vlen = map.pmDataCnt; + } + + if (vol->vlen == 0) + ERROR(EINVAL, "volume partition is empty"); + } + + if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS)) + ERROR(EINVAL, "volume is smaller than 800K"); + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->readmdb() + * DESCRIPTION: load Master Directory Block into memory + */ +int v_readmdb(hfsvol *vol) +{ + if (l_getmdb(vol, &vol->mdb, 0) == -1) + goto fail; + + if (vol->mdb.drSigWord != HFS_SIGWORD) + { + if (vol->mdb.drSigWord == HFS_SIGWORD_MFS) + ERROR(EINVAL, "MFS volume format not supported"); + else + ERROR(EINVAL, "not a Macintosh HFS volume"); + } + + if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) + ERROR(EINVAL, "bad volume allocation block size"); + + vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS; + + /* extents pseudo-file structs */ + + vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN; + vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize; + vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize; + + vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; + vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; + + memcpy(&vol->ext.f.cat.u.fil.filExtRec, + &vol->mdb.drXTExtRec, sizeof(ExtDataRec)); + + f_selectfork(&vol->ext.f, fkData); + + /* catalog pseudo-file structs */ + + vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN; + vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize; + vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize; + + vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; + vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; + + memcpy(&vol->cat.f.cat.u.fil.filExtRec, + &vol->mdb.drCTExtRec, sizeof(ExtDataRec)); + + f_selectfork(&vol->cat.f, fkData); + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->writemdb() + * DESCRIPTION: flush Master Directory Block to medium + */ +int v_writemdb(hfsvol *vol) +{ + vol->mdb.drLsMod = d_mtime(time(0)); + + vol->mdb.drXTFlSize = vol->ext.f.cat.u.fil.filPyLen; + memcpy(&vol->mdb.drXTExtRec, + &vol->ext.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); + + vol->mdb.drCTFlSize = vol->cat.f.cat.u.fil.filPyLen; + memcpy(&vol->mdb.drCTExtRec, + &vol->cat.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); + + if (l_putmdb(vol, &vol->mdb, vol->flags & HFS_VOL_UPDATE_ALTMDB) == -1) + goto fail; + + vol->flags &= ~(HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB); + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->readvbm() + * DESCRIPTION: read volume bitmap into memory + */ +int v_readvbm(hfsvol *vol) +{ + unsigned int vbmst = vol->mdb.drVBMSt; + unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12; + block *bp; + + ASSERT(vol->vbm == 0); + + if (vol->mdb.drAlBlSt - vbmst < vbmsz) + ERROR(EIO, "volume bitmap collides with volume data"); + + vol->vbm = ALLOC(block, vbmsz); + if (vol->vbm == 0) + ERROR(ENOMEM, 0); + + vol->vbmsz = vbmsz; + + for (bp = vol->vbm; vbmsz--; ++bp) + { + if (b_readlb(vol, vbmst++, bp) == -1) + goto fail; + } + + return 0; + +fail: + FREE(vol->vbm); + + vol->vbm = 0; + vol->vbmsz = 0; + + return -1; +} + +/* + * NAME: vol->writevbm() + * DESCRIPTION: flush volume bitmap to medium + */ +int v_writevbm(hfsvol *vol) +{ + unsigned int vbmst = vol->mdb.drVBMSt; + unsigned int vbmsz = vol->vbmsz; + const block *bp; + + for (bp = vol->vbm; vbmsz--; ++bp) + { + if (b_writelb(vol, vbmst++, bp) == -1) + goto fail; + } + + vol->flags &= ~HFS_VOL_UPDATE_VBM; + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->mount() + * DESCRIPTION: load volume information into memory + */ +int v_mount(hfsvol *vol) +{ + /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */ + + if (v_readmdb(vol) == -1 || + v_readvbm(vol) == -1 || + bt_readhdr(&vol->ext) == -1 || + bt_readhdr(&vol->cat) == -1) + goto fail; + + if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) && + v_scavenge(vol) == -1) + goto fail; + + if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED) + vol->flags |= HFS_VOL_READONLY; + else if (vol->flags & HFS_VOL_READONLY) + vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; + else + vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; + + vol->flags |= HFS_VOL_MOUNTED; + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->dirty() + * DESCRIPTION: ensure the volume is marked "in use" before we make changes + */ +int v_dirty(hfsvol *vol) +{ + if (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) + { + vol->mdb.drAtrb &= ~HFS_ATRB_UMOUNTED; + ++vol->mdb.drWrCnt; + + if (v_writemdb(vol) == -1) + goto fail; + + if ((vol->flags & HFS_VOL_USINGCACHE) && + b_flush(vol) == -1) + goto fail; + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->catsearch() + * DESCRIPTION: search catalog tree + */ +int v_catsearch(hfsvol *vol, unsigned long parid, const char *name, + CatDataRec *data, char *cname, node *np) +{ + CatKeyRec key; + byte pkey[HFS_CATKEYLEN]; + const byte *ptr; + node n; + int found; + + if (np == 0) + np = &n; + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, pkey, 0); + + found = bt_search(&vol->cat, pkey, np); + if (found <= 0) + return found; + + ptr = HFS_NODEREC(*np, np->rnum); + + if (cname) + { + r_unpackcatkey(ptr, &key); + strcpy(cname, key.ckrCName); + } + + if (data) + r_unpackcatdata(HFS_RECDATA(ptr), data); + + return 1; +} + +/* + * NAME: vol->extsearch() + * DESCRIPTION: search extents tree + */ +int v_extsearch(hfsfile *file, unsigned int fabn, + ExtDataRec *data, node *np) +{ + ExtKeyRec key; + ExtDataRec extsave; + unsigned int fabnsave; + byte pkey[HFS_EXTKEYLEN]; + const byte *ptr; + node n; + int found; + + if (np == 0) + np = &n; + + r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn); + r_packextkey(&key, pkey, 0); + + /* in case bt_search() clobbers these */ + + memcpy(&extsave, &file->ext, sizeof(ExtDataRec)); + fabnsave = file->fabn; + + found = bt_search(&file->vol->ext, pkey, np); + + memcpy(&file->ext, &extsave, sizeof(ExtDataRec)); + file->fabn = fabnsave; + + if (found <= 0) + return found; + + if (data) + { + ptr = HFS_NODEREC(*np, np->rnum); + r_unpackextdata(HFS_RECDATA(ptr), data); + } + + return 1; +} + +/* + * NAME: vol->getthread() + * DESCRIPTION: retrieve catalog thread information for a file or directory + */ +int v_getthread(hfsvol *vol, unsigned long id, + CatDataRec *thread, node *np, int type) +{ + CatDataRec rec; + int found; + + if (thread == 0) + thread = &rec; + + found = v_catsearch(vol, id, "", thread, 0, np); + if (found == 1 && thread->cdrType != type) + ERROR(EIO, "bad thread record"); + + return found; + +fail: + return -1; +} + +/* + * NAME: vol->putcatrec() + * DESCRIPTION: store catalog information + */ +int v_putcatrec(const CatDataRec *data, node *np) +{ + byte pdata[HFS_CATDATALEN], *ptr; + unsigned int len = 0; + + r_packcatdata(data, pdata, &len); + + ptr = HFS_NODEREC(*np, np->rnum); + memcpy(HFS_RECDATA(ptr), pdata, len); + + return bt_putnode(np); +} + +/* + * NAME: vol->putextrec() + * DESCRIPTION: store extent information + */ +int v_putextrec(const ExtDataRec *data, node *np) +{ + byte pdata[HFS_EXTDATALEN], *ptr; + unsigned int len = 0; + + r_packextdata(data, pdata, &len); + + ptr = HFS_NODEREC(*np, np->rnum); + memcpy(HFS_RECDATA(ptr), pdata, len); + + return bt_putnode(np); +} + +/* + * NAME: vol->allocblocks() + * DESCRIPTION: allocate a contiguous range of blocks + */ +int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks) +{ + unsigned int request, found, foundat, start, end; + register unsigned int pt; + block *vbm; + int wrap = 0; + + if (vol->mdb.drFreeBks == 0) + ERROR(ENOSPC, "volume full"); + + request = blocks->xdrNumABlks; + found = 0; + foundat = 0; + start = vol->mdb.drAllocPtr; + end = vol->mdb.drNmAlBlks; + vbm = vol->vbm; + + ASSERT(request > 0); + + /* backtrack the start pointer to recover unused space */ + + if (! BMTST(vbm, start)) + { + while (start > 0 && ! BMTST(vbm, start - 1)) + --start; + } + + /* find largest unused block which satisfies request */ + + pt = start; + + while (1) + { + unsigned int mark; + + /* skip blocks in use */ + + while (pt < end && BMTST(vbm, pt)) + ++pt; + + if (wrap && pt >= start) + break; + + /* count blocks not in use */ + + mark = pt; + while (pt < end && pt - mark < request && ! BMTST(vbm, pt)) + ++pt; + + if (pt - mark > found) + { + found = pt - mark; + foundat = mark; + } + + if (wrap && pt >= start) + break; + + if (pt == end) + pt = 0, wrap = 1; + + if (found == request) + break; + } + + if (found == 0 || found > vol->mdb.drFreeBks) + ERROR(EIO, "bad volume bitmap or free block count"); + + blocks->xdrStABN = foundat; + blocks->xdrNumABlks = found; + + if (v_dirty(vol) == -1) + goto fail; + + vol->mdb.drAllocPtr = pt; + vol->mdb.drFreeBks -= found; + + for (pt = foundat; pt < foundat + found; ++pt) + BMSET(vbm, pt); + + vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; + + if (vol->flags & HFS_OPT_ZERO) + { + block b; + unsigned int i; + + memset(&b, 0, sizeof(b)); + + for (pt = foundat; pt < foundat + found; ++pt) + { + for (i = 0; i < vol->lpa; ++i) + b_writeab(vol, pt, i, &b); + } + } + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->freeblocks() + * DESCRIPTION: deallocate a contiguous range of blocks + */ +int v_freeblocks(hfsvol *vol, const ExtDescriptor *blocks) +{ + unsigned int start, len, pt; + block *vbm; + + start = blocks->xdrStABN; + len = blocks->xdrNumABlks; + vbm = vol->vbm; + + if (v_dirty(vol) == -1) + goto fail; + + vol->mdb.drFreeBks += len; + + for (pt = start; pt < start + len; ++pt) + BMCLR(vbm, pt); + + vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; + + return 0; + +fail: + return -1; +} + +/* + * NAME: vol->resolve() + * DESCRIPTION: translate a pathname; return catalog information + */ +int v_resolve(hfsvol **vol, const char *path, + CatDataRec *data, long *parid, char *fname, node *np) +{ + unsigned long dirid; + char name[HFS_MAX_FLEN + 1], *nptr; + int found = 0; + + if (*path == 0) + ERROR(ENOENT, "empty path"); + + if (parid) + *parid = 0; + + nptr = strchr(path, ':'); + + if (*path == ':' || nptr == 0) + { + dirid = (*vol)->cwd; /* relative path */ + + if (*path == ':') + ++path; + + if (*path == 0) + { + found = v_getdthread(*vol, dirid, data, 0); + if (found == -1) + goto fail; + + if (found) + { + if (parid) + *parid = data->u.dthd.thdParID; + + found = v_catsearch(*vol, data->u.dthd.thdParID, + data->u.dthd.thdCName, data, fname, np); + if (found == -1) + goto fail; + } + + goto done; + } + } + else + { + hfsvol *check; + + dirid = HFS_CNID_ROOTPAR; /* absolute path */ + + if (nptr - path > HFS_MAX_VLEN) + ERROR(ENAMETOOLONG, 0); + + strncpy(name, path, nptr - path); + name[nptr - path] = 0; + + for (check = hfs_mounts; check; check = check->next) + { + if (d_relstring(check->mdb.drVN, name) == 0) + { + *vol = check; + break; + } + } + } + + while (1) + { + while (*path == ':') + { + ++path; + + found = v_getdthread(*vol, dirid, data, 0); + if (found == -1) + goto fail; + else if (! found) + goto done; + + dirid = data->u.dthd.thdParID; + } + + if (*path == 0) + { + found = v_getdthread(*vol, dirid, data, 0); + if (found == -1) + goto fail; + + if (found) + { + if (parid) + *parid = data->u.dthd.thdParID; + + found = v_catsearch(*vol, data->u.dthd.thdParID, + data->u.dthd.thdCName, data, fname, np); + if (found == -1) + goto fail; + } + + goto done; + } + + nptr = name; + while (nptr < name + sizeof(name) - 1 && *path && *path != ':') + *nptr++ = *path++; + + if (*path && *path != ':') + ERROR(ENAMETOOLONG, 0); + + *nptr = 0; + if (*path == ':') + ++path; + + if (parid) + *parid = dirid; + + found = v_catsearch(*vol, dirid, name, data, fname, np); + if (found == -1) + goto fail; + + if (! found) + { + if (*path && parid) + *parid = 0; + + if (*path == 0 && fname) + strcpy(fname, name); + + goto done; + } + + switch (data->cdrType) + { + case cdrDirRec: + if (*path == 0) + goto done; + + dirid = data->u.dir.dirDirID; + break; + + case cdrFilRec: + if (*path == 0) + goto done; + + ERROR(ENOTDIR, "invalid pathname"); + + default: + ERROR(EIO, "unexpected catalog record"); + } + } + +done: + return found; + +fail: + return -1; +} + +/* + * NAME: vol->adjvalence() + * DESCRIPTION: update a volume's valence counts + */ +int v_adjvalence(hfsvol *vol, unsigned long parid, int isdir, int adj) +{ + node n; + CatDataRec data; + int result = 0; + + if (isdir) + vol->mdb.drDirCnt += adj; + else + vol->mdb.drFilCnt += adj; + + vol->flags |= HFS_VOL_UPDATE_MDB; + + if (parid == HFS_CNID_ROOTDIR) + { + if (isdir) + vol->mdb.drNmRtDirs += adj; + else + vol->mdb.drNmFls += adj; + } + else if (parid == HFS_CNID_ROOTPAR) + goto done; + + if (v_getdthread(vol, parid, &data, 0) <= 0 || + v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName, + &data, 0, &n) <= 0 || + data.cdrType != cdrDirRec) + ERROR(EIO, "can't find parent directory"); + + data.u.dir.dirVal += adj; + data.u.dir.dirMdDat = d_mtime(time(0)); + + result = v_putcatrec(&data, &n); + +done: + return result; + +fail: + return -1; +} + +/* + * NAME: vol->mkdir() + * DESCRIPTION: create a new HFS directory + */ +int v_mkdir(hfsvol *vol, unsigned long parid, const char *name) +{ + CatKeyRec key; + CatDataRec data; + unsigned long id; + byte record[HFS_MAX_CATRECLEN]; + unsigned int reclen; + int i; + + if (bt_space(&vol->cat, 2) == -1) + goto fail; + + id = vol->mdb.drNxtCNID++; + vol->flags |= HFS_VOL_UPDATE_MDB; + + /* create directory record */ + + data.cdrType = cdrDirRec; + data.cdrResrv2 = 0; + + data.u.dir.dirFlags = 0; + data.u.dir.dirVal = 0; + data.u.dir.dirDirID = id; + data.u.dir.dirCrDat = d_mtime(time(0)); + data.u.dir.dirMdDat = data.u.dir.dirCrDat; + data.u.dir.dirBkDat = 0; + + memset(&data.u.dir.dirUsrInfo, 0, sizeof(data.u.dir.dirUsrInfo)); + memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo)); + for (i = 0; i < 4; ++i) + data.u.dir.dirResrv[i] = 0; + + r_makecatkey(&key, parid, name); + r_packcatrec(&key, &data, record, &reclen); + + if (bt_insert(&vol->cat, record, reclen) == -1) + goto fail; + + /* create thread record */ + + data.cdrType = cdrThdRec; + data.cdrResrv2 = 0; + + data.u.dthd.thdResrv[0] = 0; + data.u.dthd.thdResrv[1] = 0; + data.u.dthd.thdParID = parid; + strcpy(data.u.dthd.thdCName, name); + + r_makecatkey(&key, id, ""); + r_packcatrec(&key, &data, record, &reclen); + + if (bt_insert(&vol->cat, record, reclen) == -1 || + v_adjvalence(vol, parid, 1, 1) == -1) + goto fail; + + return 0; + +fail: + return -1; +} + +/* + * NAME: markexts() + * DESCRIPTION: set bits from an extent record in the volume bitmap + */ +static +void markexts(block *vbm, const ExtDataRec *exts) +{ + int i; + unsigned int pt, len; + + for (i = 0; i < 3; ++i) + { + for ( pt = (*exts)[i].xdrStABN, + len = (*exts)[i].xdrNumABlks; len--; ++pt) + BMSET(vbm, pt); + } +} + +/* + * NAME: vol->scavenge() + * DESCRIPTION: safeguard blocks in the volume bitmap + */ +int v_scavenge(hfsvol *vol) +{ + block *vbm = vol->vbm; + node n; + unsigned int pt, blks; + unsigned long lastcnid = 15; + +# ifdef DEBUG + fprintf(stderr, "VOL: \"%s\" not cleanly unmounted\n", + vol->mdb.drVN); +# endif + + if (vol->flags & HFS_VOL_READONLY) + goto done; + +# ifdef DEBUG + fprintf(stderr, "VOL: scavenging...\n"); +# endif + + /* reset MDB by marking it dirty again */ + + vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; + if (v_dirty(vol) == -1) + goto fail; + + /* begin by marking extents in MDB */ + + markexts(vbm, &vol->mdb.drXTExtRec); + markexts(vbm, &vol->mdb.drCTExtRec); + + vol->flags |= HFS_VOL_UPDATE_VBM; + + /* scavenge the extents overflow file */ + + if (vol->ext.hdr.bthFNode > 0) + { + if (bt_getnode(&n, &vol->ext, vol->ext.hdr.bthFNode) == -1) + goto fail; + + n.rnum = 0; + + while (1) + { + ExtDataRec data; + const byte *ptr; + + while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) + { + if (bt_getnode(&n, &vol->ext, n.nd.ndFLink) == -1) + goto fail; + + n.rnum = 0; + } + + if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) + break; + + ptr = HFS_NODEREC(n, n.rnum); + r_unpackextdata(HFS_RECDATA(ptr), &data); + + markexts(vbm, &data); + + ++n.rnum; + } + } + + /* scavenge the catalog file */ + + if (vol->cat.hdr.bthFNode > 0) + { + if (bt_getnode(&n, &vol->cat, vol->cat.hdr.bthFNode) == -1) + goto fail; + + n.rnum = 0; + + while (1) + { + CatDataRec data; + const byte *ptr; + + while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) + { + if (bt_getnode(&n, &vol->cat, n.nd.ndFLink) == -1) + goto fail; + + n.rnum = 0; + } + + if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) + break; + + ptr = HFS_NODEREC(n, n.rnum); + r_unpackcatdata(HFS_RECDATA(ptr), &data); + + switch (data.cdrType) + { + case cdrFilRec: + markexts(vbm, &data.u.fil.filExtRec); + markexts(vbm, &data.u.fil.filRExtRec); + + if (data.u.fil.filFlNum > lastcnid) + lastcnid = data.u.fil.filFlNum; + break; + + case cdrDirRec: + if (data.u.dir.dirDirID > lastcnid) + lastcnid = data.u.dir.dirDirID; + break; + } + + ++n.rnum; + } + } + + /* count free blocks */ + + for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; ) + { + if (! BMTST(vbm, pt)) + ++blks; + } + + if (vol->mdb.drFreeBks != blks) + { +# ifdef DEBUG + fprintf(stderr, "VOL: updating free blocks from %u to %u\n", + vol->mdb.drFreeBks, blks); +# endif + + vol->mdb.drFreeBks = blks; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + + /* ensure next CNID is sane */ + + if ((unsigned long) vol->mdb.drNxtCNID <= lastcnid) + { +# ifdef DEBUG + fprintf(stderr, "VOL: updating next CNID from %lu to %lu\n", + vol->mdb.drNxtCNID, lastcnid + 1); +# endif + + vol->mdb.drNxtCNID = lastcnid + 1; + vol->flags |= HFS_VOL_UPDATE_MDB; + } + +# ifdef DEBUG + fprintf(stderr, "VOL: scavenging complete\n"); +# endif + +done: + return 0; + +fail: + return -1; +} diff --git a/libhfs/volume.h b/libhfs/volume.h new file mode 100644 index 0000000..ae8a093 --- /dev/null +++ b/libhfs/volume.h @@ -0,0 +1,62 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: volume.h,v 1.7 1998/11/02 22:09:12 rob Exp $ + */ + +void v_init(hfsvol *, int); + +int v_open(hfsvol *, const char *, int); +int v_flush(hfsvol *); +int v_close(hfsvol *); + +int v_same(hfsvol *, const char *); +int v_geometry(hfsvol *, int); + +int v_readmdb(hfsvol *); +int v_writemdb(hfsvol *); + +int v_readvbm(hfsvol *); +int v_writevbm(hfsvol *); + +int v_mount(hfsvol *); +int v_dirty(hfsvol *); + +int v_catsearch(hfsvol *, unsigned long, const char *, + CatDataRec *, char *, node *); +int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *); + +int v_getthread(hfsvol *, unsigned long, CatDataRec *, node *, int); + +# define v_getdthread(vol, id, thread, np) \ + v_getthread(vol, id, thread, np, cdrThdRec) +# define v_getfthread(vol, id, thread, np) \ + v_getthread(vol, id, thread, np, cdrFThdRec) + +int v_putcatrec(const CatDataRec *, node *); +int v_putextrec(const ExtDataRec *, node *); + +int v_allocblocks(hfsvol *, ExtDescriptor *); +int v_freeblocks(hfsvol *, const ExtDescriptor *); + +int v_resolve(hfsvol **, const char *, CatDataRec *, long *, char *, node *); + +int v_adjvalence(hfsvol *, unsigned long, int, int); +int v_mkdir(hfsvol *, unsigned long, const char *); + +int v_scavenge(hfsvol *); diff --git a/librsrc/.stamp/config.h b/librsrc/.stamp/config.h new file mode 100644 index 0000000..e69de29 diff --git a/librsrc/.stamp/config.h.in b/librsrc/.stamp/config.h.in new file mode 100644 index 0000000..e69de29 diff --git a/librsrc/.stamp/configure b/librsrc/.stamp/configure new file mode 100644 index 0000000..e69de29 diff --git a/librsrc/Makefile.in b/librsrc/Makefile.in new file mode 100644 index 0000000..f4912fe --- /dev/null +++ b/librsrc/Makefile.in @@ -0,0 +1,127 @@ +# +# librsrc - library for reading and writing Macintosh resources +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile.in,v 1.8 1998/04/12 01:33:26 rob Exp $ +# + +@SET_MAKE@ +src = @srcdir@ +VPATH = @srcdir@ + +### USER CUSTOMIZATIONS FOLLOW ################################################ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +MANDEST = @mandir@ + +INCDEST = @includedir@ +LIBDEST = @libdir@ + +INSTALL = @INSTALL@ +LIBINSTALL = @INSTALL_DATA@ + +CC = @CC@ +INCLUDES = @CPPFLAGS@ +DEFINES = @DEFS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ + +COPTS = @CFLAGS@ +LDOPTS = @LDFLAGS@ + +AR = ar rc +RANLIB = @RANLIB@ + +### END OF USER CUSTOMIZATION ################################################# + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(RSRCTARGET) + +RSRCTARGET = librsrc.a +RSRCOBJS = data.o rsrc.o version.o $(LIBOBJS) + +############################################################################### + +all :: $(TARGETS) + +check :: all + @echo "No self-tests available." + +install :: + $(LIBINSTALL) librsrc.a "$(LIBDEST)/." + $(LIBINSTALL) rsrc.h "$(INCDEST)/." + +again :: clean all + +depend :: + ( sed -n '1,/^### DEPEND/p' Makefile.in; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.in.new + mv -f Makefile.in.new Makefile.in + +clean :: + rm -f $(TARGETS) *.o gmon.* core + rm -f main + +distclean :: clean + rm -f config.status config.cache config.log config.h Makefile + +maintainer-clean :: distclean + rm -f config.h.in configure + +############################################################################### + +Makefile: config.status + ./config.status && touch .stamp/config.h + +.stamp/config.h: config.status + ./config.status && touch .stamp/config.h + +config.status: .stamp/configure .stamp/config.h.in Makefile.in + ./config.status --recheck + +.stamp/configure: configure.in + autoconf && touch .stamp/configure + +.stamp/config.h.in: configure.in acconfig.h + autoheader && touch .stamp/config.h.in + +############################################################################### + +$(RSRCTARGET): $(RSRCOBJS) + $(AR) $@ $(RSRCOBJS) + $(RANLIB) $@ + +main.o: + $(CC) $(CFLAGS) -I. -I../libhfs -c main.c -o $@ + +main: librsrc.a main.o + $(CC) $(LDFLAGS) -L. -L../libhfs main.o -lhfs -lrsrc -o $@ + +### DEPENDENCIES FOLLOW ####################################################### + +data.o: data.c config.h data.h +main.o: main.c /usr/local/include/hfs.h rsrc.h +rsrc.o: rsrc.c config.h librsrc.h rsrc.h data.h +version.o: version.c version.h diff --git a/librsrc/acconfig.h b/librsrc/acconfig.h new file mode 100644 index 0000000..1174db6 --- /dev/null +++ b/librsrc/acconfig.h @@ -0,0 +1,34 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:27:18 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ +@TOP@ + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +@BOTTOM@ + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ diff --git a/librsrc/config.h.in b/librsrc/config.h.in new file mode 100644 index 0000000..6f5b622 --- /dev/null +++ b/librsrc/config.h.in @@ -0,0 +1,53 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: acconfig.h,v 1.5 1998/04/11 08:27:18 rob Exp $ + */ + +/***************************************************************************** + * Definitions selected automatically by `configure' * + *****************************************************************************/ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if you want to enable diagnostic debugging support. */ +#undef DEBUG + +/* Define if you have the mktime function. */ +#undef HAVE_MKTIME + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/***************************************************************************** + * End of automatically configured definitions * + *****************************************************************************/ diff --git a/librsrc/configure b/librsrc/configure new file mode 100755 index 0000000..302f166 --- /dev/null +++ b/librsrc/configure @@ -0,0 +1,1662 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --enable-debug enable diagnostic debugging support" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=librsrc.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + : +fi + + +if test "x$enable_debug" = xyes +then + cat >> confdefs.h <<\EOF +#define DEBUG 1 +EOF + +fi + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:543: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:572: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:601: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:649: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:683: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:688: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:712: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:769: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:821: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:849: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:870: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:887: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:911: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:959: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:972: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1039: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in unistd.h fcntl.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1066: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1076: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1105: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:1138: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:1151: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1173: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1227: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + + + + +for ac_func in mktime +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1253: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1281: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@CPP@%$CPP%g +s%@LIBOBJS@%$LIBOBJS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/librsrc/configure.in b/librsrc/configure.in new file mode 100644 index 0000000..5e091f2 --- /dev/null +++ b/librsrc/configure.in @@ -0,0 +1,62 @@ +dnl -*- shell-script -*- +dnl +dnl librsrc - library for reading and writing Macintosh resources +dnl Copyright (C) 1996-1998 Robert Leslie +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +dnl +dnl $Id: configure.in,v 1.5 1998/04/11 08:27:18 rob Exp $ +dnl + +AC_INIT(librsrc.h) +AC_CONFIG_HEADER(config.h) + +AC_ARG_ENABLE(debug, + [ --enable-debug enable diagnostic debugging support]) + +if test "x$enable_debug" = xyes +then + AC_DEFINE(DEBUG) +fi + +dnl Checks for programs. + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +AC_PROG_GCC_TRADITIONAL + +dnl Checks for header files. + +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h fcntl.h) + +dnl Checks for typedefs, structures, and compiler characteristics. + +AC_TYPE_SIZE_T +AC_STRUCT_TM + +AC_C_CONST + +dnl Checks for library functions. + +AC_SUBST(LIBOBJS) +AC_CHECK_FUNCS(mktime) + +dnl Create output files. + +AC_OUTPUT(Makefile) diff --git a/librsrc/data.c b/librsrc/data.c new file mode 100644 index 0000000..4f005fa --- /dev/null +++ b/librsrc/data.c @@ -0,0 +1,467 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: data.c,v 1.5 1998/04/11 08:27:18 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +# ifdef TM_IN_SYS_TIME +# include +# endif + +# include "data.h" + +# define TIMEDIFF 2082844800UL + +static +time_t tzdiff = -1; + +const +unsigned char hfs_charorder[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + 0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + + 0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae, + + 0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + + 0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55, + 0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64, + 0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89, + 0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a, + + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85, + + 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26, + 0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87, + 0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8, + 0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +/* + * NAME: data->getsb() + * DESCRIPTION: marshal 1 signed byte into local host format + */ +signed char d_getsb(const unsigned char *ptr) +{ + return ptr[0]; +} + +/* + * NAME: data->getub() + * DESCRIPTION: marshal 1 unsigned byte into local host format + */ +unsigned char d_getub(const unsigned char *ptr) +{ + return ptr[0]; +} + +/* + * NAME: data->getsw() + * DESCRIPTION: marshal 2 signed bytes into local host format + */ +signed short d_getsw(const unsigned char *ptr) +{ + return + ((( signed short) ptr[0] << 8) | + ((unsigned short) ptr[1] << 0)); +} + +/* + * NAME: data->getuw() + * DESCRIPTION: marshal 2 unsigned bytes into local host format + */ +unsigned short d_getuw(const unsigned char *ptr) +{ + return + (((unsigned short) ptr[0] << 8) | + ((unsigned short) ptr[1] << 0)); +} + +/* + * NAME: data->getsl() + * DESCRIPTION: marshal 4 signed bytes into local host format + */ +signed long d_getsl(const unsigned char *ptr) +{ + return + ((( signed long) ptr[0] << 24) | + ((unsigned long) ptr[1] << 16) | + ((unsigned long) ptr[2] << 8) | + ((unsigned long) ptr[3] << 0)); +} + +/* + * NAME: data->getul() + * DESCRIPTION: marshal 4 unsigned bytes into local host format + */ +unsigned long d_getul(const unsigned char *ptr) +{ + return + (((unsigned long) ptr[0] << 24) | + ((unsigned long) ptr[1] << 16) | + ((unsigned long) ptr[2] << 8) | + ((unsigned long) ptr[3] << 0)); +} + +/* + * NAME: data->putsb() + * DESCRIPTION: marshal 1 signed byte out in big-endian format + */ +void d_putsb(unsigned char *ptr, signed char data) +{ + *ptr = data; +} + +/* + * NAME: data->putub() + * DESCRIPTION: marshal 1 unsigned byte out in big-endian format + */ +void d_putub(unsigned char *ptr, unsigned char data) +{ + *ptr = data; +} + +/* + * NAME: data->putsw() + * DESCRIPTION: marshal 2 signed bytes out in big-endian format + */ +void d_putsw(unsigned char *ptr, signed short data) +{ + *ptr++ = ((unsigned short) data & 0xff00) >> 8; + *ptr = ((unsigned short) data & 0x00ff) >> 0; +} + +/* + * NAME: data->putuw() + * DESCRIPTION: marshal 2 unsigned bytes out in big-endian format + */ +void d_putuw(unsigned char *ptr, unsigned short data) +{ + *ptr++ = (data & 0xff00) >> 8; + *ptr = (data & 0x00ff) >> 0; +} + +/* + * NAME: data->putsl() + * DESCRIPTION: marshal 4 signed bytes out in big-endian format + */ +void d_putsl(unsigned char *ptr, signed long data) +{ + *ptr++ = ((unsigned long) data & 0xff000000UL) >> 24; + *ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16; + *ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8; + *ptr = ((unsigned long) data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->putul() + * DESCRIPTION: marshal 4 unsigned bytes out in big-endian format + */ +void d_putul(unsigned char *ptr, unsigned long data) +{ + *ptr++ = (data & 0xff000000UL) >> 24; + *ptr++ = (data & 0x00ff0000UL) >> 16; + *ptr++ = (data & 0x0000ff00UL) >> 8; + *ptr = (data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->fetchsb() + * DESCRIPTION: incrementally retrieve a signed byte of data + */ +void d_fetchsb(const unsigned char **ptr, signed char *dest) +{ + *dest = *(*ptr)++; +} + +/* + * NAME: data->fetchub() + * DESCRIPTION: incrementally retrieve an unsigned byte of data + */ +void d_fetchub(const unsigned char **ptr, unsigned char *dest) +{ + *dest = *(*ptr)++; +} + +/* + * NAME: data->fetchsw() + * DESCRIPTION: incrementally retrieve a signed word of data + */ +void d_fetchsw(const unsigned char **ptr, signed short *dest) +{ + *dest = + ((( signed short) (*ptr)[0] << 8) | + ((unsigned short) (*ptr)[1] << 0)); + *ptr += 2; +} + +/* + * NAME: data->fetchuw() + * DESCRIPTION: incrementally retrieve an unsigned word of data + */ +void d_fetchuw(const unsigned char **ptr, unsigned short *dest) +{ + *dest = + (((unsigned short) (*ptr)[0] << 8) | + ((unsigned short) (*ptr)[1] << 0)); + *ptr += 2; +} + +/* + * NAME: data->fetchsl() + * DESCRIPTION: incrementally retrieve a signed long word of data + */ +void d_fetchsl(const unsigned char **ptr, signed long *dest) +{ + *dest = + ((( signed long) (*ptr)[0] << 24) | + ((unsigned long) (*ptr)[1] << 16) | + ((unsigned long) (*ptr)[2] << 8) | + ((unsigned long) (*ptr)[3] << 0)); + *ptr += 4; +} + +/* + * NAME: data->fetchul() + * DESCRIPTION: incrementally retrieve an unsigned long word of data + */ +void d_fetchul(const unsigned char **ptr, unsigned long *dest) +{ + *dest = + (((unsigned long) (*ptr)[0] << 24) | + ((unsigned long) (*ptr)[1] << 16) | + ((unsigned long) (*ptr)[2] << 8) | + ((unsigned long) (*ptr)[3] << 0)); + *ptr += 4; +} + +/* + * NAME: data->storesb() + * DESCRIPTION: incrementally store a signed byte of data + */ +void d_storesb(unsigned char **ptr, signed char data) +{ + *(*ptr)++ = data; +} + +/* + * NAME: data->storeub() + * DESCRIPTION: incrementally store an unsigned byte of data + */ +void d_storeub(unsigned char **ptr, unsigned char data) +{ + *(*ptr)++ = data; +} + +/* + * NAME: data->storesw() + * DESCRIPTION: incrementally store a signed word of data + */ +void d_storesw(unsigned char **ptr, signed short data) +{ + *(*ptr)++ = ((unsigned short) data & 0xff00) >> 8; + *(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0; +} + +/* + * NAME: data->storeuw() + * DESCRIPTION: incrementally store an unsigned word of data + */ +void d_storeuw(unsigned char **ptr, unsigned short data) +{ + *(*ptr)++ = (data & 0xff00) >> 8; + *(*ptr)++ = (data & 0x00ff) >> 0; +} + +/* + * NAME: data->storesl() + * DESCRIPTION: incrementally store a signed long word of data + */ +void d_storesl(unsigned char **ptr, signed long data) +{ + *(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24; + *(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16; + *(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8; + *(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->storeul() + * DESCRIPTION: incrementally store an unsigned long word of data + */ +void d_storeul(unsigned char **ptr, unsigned long data) +{ + *(*ptr)++ = (data & 0xff000000UL) >> 24; + *(*ptr)++ = (data & 0x00ff0000UL) >> 16; + *(*ptr)++ = (data & 0x0000ff00UL) >> 8; + *(*ptr)++ = (data & 0x000000ffUL) >> 0; +} + +/* + * NAME: data->fetchstr() + * DESCRIPTION: incrementally retrieve a string + */ +void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size) +{ + unsigned len; + + len = d_getub(*ptr); + + if (len > 0 && len < size) + memcpy(dest, *ptr + 1, len); + else + len = 0; + + dest[len] = 0; + + *ptr += size; +} + +/* + * NAME: data->storestr() + * DESCRIPTION: incrementally store a string + */ +void d_storestr(unsigned char **ptr, const char *src, unsigned size) +{ + unsigned len; + + len = strlen(src); + if (len > --size) + len = 0; + + d_storeub(ptr, len); + + memcpy(*ptr, src, len); + memset(*ptr + len, 0, size - len); + + *ptr += size; +} + +/* + * NAME: data->relstring() + * DESCRIPTION: compare two strings as per MacOS for HFS + */ +int d_relstring(const char *str1, const char *str2) +{ + register int diff; + + while (*str1 && *str2) + { + diff = hfs_charorder[(unsigned char) *str1] - + hfs_charorder[(unsigned char) *str2]; + + if (diff) + return diff; + + ++str1, ++str2; + } + + if (! *str1 && *str2) + return -1; + else if (*str1 && ! *str2) + return 1; + + return 0; +} + +/* + * NAME: calctzdiff() + * DESCRIPTION: calculate the timezone difference between local time and UTC + */ +static +void calctzdiff(void) +{ +# ifdef HAVE_MKTIME + + time_t t; + int isdst; + struct tm tm; + const struct tm *tmp; + + time(&t); + isdst = localtime(&t)->tm_isdst; + + tmp = gmtime(&t); + if (tmp) + { + tm = *tmp; + tm.tm_isdst = isdst; + + tzdiff = t - mktime(&tm); + } + else + tzdiff = 0; + +# else + + tzdiff = 0; + +# endif +} + +/* + * NAME: data->ltime() + * DESCRIPTION: convert MacOS time to local time + */ +time_t d_ltime(unsigned long mtime) +{ + if (tzdiff == -1) + calctzdiff(); + + return (time_t) (mtime - TIMEDIFF) - tzdiff; +} + +/* + * NAME: data->mtime() + * DESCRIPTION: convert local time to MacOS time + */ +unsigned long d_mtime(time_t ltime) +{ + if (tzdiff == -1) + calctzdiff(); + + return (unsigned long) (ltime + tzdiff) + TIMEDIFF; +} diff --git a/librsrc/data.h b/librsrc/data.h new file mode 100644 index 0000000..6e5d644 --- /dev/null +++ b/librsrc/data.h @@ -0,0 +1,58 @@ +/* + * libhfs - library for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: data.h,v 1.5 1998/04/11 08:27:18 rob Exp $ + */ + +extern const unsigned char hfs_charorder[]; + +signed char d_getsb(const unsigned char *); +unsigned char d_getub(const unsigned char *); +signed short d_getsw(const unsigned char *); +unsigned short d_getuw(const unsigned char *); +signed long d_getsl(const unsigned char *); +unsigned long d_getul(const unsigned char *); + +void d_putsb(unsigned char *, signed char); +void d_putub(unsigned char *, unsigned char); +void d_putsw(unsigned char *, signed short); +void d_putuw(unsigned char *, unsigned short); +void d_putsl(unsigned char *, signed long); +void d_putul(unsigned char *, unsigned long); + +void d_fetchsb(const unsigned char **, signed char *); +void d_fetchub(const unsigned char **, unsigned char *); +void d_fetchsw(const unsigned char **, signed short *); +void d_fetchuw(const unsigned char **, unsigned short *); +void d_fetchsl(const unsigned char **, signed long *); +void d_fetchul(const unsigned char **, unsigned long *); + +void d_storesb(unsigned char **, signed char); +void d_storeub(unsigned char **, unsigned char); +void d_storesw(unsigned char **, signed short); +void d_storeuw(unsigned char **, unsigned short); +void d_storesl(unsigned char **, signed long); +void d_storeul(unsigned char **, unsigned long); + +void d_fetchstr(const unsigned char **, char *, unsigned); +void d_storestr(unsigned char **, const char *, unsigned); + +int d_relstring(const char *, const char *); + +time_t d_ltime(unsigned long); +unsigned long d_mtime(time_t); diff --git a/librsrc/librsrc.h b/librsrc/librsrc.h new file mode 100644 index 0000000..2919010 --- /dev/null +++ b/librsrc/librsrc.h @@ -0,0 +1,101 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: librsrc.h,v 1.5 1998/04/11 08:27:19 rob Exp $ + */ + +# include "rsrc.h" + +extern int errno; + +# define ERROR(code, str) \ + do { rsrc_error = (str), errno = (code); goto fail; } while (0) + +# ifdef DEBUG +# define ASSERT(cond) do { if (! (cond)) abort(); } while (0) +# else +# define ASSERT(cond) /* nothing */ +# endif + +# define SIZE(type, n) ((size_t) (sizeof(type) * (n))) +# define ALLOC(type, n) ((type *) malloc(SIZE(type, n))) +# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0) +# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0) + +# define REALLOC(ptr, type, n) \ + ((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n)))) +# define REALLOCX(ptr, type, n) \ + ((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0)) + +# define BMTST(bm, num) \ + (((byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07))) +# define BMSET(bm, num) \ + (((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07))) +# define BMCLR(bm, num) \ + (((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07))) + +# define STRINGIZE(x) #x +# define STR(x) STRINGIZE(x) + +typedef unsigned char byte; + +typedef struct { + unsigned long dstart; + unsigned long mstart; + + unsigned long dlen; + unsigned long mlen; +} rsrchdr; + +typedef struct { + byte *data; + + unsigned short attrs; + + byte *tlist; + byte *nlist; +} rsrcmap; + +# define RSRC_MAP_READONLY 0x0080 /* set to make file read-only */ +# define RSRC_MAP_COMPACT 0x0040 /* set to compact file on update */ +# define RSRC_MAP_CHANGED 0x0020 /* set to write map on update */ + +# define RSRC_RES_SYSHEAP 0x40 /* set if read into system heap */ +# define RSRC_RES_PURGEABLE 0x20 /* set if purgeable */ +# define RSRC_RES_LOCKED 0x10 /* set if locked */ +# define RSRC_RES_PROTECTED 0x08 /* set if protected */ +# define RSRC_RES_PRELOAD 0x04 /* set if to be preloaded */ +# define RSRC_RES_CHANGED 0x02 /* set if to be written to rsrc fork */ + +struct _rsrcfile_ { + void *priv; /* file-dependent private data */ + struct rsrcprocs procs; /* procedures for accessing the file path */ + + rsrchdr hdr; /* resource header */ + rsrcmap map; /* resource map */ +}; + +typedef struct { + struct _rsrcfile_ *rfile; + const byte *ritem; + + unsigned short attrs; + + unsigned long len; + byte data[1]; +} rsrchandle; diff --git a/librsrc/main.c b/librsrc/main.c new file mode 100644 index 0000000..55cb2e8 --- /dev/null +++ b/librsrc/main.c @@ -0,0 +1,112 @@ +# include +# include +# include + +# include "hfs.h" +# include "rsrc.h" + +static +void die(const char *msg) +{ + perror(msg); + exit(2); +} + +long rseek(void *file, long where, int whence) +{ + printf("SEEK %lu\n", where); + return hfs_seek(file, where, whence); +} + +long rread(void *file, void *block, unsigned long howmuch) +{ + printf("READ %lu\n", howmuch); + return hfs_read(file, block, howmuch); +} + +struct rsrcprocs fileprocs = { + (rsrcseekfunc) rseek, + (rsrcreadfunc) rread, + (rsrcwritefunc) hfs_write +}; + +int main(int argc, char *argv[]) +{ + hfsvol *vol; + hfsfile *file; + rsrcfile *rfile; + int i; + + if (argc < 3) + { + fprintf(stderr, "Args: vol file\n"); + exit(1); + } + + vol = hfs_mount(argv[1], 1, HFS_MODE_ANY); + if (vol == 0) + die(hfs_error); + + file = hfs_open(vol, argv[2]); + if (file == 0 || hfs_setfork(file, 1) == -1) + die(hfs_error); + + rfile = rsrc_init(file, &fileprocs); + if (rfile == 0) + die(rsrc_error); + + /* ... */ + + for (i = rsrc_counttypes(rfile); i > 0; --i) + { + char type[5]; + int count; + + rsrc_gettype(rfile, i, type); + count = rsrc_count(rfile, type); + + while (count > 0) + { + unsigned char *data; + unsigned long len; + FILE *hex; + + data = rsrc_getind(rfile, type, count); + if (data) + { + len = rsrc_size(data); + + printf("'%s' %d: %lu byte%s\n", type, count, len, + len == 1 ? "" : "s"); + fflush(stdout); + + hex = popen("hex", "w"); + if (hex == 0) + die("error forking `hex'"); + + fwrite(data, len, 1, hex); + fclose(hex); + + rsrc_release(data); + } + else + { + printf("%5d: %s\n", count, rsrc_error); + fflush(stdout); + } + + --count; + } + } + + /* ... */ + + if (rsrc_finish(rfile) == -1) + die(rsrc_error); + + if (hfs_close(file) == -1 || + hfs_umount(vol) == -1) + die(hfs_error); + + return 0; +} diff --git a/librsrc/rsrc.c b/librsrc/rsrc.c new file mode 100644 index 0000000..af8d3b3 --- /dev/null +++ b/librsrc/rsrc.c @@ -0,0 +1,494 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: rsrc.c,v 1.7 1998/11/02 22:09:15 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include +# include + +# include "librsrc.h" +# include "data.h" + +const char *rsrc_error = "no error"; /* static error string */ + +/* + * NAME: rsrc->init() + * DESCRIPTION: initialize access to a resource file + */ +rsrcfile *rsrc_init(void *priv, const struct rsrcprocs *procs) +{ + rsrcfile *rfile; + byte head[16]; + const byte *ptr = head; + unsigned long nbytes; + unsigned short tlistoff, nlistoff; + + rfile = ALLOC(rsrcfile, 1); + if (rfile == 0) + ERROR(ENOMEM, 0); + + rfile->priv = priv; + rfile->procs = *procs; + + rfile->map.data = 0; + + if (rfile->procs.seek(rfile->priv, 0, RSRC_SEEK_SET) == (unsigned long) -1) + ERROR(errno, "error seeking resource header"); + + nbytes = rfile->procs.read(rfile->priv, head, sizeof(head)); + if (nbytes != sizeof(head)) + { + if (nbytes == (unsigned long) -1) + ERROR(errno, "error reading resource header"); + else + ERROR(EINVAL, "truncated resource header"); + } + + d_fetchul(&ptr, &rfile->hdr.dstart); + d_fetchul(&ptr, &rfile->hdr.mstart); + + d_fetchul(&ptr, &rfile->hdr.dlen); + d_fetchul(&ptr, &rfile->hdr.mlen); + + rfile->map.data = ALLOC(byte, rfile->hdr.mlen); + if (rfile->map.data == 0) + ERROR(ENOMEM, 0); + + if (rfile->procs.seek(rfile->priv, rfile->hdr.mstart, RSRC_SEEK_SET) == + (unsigned long) -1) + ERROR(errno, "error seeking resource map"); + + nbytes = rfile->procs.read(rfile->priv, rfile->map.data, rfile->hdr.mlen); + if (nbytes != rfile->hdr.mlen) + { + if (nbytes == (unsigned long) -1) + ERROR(errno, "error reading resource map"); + else + ERROR(EIO, "truncated resource map"); + } + + memcpy(rfile->map.data, head, sizeof(head)); + + ptr = rfile->map.data + 22; + + d_fetchuw(&ptr, &rfile->map.attrs); + + d_fetchuw(&ptr, &tlistoff); + d_fetchuw(&ptr, &nlistoff); + + rfile->map.tlist = rfile->map.data + tlistoff; + rfile->map.nlist = rfile->map.data + nlistoff; + + return rfile; + +fail: + if (rfile) + FREE(rfile->map.data); + + FREE(rfile); + + return 0; +} + +/* + * NAME: rsrc->finish() + * DESCRIPTION: terminate access to a resource file + */ +int rsrc_finish(rsrcfile *rfile) +{ + FREE(rfile->map.data); + FREE(rfile); + + return 0; +} + +/* + * NAME: findtype() + * DESCRIPTION: locate an item in a resource type list + */ +static +const byte *findtype(rsrcmap *map, const char *type) +{ + const byte *ptr; + long typeint; + short ntypes; + + typeint = d_getsl((const unsigned char *) type); + ntypes = d_getsw(map->tlist); + + for (ptr = map->tlist + 2; ntypes >= 0; ptr += 8, --ntypes) + { + if (d_getsl(ptr) == typeint) + break; + } + + if (ntypes < 0) + ERROR(EINVAL, "resource type not found"); + + return ptr; + +fail: + return 0; +} + +/* + * NAME: rsrc->counttypes() + * DESCRIPTION: return the number of unique resource types + */ +int rsrc_counttypes(rsrcfile *rfile) +{ + return d_getsw(rfile->map.tlist) + 1; +} + +/* + * NAME: rsrc->count() + * DESCRIPTION: return the number of resources of a given type + */ +int rsrc_count(rsrcfile *rfile, const char *type) +{ + const byte *ptr; + + ptr = findtype(&rfile->map, type); + if (ptr == 0) + return 0; + + return d_getsw(ptr + 4) + 1; +} + +/* + * NAME: rsrc->gettype() + * DESCRIPTION: return an indexed type from the resource file + */ +void rsrc_gettype(rsrcfile *rfile, int index, char *type) +{ + if (index < 1 || index > (d_getsw(rfile->map.tlist) + 1)) + memset(type, 0, 5); + else + { + memcpy(type, rfile->map.tlist + 2 + 8 * (index - 1), 4); + type[4] = 0; + } +} + +/* + * NAME: find() + * DESCRIPTION: generic routine to locate and return a resource + */ +static +const byte *find(rsrcmap *map, const char *type, + int (*compare)(rsrcmap *, const byte *, const void *), + const void *key) +{ + const byte *ptr; + short nitems; + unsigned short rlistoff; + + ptr = findtype(map, type); + if (ptr == 0) + goto fail; + + ptr += 4; + d_fetchsw(&ptr, &nitems); + d_fetchuw(&ptr, &rlistoff); + + for (ptr = map->tlist + rlistoff; nitems >= 0; ptr += 12, --nitems) + { + if (compare(map, ptr, key)) + break; + } + + if (nitems < 0) + ERROR(EINVAL, "resource not found"); + + return ptr; + +fail: + return 0; +} + +/* + * NAME: compare_id() + * DESCRIPTION: compare reference list item with numeric id + */ +static +int compare_id(rsrcmap *map, const byte *ritem, const void *key) +{ + return d_getsw(ritem) == *(const int *) key; +} + +/* + * NAME: compare_name() + * DESCRIPTION: compare reference list item with character name + */ +static +int compare_name(rsrcmap *map, const byte *ritem, const void *key) +{ + char name[256]; + const byte *nptr = map->nlist + d_getuw(ritem + 2); + + memcpy(name, nptr + 1, *nptr); + name[*nptr] = 0; + + return d_relstring(name, key) == 0; +} + +/* + * NAME: load() + * DESCRIPTION: retrieve a resource from the resource file + */ +static +rsrchandle *load(rsrcfile *rfile, const byte *ritem) +{ + unsigned long offs, nbytes, len, count; + byte data[260]; + rsrchandle *rsrc = 0; + + offs = d_getul(ritem + 4) & 0x00ffffff; + + if (rfile->procs.seek(rfile->priv, + rfile->hdr.dstart + offs, RSRC_SEEK_SET) == + (unsigned long) -1) + ERROR(errno, "error seeking resource data"); + + nbytes = rfile->procs.read(rfile->priv, data, sizeof(data)); + if (nbytes < 4) + { + if (nbytes == (unsigned long) -1) + ERROR(errno, "error reading resource data"); + else + ERROR(EIO, "truncated resource data"); + } + + len = d_getul(data); + + rsrc = (rsrchandle *) ALLOC(byte, sizeof(rsrchandle) + len); + if (rsrc == 0) + ERROR(ENOMEM, 0); + + count = nbytes - 4; + if (count > len) + count = len; + + memcpy(rsrc->data, data + 4, count); + + if (count < len) + { + nbytes = rfile->procs.read(rfile->priv, rsrc->data + count, len - count); + if (nbytes != len - count) + { + if (nbytes == (unsigned long) -1) + ERROR(errno, "error reading resource data"); + else + ERROR(EIO, "truncated resource data"); + } + } + + rsrc->rfile = rfile; + rsrc->ritem = ritem; + rsrc->len = len; + + return rsrc; + +fail: + FREE(rsrc); + return 0; +} + +/* + * NAME: getrdata() + * DESCRIPTION: generate application resource data pointer from resource handle + */ +static +void *getrdata(rsrchandle *rsrc) +{ + return rsrc->data; +} + +/* + * NAME: gethandle() + * DESCRIPTION: recover resource handle from application resource data pointer + */ +static +rsrchandle *gethandle(void *rdata) +{ + rsrchandle dummy; + + return (rsrchandle *) ((char *) rdata - + ((char *) dummy.data - (char *) &dummy)); +} + +/* + * NAME: rsrc->maxsize() + * DESCRIPTION: return maximum possible size of the given resource (type/id) + */ +unsigned long rsrc_maxsize(rsrcfile *rfile, const char *type, int id) +{ + const byte *ptr; + unsigned long offs1, offs2; + + ptr = find(&rfile->map, type, compare_id, &id); + if (ptr == 0) + return 0; + + offs1 = d_getul(ptr + 4) & 0x00ffffff; + /* ... */ + + offs2 = 0; + + return 0; +} + +/* + * NAME: rsrc->get() + * DESCRIPTION: return a resource by type/id + */ +void *rsrc_get(rsrcfile *rfile, const char *type, int id) +{ + const byte *ptr; + rsrchandle *rsrc; + + ptr = find(&rfile->map, type, compare_id, &id); + if (ptr == 0) + return 0; + + rsrc = load(rfile, ptr); + if (rsrc == 0) + return 0; + + return getrdata(rsrc); +} + +/* + * NAME: rsrc->getnamed() + * DESCRIPTION: return a resource by type/name + */ +void *rsrc_getnamed(rsrcfile *rfile, const char *type, const char *name) +{ + const byte *ptr; + rsrchandle *rsrc; + + ptr = find(&rfile->map, type, compare_name, name); + if (ptr == 0) + return 0; + + rsrc = load(rfile, ptr); + if (rsrc == 0) + return 0; + + return getrdata(rsrc); +} + +/* + * NAME: rsrc->getind() + * DESCRIPTION: return a resource by type/index + */ +void *rsrc_getind(rsrcfile *rfile, const char *type, int index) +{ + const byte *ptr; + short nitems; + rsrchandle *rsrc; + + ptr = findtype(&rfile->map, type); + if (ptr == 0) + goto fail; + + nitems = d_getsw(ptr + 4) + 1; + if (index < 1 || index > nitems) + ERROR(EINVAL, "index out of range"); + + rsrc = load(rfile, rfile->map.tlist + d_getsw(ptr + 6) + 12 * (index - 1)); + if (rsrc == 0) + goto fail; + + return getrdata(rsrc); + +fail: + return 0; +} + +/* + * NAME: rsrc->size() + * DESCRIPTION: return the length of a resource in bytes + */ +unsigned long rsrc_size(void *rdata) +{ + rsrchandle *rsrc = gethandle(rdata); + + return rsrc->len; +} + +/* + * NAME: rsrc->resize() + * DESCRIPTION: change the length of a resource + */ +void *rsrc_resize(void *rdata, unsigned long newsize) +{ + rsrchandle *newrsrc, *rsrc = gethandle(rdata); + + if (rsrc->len == newsize) + goto done; + + newrsrc = (rsrchandle *) REALLOC(rsrc, byte, sizeof(rsrchandle) + newsize); + if (newrsrc == 0) + ERROR(ENOMEM, 0); + + newrsrc->attrs |= RSRC_RES_CHANGED; + newrsrc->len = newsize; + + rdata = getrdata(newrsrc); + +done: + return rdata; + +fail: + return 0; +} + +/* + * NAME: rsrc->changed() + * DESCRIPTION: indicate a changed resource which must be rewritten to disk + */ +void rsrc_changed(void *rdata) +{ + rsrchandle *rsrc = gethandle(rdata); + + rsrc->attrs |= RSRC_RES_CHANGED; +} + +/* + * NAME: rsrc->release() + * DESCRIPTION: dispose memory used by a resource + */ +void rsrc_release(void *rdata) +{ + rsrchandle *rsrc = gethandle(rdata); + + if (rsrc->attrs & RSRC_RES_CHANGED) + { + /* ... */ + } + + FREE(rsrc); +} diff --git a/librsrc/rsrc.h b/librsrc/rsrc.h new file mode 100644 index 0000000..aab04ec --- /dev/null +++ b/librsrc/rsrc.h @@ -0,0 +1,58 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: rsrc.h,v 1.7 1998/11/02 22:09:16 rob Exp $ + */ + +typedef struct _rsrcfile_ rsrcfile; + +extern const char *rsrc_error; + +typedef unsigned long (*rsrcseekfunc)(void *, long, int); +typedef unsigned long (*rsrcreadfunc)(void *, void *, unsigned long); +typedef unsigned long (*rsrcwritefunc)(void *, const void *, unsigned long); + +struct rsrcprocs { + rsrcseekfunc seek; + rsrcreadfunc read; + rsrcwritefunc write; +}; + +# define RSRC_SEEK_SET 0 +# define RSRC_SEEK_CUR 1 +# define RSRC_SEEK_END 2 + +rsrcfile *rsrc_init(void *, const struct rsrcprocs *); +int rsrc_finish(rsrcfile *); + +int rsrc_counttypes(rsrcfile *); +int rsrc_count(rsrcfile *, const char *); + +void rsrc_gettype(rsrcfile *, int, char *); + +unsigned long rsrc_maxsize(rsrcfile *, const char *, int); + +void *rsrc_get(rsrcfile *, const char *, int); +void *rsrc_getnamed(rsrcfile *, const char *, const char *); +void *rsrc_getind(rsrcfile *, const char *, int); + +unsigned long rsrc_size(void *); +void *rsrc_resize(void *, unsigned long); + +void rsrc_changed(void *); +void rsrc_release(void *); diff --git a/librsrc/version.c b/librsrc/version.c new file mode 100644 index 0000000..00a4309 --- /dev/null +++ b/librsrc/version.c @@ -0,0 +1,29 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.c,v 1.11 1998/11/02 22:09:17 rob Exp $ + */ + +# include "version.h" + +const char librsrc_rcsid[] = + "$Id: version.c,v 1.11 1998/11/02 22:09:17 rob Exp $"; + +const char librsrc_version[] = "librsrc version 3.2.6"; +const char librsrc_copyright[] = "Copyright (C) 1996-1998 Robert Leslie"; +const char librsrc_author[] = "Robert Leslie "; diff --git a/librsrc/version.h b/librsrc/version.h new file mode 100644 index 0000000..bb36f69 --- /dev/null +++ b/librsrc/version.h @@ -0,0 +1,26 @@ +/* + * librsrc - library for reading and writing Macintosh resources + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.h,v 1.6 1998/09/18 22:56:44 rob Exp $ + */ + +extern const char librsrc_rcsid[]; + +extern const char librsrc_version[]; +extern const char librsrc_copyright[]; +extern const char librsrc_author[]; diff --git a/linux/Makefile.in b/linux/Makefile.in new file mode 100644 index 0000000..1cf0b25 --- /dev/null +++ b/linux/Makefile.in @@ -0,0 +1,107 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile.in,v 1.4 1998/04/11 23:40:08 rob Exp $ +# + +@SET_MAKE@ +src = @srcdir@ +VPATH = @srcdir@ + +### USER CUSTOMIZATIONS FOLLOW ################################################ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +MANDEST = @mandir@ + +INCDEST = @includedir@ +LIBDEST = @libdir@ + +INSTALL = @INSTALL@ +LIBINSTALL = @INSTALL_DATA@ + +CC = @CC@ +INCLUDES = @CPPFLAGS@ -I.. -I../libhfs +DEFINES = @DEFS@ +LIBOBJS = @LIBOBJS@ ../suid.o ../version.o +LIBS = @LIBS@ -L../libhfs -lhfs + +COPTS = @CFLAGS@ +LDOPTS = @LDFLAGS@ + +### END OF USER CUSTOMIZATION ################################################# + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(MKFSTARGET) $(FSCKTARGET) $(HDUMPTARGET) + +MKFSTARGET = mkfs.hfs +FSCKTARGET = fsck.hfs +HDUMPTARGET = hdump + +SUBDIRS = kernel + +MKFSOBJS = mkfs.o $(LIBOBJS) +FSCKOBJS = fsck.o $(LIBOBJS) +HDUMPOBJS = hdump.o $(LIBOBJS) + +############################################################################### + +all :: $(TARGETS) + +again :: clean all + +depend :: + for dir in $(SUBDIRS); do \ + (cd $$dir && $(MAKE) depend); \ + done + + ( sed -n '1,/^### DEPEND/p' Makefile.in; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.in.new + mv -f Makefile.in.new Makefile.in + +clean :: + for dir in $(SUBDIRS); do \ + (cd $$dir && $(MAKE) clean); \ + done + + rm -f $(TARGETS) *.o gmon.* core + +############################################################################### + +$(MKFSTARGET): $(MKFSOBJS) + $(CC) $(LDFLAGS) $(MKFSOBJS) $(LIBS) -o $@ + +$(FSCKTARGET): $(FSCKOBJS) + $(CC) $(LDFLAGS) $(FSCKOBJS) $(LIBS) -o $@ + +$(HDUMPTARGET): $(HDUMPOBJS) + $(CC) $(LDFLAGS) $(HDUMPOBJS) $(LIBS) -o $@ + +### DEPENDENCIES FOLLOW ####################################################### + +fsck.o: fsck.c +hdump.o: hdump.c ../libhfs/libhfs.h ../libhfs/hfs.h ../libhfs/apple.h \ + ../libhfs/volume.h ../libhfs/low.h ../libhfs/data.h +mkfs.o: mkfs.c diff --git a/linux/fsck.c b/linux/fsck.c new file mode 100644 index 0000000..c3d6f0e --- /dev/null +++ b/linux/fsck.c @@ -0,0 +1,25 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: fsck.c,v 1.3 1998/04/11 08:27:21 rob Exp $ + */ + +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/linux/hdump.c b/linux/hdump.c new file mode 100644 index 0000000..b8bdfe2 --- /dev/null +++ b/linux/hdump.c @@ -0,0 +1,147 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: hdump.c,v 1.4 1998/11/02 22:09:18 rob Exp $ + */ + +# include + +# include "libhfs.h" +# include "volume.h" +# include "low.h" +# include "data.h" + +int main(int argc, char *argv[]) +{ + hfsvol vol; + Block0 ddr; + Partition map; + unsigned long bnum; + int i; + + v_init(&vol, HFS_OPT_NOCACHE); + + if (argc != 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + goto fail; + } + + if (v_open(&vol, argv[1], HFS_MODE_RDONLY) == -1) + { + fprintf(stderr, "v_open: %s\n", hfs_error); + goto fail; + } + + if (l_getddr(&vol, &ddr) == -1) + { + fprintf(stderr, "l_getddr: %s\n", hfs_error); + goto fail; + } + + if (ddr.sbSig != HFS_DDR_SIGWORD) + fprintf(stderr, "block 0: not a valid driver descriptor record\n"); + else + { + printf("Driver Descriptor Record\n"); + printf(" sbSig \t= 0x%04x\n", ddr.sbSig); + printf(" sbBlkSize \t= %d\n", ddr.sbBlkSize); + printf(" sbBlkCount \t= %ld\n", ddr.sbBlkCount); + printf(" sbDevType \t= %d\n", ddr.sbDevType); + printf(" sbDevId \t= %d\n", ddr.sbDevId); + printf(" sbData \t= %ld\n", ddr.sbData); + printf(" sbDrvrCount\t= %d\n", ddr.sbDrvrCount); + + printf(" ddBlock[0] \t= %ld\n", ddr.ddBlock); + printf(" ddSize[0] \t= %d\n", ddr.ddSize); + printf(" ddType[0] \t= %d\n", ddr.ddType); + + for (i = 0; i < 243; i += 4) + { + ddr.ddBlock = d_getsl((unsigned char *) &ddr.ddPad[i]); + ddr.ddSize = d_getsw((unsigned char *) &ddr.ddPad[i + 2]); + ddr.ddType = d_getsw((unsigned char *) &ddr.ddPad[i + 3]); + + if (ddr.ddBlock != 0 || + ddr.ddSize != 0 || + ddr.ddType != 0) + { + printf(" ddBlock[%d] \t= %ld\n", i >> 2, ddr.ddBlock); + printf(" ddSize[%d] \t= %d\n", i >> 2, ddr.ddSize); + printf(" ddType[%d] \t= %d\n", i >> 2, ddr.ddType); + } + } + } + + bnum = 1; + while (1) + { + if (l_getpmentry(&vol, &map, bnum) == -1) + { + fprintf(stderr, "l_getpmentry: %s\n", hfs_error); + goto fail; + } + + if (map.pmSig != HFS_PM_SIGWORD) + { + fprintf(stderr, "block %lu: not a valid partition entry\n", bnum); + goto fail; + } + + printf("Partition Entry %lu\n", bnum); + printf(" pmSig \t= 0x%04x\n", map.pmSig); + printf(" pmSigPad \t= %d\n", map.pmSigPad); + printf(" pmMapBlkCnt \t= %ld\n", map.pmMapBlkCnt); + printf(" pmPyPartStart\t= %ld\n", map.pmPyPartStart); + printf(" pmPartBlkCnt \t= %ld\n", map.pmPartBlkCnt); + printf(" pmPartName \t= \"%s\"\n", map.pmPartName); + printf(" pmParType \t= \"%s\"\n", map.pmParType); + printf(" pmLgDataStart\t= %ld\n", map.pmLgDataStart); + printf(" pmDataCnt \t= %ld\n", map.pmDataCnt); + printf(" pmPartStatus \t= %ld\n", map.pmPartStatus); + printf(" pmLgBootStart\t= %ld\n", map.pmLgBootStart); + printf(" pmBootSize \t= %ld\n", map.pmBootSize); + printf(" pmBootAddr \t= %ld\n", map.pmBootAddr); + printf(" pmBootAddr2 \t= %ld\n", map.pmBootAddr2); + printf(" pmBootEntry \t= %ld\n", map.pmBootEntry); + printf(" pmBootEntry2 \t= %ld\n", map.pmBootEntry2); + printf(" pmBootCksum \t= %ld\n", map.pmBootCksum); + printf(" pmProcessor \t= \"%s\"\n", map.pmProcessor); + + for (i = 0; i < 188; ++i) + { + if (map.pmPad[i] != 0) + printf(" pmPad[%d]\t= %d\n", i, map.pmPad[i]); + } + + if (bnum++ >= map.pmMapBlkCnt) + break; + } + + if (v_close(&vol) == -1) + { + fprintf(stderr, "v_close: %s\n", hfs_error); + goto fail; + } + + return 0; + +fail: + v_close(&vol); + return 1; +} diff --git a/linux/kernel/Makefile b/linux/kernel/Makefile new file mode 100644 index 0000000..6cdb476 --- /dev/null +++ b/linux/kernel/Makefile @@ -0,0 +1,65 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile,v 1.4 1998/04/11 23:40:09 rob Exp $ +# + +CC = gcc +LD = ld +INCLUDES = -I/usr/src/linux/include -I../../libhfs +DEFINES = -D__KERNEL__ -DMODULE +LIBS = + +COPTS = -g #-Wall -Wstrict-prototypes +LDOPTS = + +CFLAGS = $(COPTS) $(INCLUDES) $(DEFINES) +LDFLAGS = $(LDOPTS) + +############################################################################### + +TARGETS = $(KERNTARGET) + +KERNTARGET = hfs.o +KERNOBJS = module.o + +############################################################################### + +all :: $(TARGETS) + +again :: clean all + +depend :: + ( sed -n '1,/^### DEPEND/p' Makefile; \ + echo; \ + $(CC) -MM $(INCLUDES) $(DEFINES) *.c; \ + ) > Makefile.new + mv -f Makefile.new Makefile + +clean :: + rm -f $(TARGETS) *.o gmon.* core + +############################################################################### + +$(KERNTARGET): $(KERNOBJS) + $(LD) -r -o $@ $(KERNOBJS) + +### DEPENDENCIES FOLLOW ####################################################### + +module.o: module.c ../../libhfs/libhfs.h ../../libhfs/hfs.h \ + ../../libhfs/apple.h diff --git a/linux/kernel/module.c b/linux/kernel/module.c new file mode 100644 index 0000000..0614f47 --- /dev/null +++ b/linux/kernel/module.c @@ -0,0 +1,100 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: module.c,v 1.3 1998/04/11 08:27:22 rob Exp $ + */ + +# include +# include +# include + +# include "libhfs.h" + +/* Superblock Operations =================================================== */ + +static +struct super_operations hfs_super_operations = { + 0, /* read_inode */ + 0, /* notify_change */ + 0, /* write_inode */ + 0, /* put_inode */ + 0, /* put_super */ + 0, /* write_super */ + 0, /* statfs */ + 0, /* remount_fs */ +}; + +static +struct super_block *hfs_read_super(struct super_block *sb, + void *data, int silent) +{ + char *opts = data; + + MOD_INC_USE_COUNT; + + lock_super(sb); + + set_blocksize(sb->s_dev, HFS_BLOCKSZ); + + sb->s_blocksize = HFS_BLOCKSZ; + sb->s_blocksize_bits = HFS_BLOCKSZ_BITS; + sb->s_op = &hfs_super_operations; + sb->s_magic = HFS_SIGWORD; + sb->s_mounted = 0; + + unlock_super(sb); + + return sb; +} + +static +void hfs_put_super(struct super_block *sb) +{ + lock_super(sb); + + set_blocksize(sb->s_dev, BLOCK_SIZE); + + sb->s_dev = 0; + + unlock_super(sb); + + MOD_DEC_USE_COUNT; +} + +/* Module Functions ======================================================== */ + +static +struct file_system_type hfs_fs = { hfs_read_super, "hfs", 1, 0 }; + +int init_module(void) +{ + int err; + + if ((err = register_filesystem(&hfs_fs)) < 0) + return err; + + return 0; +} + +void cleanup_module(void) +{ + int err; + + if ((err = unregister_filesystem(&hfs_fs)) < 0) + printk("HFS: error %d during unregister_filesystem()\n", err); +} diff --git a/linux/mkfs.c b/linux/mkfs.c new file mode 100644 index 0000000..f6336aa --- /dev/null +++ b/linux/mkfs.c @@ -0,0 +1,25 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: mkfs.c,v 1.3 1998/04/11 08:27:21 rob Exp $ + */ + +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/strdup.c b/strdup.c new file mode 100644 index 0000000..3a7b51c --- /dev/null +++ b/strdup.c @@ -0,0 +1,42 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: strdup.c,v 1.5 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +/* + * NAME: strdup() + * DESCRIPTION: duplicate a string + */ +char *strdup(const char *s) +{ + char *ns; + + ns = malloc(strlen(s) + 1); + if (ns) + strcpy(ns, s); + + return ns; +} diff --git a/strerror.c b/strerror.c new file mode 100644 index 0000000..ff944b8 --- /dev/null +++ b/strerror.c @@ -0,0 +1,74 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: strerror.c,v 1.5 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include +# include + +# define CHARLEN(type) ((sizeof(type) * CHAR_BIT + 2) / 3 + 1) + +# ifdef HAVE_SYS_ERRLIST + +extern char *sys_errlist[]; +extern int sys_nerr; + +# endif + +/* + * NAME: strerror() + * DESCRIPTION: return string describing error code + */ +char *strerror(int errnum) +{ + static char str[14 + CHARLEN(int) + 1]; + +# ifdef HAVE_SYS_ERRLIST + + if (errnum >= 0 && errnum < sys_nerr) + return sys_errlist[errnum]; + +# else + + switch (errnum) + { + case ENOENT: return "No such file or directory"; + case EIO: return "I/O error"; + case ENOMEM: return "Out of memory"; + case EEXIST: return "File exists"; + case ENOTDIR: return "Not a directory"; + case EISDIR: return "Is a directory"; + case EINVAL: return "Invalid argument"; + case ENOSPC: return "No space left on device"; + case EROFS: return "Read-only file system"; + case ENAMETOOLONG: return "File name too long"; + case ENOTEMPTY: return "Directory not empty"; + } + +# endif + + sprintf(str, "System error #%d", errnum); + + return str; +} diff --git a/strstr.c b/strstr.c new file mode 100644 index 0000000..1e8dd92 --- /dev/null +++ b/strstr.c @@ -0,0 +1,56 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: strstr.c,v 1.5 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +/* + * NAME: strstr() + * DESCRIPTION: locate a substring + */ +char *strstr(const char *haystack, const char *needle) +{ + register const char *hptr, *nptr; + + if (*needle == 0) + return haystack; + + while (*haystack) + { + if (*haystack++ != *needle) + continue; + + hptr = haystack; + nptr = needle + 1; + + while (1) + { + if (*nptr == 0) + return haystack - 1; + + if (*hptr++ != *nptr++) + break; + } + } + + return 0; +} diff --git a/strtol.c b/strtol.c new file mode 100644 index 0000000..30ffe73 --- /dev/null +++ b/strtol.c @@ -0,0 +1,67 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: strtol.c,v 1.5 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include +# include + +/* + * NAME: strtol() + * DESCRIPTION: convert a string to a long integer + */ +long strtol(const char *nptr, char **endptr, int base) +{ + long result = 0; + int negative = 0; + + /* this replacement function optimized for use with hfsutils */ + + if (endptr || base) + abort(); + + while (isspace(*nptr)) + ++nptr; + + switch (*nptr) + { + case '-': + negative = 1; + + case '+': + ++nptr; + } + + while (1) + { + unsigned digit; + + digit = *nptr++ - '0'; + if (digit > 9) + break; + + result = 10 * result - digit; + } + + return negative ? result : -result; +} diff --git a/suid.c b/suid.c new file mode 100644 index 0000000..be21ee2 --- /dev/null +++ b/suid.c @@ -0,0 +1,100 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: suid.c,v 1.7 1998/04/11 08:27:00 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# include + +# ifdef HAVE_UNISTD_H +# include +# else +uid_t getuid(void); +uid_t geteuid(void); +gid_t getgid(void); +gid_t getegid(void); +# ifdef HAVE_SETREUID +int setreuid(uid_t, uid_t); +int setregid(gid_t, gid_t); +# else +int setuid(uid_t); +int setgid(gid_t); +# endif +# endif + +# include "suid.h" + +static uid_t uid, euid; +static gid_t gid, egid; + +/* + * NAME: suid->init() + * DESCRIPTION: initialize application which may be running setuid/setgid + */ +void suid_init(void) +{ + uid = getuid(); + gid = getgid(); + + euid = geteuid(); + egid = getegid(); + + suid_disable(); +} + +/* + * NAME: suid->enable() + * DESCRIPTION: engage any setuid privileges + */ +void suid_enable(void) +{ +# ifdef HAVE_SETREUID + + setreuid(-1, euid); + setregid(-1, egid); + +# else + + setuid(euid); + setgid(egid); + +# endif +} + +/* + * NAME: suid->disable() + * DESCRIPTION: revoke all setuid privileges + */ +void suid_disable(void) +{ +# ifdef HAVE_SETREUID + + setreuid(-1, uid); + setregid(-1, gid); + +# else + + setuid(uid); + setgid(gid); + +# endif +} diff --git a/suid.h b/suid.h new file mode 100644 index 0000000..7cfcc40 --- /dev/null +++ b/suid.h @@ -0,0 +1,24 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: suid.h,v 1.6 1998/04/11 08:27:00 rob Exp $ + */ + +void suid_init(void); +void suid_enable(void); +void suid_disable(void); diff --git a/tclhfs.c b/tclhfs.c new file mode 100644 index 0000000..c6182f3 --- /dev/null +++ b/tclhfs.c @@ -0,0 +1,1485 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: tclhfs.c,v 1.12 1998/11/02 22:08:32 rob Exp $ + */ + +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +# ifdef HAVE_UNISTD_H +# include +# endif + +# include +# include +# include +# include +# include + +# include + +# include "tclhfs.h" +# include "hfs.h" +# include "glob.h" +# include "copyin.h" +# include "copyout.h" +# include "charset.h" +# include "suid.h" +# include "version.h" + +extern int errno; + +# define ERROR(code, str) (hfs_error = (str), errno = (code)) + +# define SIZE(type, n) ((size_t) (sizeof(type) * (n))) +# define ALLOC(type, n) ((type *) malloc(SIZE(type, n))) +# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0) +# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0) + +# define REALLOC(ptr, type, n) \ + ((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n)))) +# define REALLOCX(ptr, type, n) \ + ((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0)) + +# define CHARLEN(type) ((sizeof(type) * CHAR_BIT + 2) / 3 + 1) + +static +int err_umount, err_close; + +typedef struct { + hfsvol *vol; + unsigned long cwd; +} volref; + +typedef struct { + hfsfile *file; + Tcl_Interp *interp; + Tcl_Command cmd; +} fileref; + +Tcl_HashTable volumes; /* set containing mounted volumes (no values) */ +Tcl_HashTable files; /* mapping of frefs -> vrefs */ + +/* + * NAME: error() + * DESCRIPTION: return a Tcl error for an HFS error + */ +static +int error(Tcl_Interp *interp, const char *msg) +{ + const char *str; + char c[2]; + + str = strerror(errno); + c[0] = tolower(*str); + c[1] = 0; + + Tcl_ResetResult(interp); + + if (msg) + Tcl_AppendResult(interp, msg, ": ", (char *) 0); + + if (hfs_error == 0) + Tcl_AppendResult(interp, c, str + 1, (char *) 0); + else + Tcl_AppendResult(interp, hfs_error, " (", str, ")", (char *) 0); + + return TCL_ERROR; +} + +/* + * NAME: direntstr() + * DESCRIPTION: return a Tcl stat list for a single HFS entity + */ +static +char *direntstr(hfsdirent *ent) +{ + char + cnid[CHARLEN(unsigned long) + 1], + parid[CHARLEN(unsigned long) + 1], + rsize[CHARLEN(unsigned long) + 1], + dsize[CHARLEN(unsigned long) + 1], + crdate[CHARLEN(long) + 1], + mddate[CHARLEN(long) + 1], + bkdate[CHARLEN(long) + 1]; + register int argc; + char *argv[24]; + int locked, invis; + + argc = 0; + + argv[argc++] = "name"; + argv[argc++] = ent->name; + + sprintf(cnid, "%lu", ent->cnid); + sprintf(parid, "%lu", ent->parid); + + argv[argc++] = "cnid"; + argv[argc++] = cnid; + + argv[argc++] = "parid"; + argv[argc++] = parid; + + argv[argc++] = "kind"; + + if (ent->flags & HFS_ISDIR) + { + argv[argc++] = "directory"; + + sprintf(dsize, "%u", ent->u.dir.valence); + + argv[argc++] = "size"; + argv[argc++] = dsize; + + argv[argc++] = "flags"; + argv[argc++] = (ent->fdflags & HFS_FNDR_ISINVISIBLE) ? "invis" : ""; + } + else + { + argv[argc++] = "file"; + + argv[argc++] = "type"; + argv[argc++] = ent->u.file.type; + + argv[argc++] = "creator"; + argv[argc++] = ent->u.file.creator; + + locked = ent->flags & HFS_ISLOCKED; + invis = ent->fdflags & HFS_FNDR_ISINVISIBLE; + + argv[argc++] = "flags"; + argv[argc++] = (locked && invis) ? "locked invis" : + (locked ? "locked" : (invis ? "invis" : "")); + + sprintf(rsize, "%lu", ent->u.file.rsize); + sprintf(dsize, "%lu", ent->u.file.dsize); + + argv[argc++] = "rsize"; + argv[argc++] = rsize; + + argv[argc++] = "dsize"; + argv[argc++] = dsize; + } + + sprintf(crdate, "%ld", (long) ent->crdate); + sprintf(mddate, "%ld", (long) ent->mddate); + sprintf(bkdate, "%ld", (long) ent->bkdate); + + argv[argc++] = "crdate"; + argv[argc++] = crdate; + + argv[argc++] = "mddate"; + argv[argc++] = mddate; + + argv[argc++] = "bkdate"; + argv[argc++] = bkdate; + + return Tcl_Merge(argc, argv); +} + +/* + * NAME: getdir() + * DESCRIPTION: collect and return the contents of an HFS directory + */ +static +int getdir(Tcl_Interp *interp, volref *vref, const char *path) +{ + hfsvol *vol = vref->vol; + hfsdir *dir; + hfsdirent ent; + char *str; + + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_stat(vol, path, &ent) == -1) + return error(interp, 0); + + if (ent.flags & HFS_ISDIR) + { + dir = hfs_opendir(vol, path); + if (dir == 0) + return error(interp, 0); + + while (hfs_readdir(dir, &ent) != -1) + { + str = direntstr(&ent); + if (str == 0) + { + hfs_closedir(dir); + Tcl_SetResult(interp, "out of memory", TCL_STATIC); + return TCL_ERROR; + } + + Tcl_AppendElement(interp, str); + + free(str); + } + + if (hfs_closedir(dir) == -1) + return error(interp, 0); + } + else /* ! HFS_ISDIR */ + { + str = direntstr(&ent); + if (str == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + Tcl_AppendElement(interp, str); + + free(str); + } + + return TCL_OK; +} + +/* + * NAME: file->del() + * DESCRIPTION: called by Tcl when a file reference is deleted + */ +static +void file_del(ClientData clientData) +{ + fileref *fref = clientData; + Tcl_HashEntry *entry; + + entry = Tcl_FindHashEntry(&files, (char *) fref); + if (entry) + Tcl_DeleteHashEntry(entry); + + err_close = hfs_close(fref->file); + + FREE(fref); +} + +/* + * NAME: file->cmd() + * DESCRIPTION: operate on an HFS file + */ +static +int file_cmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + fileref *fref = clientData; + hfsfile *file = fref->file; + + switch (argc) + { + case 1: + interp->result = "missing command"; + return TCL_ERROR; + + case 2: + if (strcmp(argv[1], "close") == 0) + { + Tcl_DeleteCommand(interp, argv[0]); + if (err_close == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "tell") == 0) + { + long offset; + + offset = hfs_seek(file, 0, HFS_SEEK_CUR); + if (offset == -1) + return error(interp, 0); + + sprintf(interp->result, "%lu", offset); + } + else if (strcmp(argv[1], "stat") == 0) + { + hfsdirent ent; + char *str; + + if (hfs_fstat(file, &ent) == -1) + return error(interp, 0); + + str = direntstr(&ent); + if (str == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + Tcl_SetResult(interp, str, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "getfork") == 0) + { + interp->result = (hfs_getfork(file) == 0) ? "data" : "rsrc"; + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + case 3: + if (strcmp(argv[1], "setfork") == 0 || + strcmp(argv[1], "fork") == 0) + { + int fork; + + if (strcmp(argv[2], "data") == 0) + fork = 0; + else if (strcmp(argv[2], "rsrc") == 0 || + strcmp(argv[2], "resource") == 0) + fork = 1; + else + { + interp->result = "bad arg to setfork: must be data or rsrc"; + return TCL_ERROR; + } + + hfs_setfork(file, fork); + } + else if (strcmp(argv[1], "seek") == 0) + { + long offset; + + if (Tcl_ExprLong(interp, argv[2], &offset) != TCL_OK) + return TCL_ERROR; + + offset = hfs_seek(file, offset, HFS_SEEK_SET); + if (offset == -1) + return error(interp, 0); + + sprintf(interp->result, "%lu", offset); + } + else if (strcmp(argv[1], "read") == 0) + { + long bytes; + char *mem; + + if (Tcl_ExprLong(interp, argv[2], &bytes) != TCL_OK) + return TCL_ERROR; + + if (bytes < 0) + { + interp->result = "size must be >= 0"; + return TCL_ERROR; + } + + mem = ALLOC(char, bytes + 1); + if (mem == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + bytes = hfs_read(file, mem, bytes); + if (bytes == -1) + { + free(mem); + return error(interp, 0); + } + + mem[bytes] = 0; + + Tcl_SetResult(interp, mem, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "write") == 0) + { + long bytes; + + bytes = hfs_write(file, argv[2], strlen(argv[2])); + if (bytes == -1) + return error(interp, 0); + + sprintf(interp->result, "%lu", bytes); + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + case 4: + if (strcmp(argv[1], "seek") == 0) + { + long offset; + int whence; + + if (Tcl_ExprLong(interp, argv[2], &offset) != TCL_OK) + return TCL_ERROR; + + if (strcmp(argv[3], "start") == 0 || + strcmp(argv[3], "set") == 0) + whence = HFS_SEEK_SET; + else if (strcmp(argv[3], "current") == 0 || + strcmp(argv[3], "cur") == 0) + whence = HFS_SEEK_CUR; + else if (strcmp(argv[3], "end") == 0) + whence = HFS_SEEK_END; + else + { + interp->result = "bad arg 3: must be start, current, or end"; + return TCL_ERROR; + } + + offset = hfs_seek(file, offset, whence); + if (offset == -1) + return error(interp, 0); + + sprintf(interp->result, "%lu", offset); + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + default: + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + * NAME: fork->native() + * DESCRIPTION: copy a single fork in native mode + */ +static +int fork_native(Tcl_Interp *interp, hfsfile *ifile, hfsfile *ofile) +{ + char buf[HFS_BLOCKSZ * 8]; + long chunk, bytes; + + while (1) + { + chunk = hfs_read(ifile, buf, sizeof(buf)); + if (chunk == -1) + return error(interp, "error reading source file"); + else if (chunk == 0) + break; + + bytes = hfs_write(ofile, buf, chunk); + if (bytes == -1) + return error(interp, "error writing destination file"); + else if (bytes != chunk) + return error(interp, "wrote incomplete chunk"); + } + + return TCL_OK; +} + +/* + * NAME: file->ref() + * DESCRIPTION: called to initialize a new file reference + */ +static +void file_ref(Tcl_Interp *interp, volref *vref, fileref *fref, hfsfile *file) +{ + static int id = 0; + Tcl_CmdInfo info; + Tcl_HashEntry *entry; + int new; + + do + sprintf(interp->result, "hfsfile%d", id++); + while (Tcl_GetCommandInfo(interp, interp->result, &info)); + + fref->file = file; + fref->interp = interp; + fref->cmd = Tcl_CreateCommand(interp, interp->result, + file_cmd, fref, file_del); + + entry = Tcl_CreateHashEntry(&files, (char *) fref, &new); + Tcl_SetHashValue(entry, vref); +} + +/* + * NAME: do_copynative() + * DESCRIPTION: perform native HFS file copy + */ +static +int do_copynative(Tcl_Interp *interp, hfsfile *ifile, hfsfile *ofile) +{ + int result; + + if (hfs_setfork(ifile, 0) == -1 || + hfs_setfork(ofile, 0) == -1) + return error(interp, "error opening data fork"); + + result = fork_native(interp, ifile, ofile); + if (result != TCL_OK) + return result; + + if (hfs_setfork(ifile, 1) == -1 || + hfs_setfork(ofile, 1) == -1) + return error(interp, "error opening resource fork"); + + result = fork_native(interp, ifile, ofile); + if (result != TCL_OK) + return result; + + return TCL_OK; +} + +/* + * NAME: copynative() + * DESCRIPTION: copy an HFS file to another HFS volume + */ +static +int copynative(Tcl_Interp *interp, volref *srcvref, char *argv[]) +{ + volref *dstvref; + Tcl_CmdInfo info; + Tcl_HashEntry *entry = 0; + hfsdirent ent; + const char *srcname, *dstname; + hfsfile *ifile, *ofile; + int result; + unsigned long cnid; + + if (Tcl_GetCommandInfo(interp, argv[3], &info)) + entry = Tcl_FindHashEntry(&volumes, (char *) info.clientData); + + if (entry == 0) + { + Tcl_AppendResult(interp, "unknown volume \"", argv[3], "\"", (char *) 0); + return TCL_ERROR; + } + + dstvref = info.clientData; + + srcname = argv[2]; + dstname = argv[4]; + + if (hfs_setcwd(srcvref->vol, srcvref->cwd) == -1) + return error(interp, 0); + + ifile = hfs_open(srcvref->vol, srcname); + if (ifile == 0) + return error(interp, "can't open source file"); + + if (hfs_setcwd(dstvref->vol, dstvref->cwd) == -1) + { + error(interp, 0); + hfs_close(ifile); + return TCL_ERROR; + } + + cnid = 0; + + if (hfs_stat(dstvref->vol, dstname, &ent) != -1) + { + if (ent.flags & HFS_ISDIR) + { + if (hfs_setcwd(dstvref->vol, ent.cnid) == -1) + { + error(interp, 0); + hfs_close(ifile); + return TCL_ERROR; + } + + dstname = srcname; + + if (hfs_stat(dstvref->vol, dstname, &ent) != -1) + cnid = ent.cnid; + } + else + cnid = ent.cnid; + } + + if (hfs_fstat(ifile, &ent) == -1) + { + error(interp, "can't stat source file"); + hfs_close(ifile); + return TCL_ERROR; + } + + if (srcvref->vol == dstvref->vol && + ent.cnid == cnid) + { + interp->result = "source and destination files are the same"; + hfs_close(ifile); + return TCL_ERROR; + } + + hfs_delete(dstvref->vol, dstname); + + ofile = hfs_create(dstvref->vol, dstname, + ent.u.file.type, ent.u.file.creator); + if (ofile == 0) + { + error(interp, "can't create destination file"); + hfs_close(ifile); + return TCL_ERROR; + } + + result = do_copynative(interp, ifile, ofile); + + ent.fdflags &= ~(HFS_FNDR_ISONDESK | HFS_FNDR_HASBEENINITED); + + if (result == TCL_OK && hfs_fsetattr(ofile, &ent) == -1) + result = error(interp, "can't set destination file attributes"); + + if (hfs_close(ofile) == -1 && result == TCL_OK) + result = error(interp, "error closing destination file"); + + if (hfs_close(ifile) == -1 && result == TCL_OK) + result = error(interp, "error closing source file"); + + return result; +} + +/* + * NAME: copyin() + * DESCRIPTION: copy a UNIX file into an HFS volume + */ +static +int copyin(Tcl_Interp *interp, hfsvol *vol, char *argv[]) +{ + cpifunc copyfile; + + if (strcmp(argv[2], "macbinary") == 0 || + strcmp(argv[2], "macb") == 0) + copyfile = cpi_macb; + else if (strcmp(argv[2], "binhex") == 0 || + strcmp(argv[2], "binh") == 0) + copyfile = cpi_binh; + else if (strcmp(argv[2], "text") == 0) + copyfile = cpi_text; + else if (strcmp(argv[2], "raw") == 0 || + strcmp(argv[2], "data") == 0) + copyfile = cpi_raw; + else + { + interp->result = "bad mode: must be macb, binh, text, or raw"; + return TCL_ERROR; + } + + if (copyfile(argv[3], vol, argv[4]) == -1) + { + ERROR(errno, cpi_error); + return error(interp, 0); + } + + return TCL_OK; +} + +/* + * NAME: copyout() + * DESCRIPTION: copy an HFS file out to a UNIX file + */ +static +int copyout(Tcl_Interp *interp, hfsvol *vol, char *argv[]) +{ + cpofunc copyfile; + + if (strcmp(argv[2], "macbinary") == 0 || + strcmp(argv[2], "macb") == 0) + copyfile = cpo_macb; + else if (strcmp(argv[2], "binhex") == 0 || + strcmp(argv[2], "binh") == 0) + copyfile = cpo_binh; + else if (strcmp(argv[2], "text") == 0) + copyfile = cpo_text; + else if (strcmp(argv[2], "raw") == 0 || + strcmp(argv[2], "data") == 0) + copyfile = cpo_raw; + else + { + interp->result = "bad mode: must be macb, binh, text, or raw"; + return TCL_ERROR; + } + + if (copyfile(vol, argv[3], argv[4]) == -1) + { + ERROR(errno, cpo_error); + return error(interp, 0); + } + + return TCL_OK; +} + +/* + * NAME: do_zero() + * DESCRIPTION: call hfs_zero() with necessary privileges + */ +static +int do_zero(const char *path, unsigned int nparts, unsigned long *len) +{ + int result; + + suid_enable(); + result = hfs_zero(path, nparts, len); + suid_disable(); + + return result; +} + +/* + * NAME: do_mkpart() + * DESCRIPTION: call hfs_mkpart() with necessary privileges + */ +static +int do_mkpart(const char *path, unsigned long len) +{ + int result; + + suid_enable(); + result = hfs_mkpart(path, len); + suid_disable(); + + return result; +} + +/* + * NAME: do_format() + * DESCRIPTION: call hfs_format() with necessary privileges + */ +static +int do_format(const char *path, int partno, int mode, const char *vname, + unsigned int nbadblocks, unsigned long badblocks[]) +{ + int result; + + suid_enable(); + result = hfs_format(path, partno, mode, vname, nbadblocks, badblocks); + suid_disable(); + + return result; +} + +/* + * NAME: vol->del() + * DESCRIPTION: called by Tcl when a volume reference is deleted + */ +static +void vol_del(ClientData clientData) +{ + volref *vref = clientData; + Tcl_HashEntry *entry; + + entry = Tcl_FindHashEntry(&volumes, (char *) vref); + if (entry) + Tcl_DeleteHashEntry(entry); + + do + { + Tcl_HashSearch search; + + for (entry = Tcl_FirstHashEntry(&files, &search); entry; + entry = Tcl_NextHashEntry(&search)) + { + if (Tcl_GetHashValue(entry) == vref) + { + fileref *fref = (fileref *) Tcl_GetHashKey(&files, entry); + + Tcl_DeleteCommand(fref->interp, + Tcl_GetCommandName(fref->interp, fref->cmd)); + break; + } + } + } + while (entry); + + err_umount = hfs_umount(vref->vol); + + FREE(vref); +} + +/* + * NAME: vol->cmd() + * DESCRIPTION: operate on an HFS volume + */ +static +int vol_cmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + volref *vref = clientData; + hfsvol *vol = vref->vol; + + switch (argc) + { + case 1: + interp->result = "missing command"; + return TCL_ERROR; + + case 2: + if (strcmp(argv[1], "vname") == 0) + { + hfsvolent ent; + + hfs_vstat(vol, &ent); + Tcl_SetResult(interp, ent.name, TCL_VOLATILE); + } + else if (strcmp(argv[1], "size") == 0) + { + hfsvolent ent; + + hfs_vstat(vol, &ent); + sprintf(interp->result, "%lu %lu", ent.totbytes, ent.freebytes); + } + else if (strcmp(argv[1], "crdate") == 0) + { + hfsvolent ent; + + hfs_vstat(vol, &ent); + sprintf(interp->result, "%ld", (long) ent.crdate); + } + else if (strcmp(argv[1], "mddate") == 0) + { + hfsvolent ent; + + hfs_vstat(vol, &ent); + sprintf(interp->result, "%ld", (long) ent.mddate); + } + else if (strcmp(argv[1], "islocked") == 0) + { + hfsvolent ent; + + hfs_vstat(vol, &ent); + if (ent.flags & HFS_ISLOCKED) + interp->result = "1"; + else + interp->result = "0"; + } + else if (strcmp(argv[1], "umount") == 0) + { + Tcl_DeleteCommand(interp, argv[0]); + if (err_umount == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "cwd") == 0) + sprintf(interp->result, "%lu", vref->cwd); + else if (strcmp(argv[1], "path") == 0) + { + char name[HFS_MAX_FLEN + 1]; + long id; + int listc, i; + char **listv; + char *result; + + id = vref->cwd; + while (id != HFS_CNID_ROOTPAR) + { + if (hfs_dirinfo(vol, &id, name) == -1) + return error(interp, 0); + + Tcl_AppendElement(interp, name); + } + + /* reverse the resulting list */ + + if (Tcl_SplitList(interp, interp->result, &listc, &listv) != TCL_OK) + return TCL_ERROR; + + for (i = 0; i < listc / 2; ++i) + { + char *tmp; + + tmp = listv[i]; + listv[i] = listv[listc - 1 - i]; + listv[listc - 1 - i] = tmp; + } + + result = Tcl_Merge(listc, listv); + free(listv); + + Tcl_SetResult(interp, result, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "dir") == 0) + { + if (getdir(interp, vref, ":") != TCL_OK) + return TCL_ERROR; + } + else if (strcmp(argv[1], "flush") == 0) + { + if (hfs_flush(vol) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "sepchar") == 0) + interp->result = ":"; + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + case 3: + if (strcmp(argv[1], "cd") == 0 || + strcmp(argv[1], "chdir") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_chdir(vol, argv[2]) == -1) + return error(interp, 0); + + vref->cwd = hfs_getcwd(vol); + } + else if (strcmp(argv[1], "dirinfo") == 0) + { + long id; + char name[HFS_MAX_FLEN + 1], idstr[CHARLEN(unsigned long) + 1]; + + if (Tcl_ExprLong(interp, argv[2], &id) != TCL_OK) + return TCL_ERROR; + + if (hfs_dirinfo(vol, &id, name) == -1) + return error(interp, 0); + + sprintf(idstr, "%lu", id); + Tcl_AppendElement(interp, name); + Tcl_AppendElement(interp, idstr); + } + else if (strcmp(argv[1], "dir") == 0) + { + if (getdir(interp, vref, argv[2]) != TCL_OK) + return TCL_ERROR; + } + else if (strcmp(argv[1], "open") == 0) + { + fileref *fref; + hfsfile *file; + + fref = ALLOC(fileref, 1); + if (fref == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + if (hfs_setcwd(vol, vref->cwd) == -1 || + (file = hfs_open(vol, argv[2])) == 0) + { + FREE(fref); + return error(interp, 0); + } + + file_ref(interp, vref, fref, file); + } + else if (strcmp(argv[1], "stat") == 0) + { + hfsdirent ent; + char *str; + + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_stat(vol, argv[2], &ent) == -1) + return error(interp, 0); + + str = direntstr(&ent); + if (str == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + Tcl_SetResult(interp, str, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "mkdir") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_mkdir(vol, argv[2]) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "rmdir") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_rmdir(vol, argv[2]) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "delete") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_delete(vol, argv[2]) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "touch") == 0) + { + hfsdirent ent; + + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_stat(vol, argv[2], &ent) == -1) + return error(interp, 0); + + ent.mddate = time(0); + + if (hfs_setattr(vol, argv[2], &ent) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "glob") == 0) + { + int listc, fargc; + char **listv, **fargv, *result; + + if (hfs_setcwd(vol, vref->cwd) == -1) + return error(interp, 0); + + if (Tcl_SplitList(interp, argv[2], &listc, &listv) != TCL_OK) + return TCL_ERROR; + + fargv = hfs_glob(vol, listc, listv, &fargc); + free(listv); + + if (fargv == 0) + { + interp->result = "globbing error"; + return TCL_ERROR; + } + + result = Tcl_Merge(fargc, fargv); + free(fargv); + + Tcl_SetResult(interp, result, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "bless") == 0) + { + hfsvolent volent; + hfsdirent dirent; + + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_vstat(vol, &volent) == -1 || + hfs_stat(vol, argv[2], &dirent) == -1) + return error(interp, 0); + + volent.blessed = dirent.cnid; + + if (hfs_vsetattr(vol, &volent) == -1) + return error(interp, 0); + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + case 4: + if (strcmp(argv[1], "rename") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1 || + hfs_rename(vol, argv[2], argv[3]) == -1) + return error(interp, 0); + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + case 5: + if (strcmp(argv[1], "create") == 0) + { + fileref *fref; + hfsfile *file; + + if (strlen(argv[3]) != 4 || + strlen(argv[4]) != 4) + { + interp->result = "type and creator must be 4 character strings"; + return TCL_ERROR; + } + + fref = ALLOC(fileref, 1); + if (fref == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + if (hfs_setcwd(vol, vref->cwd) == -1 || + (file = hfs_create(vol, argv[2], argv[3], argv[4])) == 0) + { + FREE(fref); + return error(interp, 0); + } + + file_ref(interp, vref, fref, file); + } + else if (strcmp(argv[1], "copy") == 0) + return copynative(interp, vref, argv); + else if (strcmp(argv[1], "copyin") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1) + return error(interp, 0); + + return copyin(interp, vol, argv); + } + else if (strcmp(argv[1], "copyout") == 0) + { + if (hfs_setcwd(vol, vref->cwd) == -1) + return error(interp, 0); + + return copyout(interp, vol, argv); + } + else + { + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + break; + + default: + Tcl_AppendResult(interp, "bad command \"", argv[1], + "\" or wrong # args", (char *) 0); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + * NAME: cmd->hfs() + * DESCRIPTION: Tcl HFS command callback + */ +static +int cmd_hfs(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + static int id = 0; + + if (argc < 2) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if (strcmp(argv[1], "mount") == 0) + { + int partno; + hfsvol *vol; + volref *vref; + Tcl_CmdInfo info; + Tcl_HashEntry *entry; + int new; + + if (argc < 3 || argc > 4) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if (argc == 4) + { + if (Tcl_GetInt(interp, argv[3], &partno) != TCL_OK) + return TCL_ERROR; + } + else + { + int nparts; + + suid_enable(); + nparts = hfs_nparts(argv[2]); + suid_disable(); + + if (nparts > 1) + { + sprintf(interp->result, "must specify partition number " + "(%d available)", nparts); + return TCL_ERROR; + } + else if (nparts == -1) + partno = 0; + else + partno = 1; + } + + vref = ALLOC(volref, 1); + if (vref == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + suid_enable(); + vol = hfs_mount(argv[2], partno, HFS_MODE_ANY); + suid_disable(); + + if (vol == 0) + { + error(interp, "can't mount volume"); + FREE(vref); + return TCL_ERROR; + } + + vref->vol = vol; + vref->cwd = HFS_CNID_ROOTDIR; + + entry = Tcl_CreateHashEntry(&volumes, (char *) vref, &new); + + do + sprintf(interp->result, "hfsvol%d", id++); + while (Tcl_GetCommandInfo(interp, interp->result, &info)); + + Tcl_CreateCommand(interp, interp->result, + vol_cmd, vref, vol_del); + } + else if (strcmp(argv[1], "zero") == 0) + { + int nparts; + unsigned long len; + + if (argc != 4) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[3], &nparts) != TCL_OK) + return TCL_ERROR; + + if (do_zero(argv[2], nparts, &len) == -1) + return error(interp, 0); + + sprintf(interp->result, "%lu", len); + } + else if (strcmp(argv[1], "mkpart") == 0) + { + unsigned int len; + + if (argc != 4) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[3], &len) != TCL_OK) + return TCL_ERROR; + + if (do_mkpart(argv[2], len) == -1) + return error(interp, 0); + } + else if (strcmp(argv[1], "nparts") == 0) + { + int nparts; + + if (argc != 3) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + suid_enable(); + nparts = hfs_nparts(argv[2]); + suid_disable(); + + sprintf(interp->result, "%d", nparts); + } + else if (strcmp(argv[1], "format") == 0) + { + int partno; + + if (argc < 5 || argc > 6) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[3], &partno) != TCL_OK) + return TCL_ERROR; + + if (argc == 6) + { + int listc, i; + char **listv; + unsigned long *badblocks; + + if (Tcl_SplitList(interp, argv[5], &listc, &listv) != TCL_OK) + return TCL_ERROR; + + badblocks = ALLOCX(unsigned long, listc); + if (listc && badblocks == 0) + { + free(listv); + + interp->result = "out of memory"; + return TCL_ERROR; + } + + for (i = 0; i < listc; ++i) + { + if (Tcl_ExprLong(interp, listv[i], + (long *) &badblocks[i]) != TCL_OK) + { + free(listv); + FREE(badblocks); + return TCL_ERROR; + } + } + + free(listv); + + if (do_format(argv[2], partno, 0, argv[4], listc, badblocks) == -1) + { + FREE(badblocks); + + return error(interp, 0); + } + + FREE(badblocks); + } + else + { + if (do_format(argv[2], partno, 0, argv[4], 0, 0) == -1) + return error(interp, 0); + } + } + else if (strcmp(argv[1], "flushall") == 0) + hfs_flushall(); + else if (strcmp(argv[1], "chartrans") == 0) + { + char *result; + + if (argc != 5) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + if ((strcmp(argv[2], "latin1") != 0 && + strcmp(argv[2], "macroman") != 0) || + (strcmp(argv[3], "latin1") != 0 && + strcmp(argv[3], "macroman") != 0)) + { + interp->result = "bad arg to chartrans: " + "charsets must be one of latin1, macroman"; + return TCL_ERROR; + } + + if (strcmp(argv[2], "latin1") == 0 && + strcmp(argv[3], "macroman") == 0) + result = cs_macroman(argv[4], 0); + else if (strcmp(argv[2], "macroman") == 0 && + strcmp(argv[3], "latin1") == 0) + result = cs_latin1(argv[4], 0); + else + { + Tcl_SetResult(interp, argv[4], TCL_VOLATILE); + return TCL_OK; + } + + if (result == 0) + { + interp->result = "out of memory"; + return TCL_ERROR; + } + + Tcl_SetResult(interp, result, TCL_DYNAMIC); + } + else if (strcmp(argv[1], "version") == 0) + { + if (argc != 2) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + interp->result = (char *) hfsutils_version; + } + else if (strcmp(argv[1], "copyright") == 0) + { + if (argc != 2) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + interp->result = (char *) hfsutils_copyright; + } + else if (strcmp(argv[1], "author") == 0) + { + if (argc != 2) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + interp->result = (char *) hfsutils_author; + } + else if (strcmp(argv[1], "license") == 0) + { + if (argc != 2) + { + interp->result = "wrong # args"; + return TCL_ERROR; + } + + interp->result = (char *) hfsutils_license; + } + else + { + Tcl_AppendResult(interp, "bad hfs command \"", argv[1], + "\": should be one of " + "mount, zero, mkpart, format, flushall, " + "version, copyright, author, license", + (char *) 0); + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + * NAME: cmd->exit() + * DESCRIPTION: called to terminate; clean up volume state + */ +static +int cmd_exit(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int status = 0; + + if (argc > 2) + { + interp->result = "wrong # args: should be \"exit ?returnCode?\""; + return TCL_ERROR; + } + + if (argc == 2 && Tcl_GetInt(interp, argv[1], &status) != TCL_OK) + return TCL_ERROR; + + hfs_umountall(); + + exit(status); + + return TCL_OK; +} + +/* + * NAME: Hfs->Init() + * DESCRIPTION: initialize Tcl components for HFS handling + */ +int Hfs_Init(Tcl_Interp *interp) +{ + Tcl_InitHashTable(&volumes, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&files, TCL_ONE_WORD_KEYS); + + Tcl_CreateCommand(interp, "hfs", cmd_hfs, 0, 0); + Tcl_CreateCommand(interp, "exit", cmd_exit, 0, 0); + + return TCL_OK; +} diff --git a/tclhfs.h b/tclhfs.h new file mode 100644 index 0000000..d012c63 --- /dev/null +++ b/tclhfs.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: tclhfs.h,v 1.7 1998/04/11 08:27:01 rob Exp $ + */ + +int Hfs_Init(Tcl_Interp *); diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..b117abb --- /dev/null +++ b/test/Makefile @@ -0,0 +1,36 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: Makefile,v 1.5 1998/04/11 08:27:23 rob Exp $ +# + +all :: test1 test2 + +clean :: + rm -f gmon.* image.hfs core + +depend :: + +test1 :: ../hfssh ../hfs + @echo; echo "source main.tcl; test1" | ../hfssh ../hfs + +test2 :: ../hfssh ../hfs + @echo; echo "source main.tcl; test2" | ../hfssh ../hfs + +test3 :: ../hfssh ../hfs + @echo; echo "source main.tcl; test3" | ../hfssh ../hfs diff --git a/test/main.tcl b/test/main.tcl new file mode 100644 index 0000000..7e8799e --- /dev/null +++ b/test/main.tcl @@ -0,0 +1,43 @@ +# +# NAME: block +# DESCRIPTION: return a string of length 512 (size of one HFS block) +# +proc block {} { + set block "0123456789abcdef" + set block "$block$block$block$block" + set block "$block$block$block$block" + set block "$block$block" + + return $block +} + +# +# NAME: mkvol +# DESCRIPTION: create an empty test volume and mount it +# +proc mkvol {{size 1440} {path "image.hfs"}} { + catch {humount $path} + + puts "Zeroing $path (${size}K)..." + catch {exec rm -f $path} + catch {exec dd if=/dev/zero of=$path bs=1k count=$size} msg + puts $msg + + puts "Formatting $path..." + hformat $path 0 "Test Volume" +} + +# +# NAME: remount +# DESCRIPTION: flush and remount a volume +# +proc remount {{path "image.hfs"}} { + puts "Remounting $path..." + + humount $path + hmount $path 0 +} + +foreach test [glob test*.tcl] { + source $test +} diff --git a/test/test1.tcl b/test/test1.tcl new file mode 100644 index 0000000..9a0a23b --- /dev/null +++ b/test/test1.tcl @@ -0,0 +1,119 @@ +# +# NAME: test1 +# DESCRIPTION: fragment two files to excess +# +proc test1 {} { + global curvol + + set block [block] + set max 250 + + mkvol + + puts "Creating files..." + + set file1 "Foo" + set file2 "Bar" + + hcreate $file1 + hcreate $file2 + + puts "Fragmenting files..." + + set count 0 + + for {set i 0} {$i < $max} {incr i} { + set len [string length "$i "] + + if {[expr $count + $len] > 79} { + puts "" + set count 0 + } + + puts -nonewline "$i " + flush stdout + incr count $len + + set file [$curvol open $file1] + + $file fork data + $file seek 0 end + $file write $block + + $file fork rsrc + $file seek 0 end + $file write $block + + $file close + + set file [$curvol open $file2] + + $file fork data + $file seek 0 end + $file write $block + + $file fork rsrc + $file seek 0 end + $file write $block + + $file close + } + + puts "" + + remount + + puts "Comparing file data forks..." + + set fh1 [$curvol open $file1] + set fh2 [$curvol open $file2] + + $fh1 fork data + $fh2 fork data + + for {set i 0} {$i < $max} {incr i} { + set data [$fh1 read [string length $block]] + if {[string compare $data $block]} { + error "$fh1 ($file1) fork data block $i differs" + } + + set data [$fh2 read [string length $block]] + if {[string compare $data $block]} { + error "$fh2 ($file2) fork data block $i differs" + } + } + + if {[string length [$fh1 read 1]]} { + error "$fh1 ($file1) bad data fork length" + } + if {[string length [$fh2 read 1]]} { + error "$fh2 ($file2) bad data fork length" + } + + puts "Comparing file resource forks..." + + $fh1 fork rsrc + $fh2 fork rsrc + + for {set i 0} {$i < $max} {incr i} { + set data [$fh1 read [string length $block]] + if {[string compare $data $block]} { + error "$fh1 ($file1) fork rsrc block $i differs" + } + + set data [$fh2 read [string length $block]] + if {[string compare $data $block]} { + error "$fh2 ($file2) fork rsrc block $i differs" + } + } + + if {[string length [$fh1 read 1]]} { + error "$fh1 ($file1) bad resource fork length" + } + if {[string length [$fh2 read 1]]} { + error "$fh2 ($file2) bad resource fork length" + } + + $fh1 close + $fh2 close +} diff --git a/test/test2.tcl b/test/test2.tcl new file mode 100644 index 0000000..22d54dc --- /dev/null +++ b/test/test2.tcl @@ -0,0 +1,74 @@ +# +# NAME: test2 +# DESCRIPTION: create hundreds of files and directories +# +proc test2 {} { + global curvol + + set block [block] + + mkvol + + puts "Creating files and directories..." + + foreach dir {one two three four five six seven eight nine ten} { + puts -nonewline [format "%6s: " $dir] + + hmkdir $dir + hcd $dir + + foreach subdir {a b c d e f g h i j} { + puts -nonewline "$subdir " + flush stdout + + hmkdir "$dir/$subdir" + hcd "$dir/$subdir" + + foreach subsubdir {1 2 3 4 5 6 7 8 9 10} { + hmkdir "$dir/$subdir/$subsubdir" + + set fname ":$dir/$subdir/$subsubdir:$dir/$subdir/$subsubdir/File" + + set fd [$curvol create $fname "TEXT" "UNIX"] + $fd write $block + $fd close + } + + hcd :: + } + + hcd :: + + puts "" + } + + remount + + puts "Deleting files..." + + foreach dir {one two three four five six seven eight nine ten} { + puts -nonewline [format "%6s: " $dir] + + hcd $dir + + foreach subdir {a b c d e f g h i j} { + puts -nonewline "$subdir " + flush stdout + + hcd "$dir/$subdir" + + foreach subsubdir {2 4 6 8 10} { + hdel ":$dir/$subdir/$subsubdir:$dir/$subdir/$subsubdir/File" + # hrmdir "$dir/$subdir/$subsubdir" + } + + hcd :: + # hrmdir "$dir/$subdir" + } + + hcd :: + # hrmdir $dir + + puts "" + } +} diff --git a/test/test3.tcl b/test/test3.tcl new file mode 100644 index 0000000..cb6456c --- /dev/null +++ b/test/test3.tcl @@ -0,0 +1,13 @@ +# +# NAME: test3 +# DESCRIPTION: create thousands of files +# +proc test3 {} { + mkvol [expr 100 * 1024] ;# /usr/local/mirrors/image.hfs + + puts "Creating files..." + + for {set i 0} {$i < 25000} {incr i} { + hcreate [format "f%x" $i] + } +} diff --git a/version.c b/version.c new file mode 100644 index 0000000..9f37ab6 --- /dev/null +++ b/version.c @@ -0,0 +1,44 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.c,v 1.11 1998/11/02 22:08:33 rob Exp $ + */ + +# include "version.h" + +const char hfsutils_rcsid[] = + "$Id: version.c,v 1.11 1998/11/02 22:08:33 rob Exp $"; + +const char hfsutils_version[] = "hfsutils version 3.2.6"; +const char hfsutils_copyright[] = "Copyright (C) 1996-1998 Robert Leslie"; +const char hfsutils_author[] = "Robert Leslie "; + +const char hfsutils_license[] = + "This program is free software; you can redistribute it and/or modify it\n" + "under the terms of the GNU General Public License as published by the\n" + "Free Software Foundation; either version 2 of the License, or (at your\n" + "option) any later version.\n\n" + + "This program is distributed in the hope that it will be useful, but\n" + "WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + "General Public License for more details.\n\n" + + "You should have received a copy of the GNU General Public License along\n" + "with this program; if not, write to the Free Software Foundation, Inc.,\n" + "675 Mass Ave, Cambridge, MA 02139, USA.\n\n"; diff --git a/version.h b/version.h new file mode 100644 index 0000000..9b5f7e4 --- /dev/null +++ b/version.h @@ -0,0 +1,28 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: version.h,v 1.10 1998/09/18 22:56:27 rob Exp $ + */ + +extern const char hfsutils_rcsid[]; + +extern const char hfsutils_version[]; +extern const char hfsutils_copyright[]; +extern const char hfsutils_author[]; + +extern const char hfsutils_license[]; diff --git a/xhfs.h b/xhfs.h new file mode 100644 index 0000000..1ff117b --- /dev/null +++ b/xhfs.h @@ -0,0 +1,22 @@ +/* + * hfsutils - tools for reading and writing Macintosh HFS volumes + * Copyright (C) 1996-1998 Robert Leslie + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: xhfs.h,v 1.7 1998/04/11 08:27:01 rob Exp $ + */ + +extern char xhfs[]; diff --git a/xhfs.tcl b/xhfs.tcl new file mode 100644 index 0000000..efcb62d --- /dev/null +++ b/xhfs.tcl @@ -0,0 +1,1926 @@ +# +# hfsutils - tools for reading and writing Macintosh HFS volumes +# Copyright (C) 1996-1998 Robert Leslie +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id: xhfs.tcl,v 1.8 1998/04/11 08:27:01 rob Exp $ +# + +set tk_strictMotif 1 + +set gNormalFont {-adobe-helvetica-bold-r-normal-*-*-120-*-*-p-*-iso8859-1} +set gThinFont {-adobe-helvetica-medium-r-normal-*-*-120-*-*-p-*-iso8859-1} +set gSmallFont {-adobe-helvetica-medium-r-normal-*-*-100-*-*-p-*-iso8859-1} +set gTypeFont {-*-lucidatypewriter-medium-r-normal-*-*-120-*-*-m-*-iso8859-1} +set gTitleFont {-*-lucida-bold-i-normal-*-*-250-*-*-p-*-iso8859-1} + +set gDefaultDevice /dev/fd0 +set gLowFormatCmd [list fdformat $gDefaultDevice] +set gMountSide .l +set gFlushInterval 30000 +set gDfCmd [list df] + +option add *Font $gNormalFont + +############################################################################### + +# Set architecture defaults + +if {[catch {exec uname} gUname] == 0} { + switch $gUname { + SunOS { + if {[catch {exec uname -r} gUrelease] == 0} { + switch -glob $gUrelease { + 4.* { + set gLowFormatCmd [list fdformat -f /dev/rfd0] + } + 5.* { + set gDefaultDevice /vol/dev/aliases/floppy0 + set gLowFormatCmd [list fdformat -f $gDefaultDevice] + set gDfCmd [list df -k] + } + } + } + } + + FreeBSD { + set gDefaultDevice /dev/rfd0 + set gLowFormatCmd [list fdformat $gDefaultDevice] + } + } +} + +############################################################################### + +set gUnique 0 + +proc unique {} { + global gUnique + + return [incr gUnique] +} + +proc window {title iconname} { + set w ".w[unique]" + + toplevel $w -cursor left_ptr -relief raised -bd 1 + wm title $w $title + wm iconname $w $iconname + wm transient $w . + + wm withdraw $w + + return $w +} + +proc showwindow {w} { + update idletasks + + set x [expr [winfo x .] + [winfo width .]/2 - [winfo reqwidth $w]/2] + set y [expr [winfo y .] + [winfo height .]/3 - [winfo reqheight $w]/3] + + wm geom $w +$x+$y + wm deiconify $w +} + +proc dialog {title cancel {ok ""} {default 0}} { + set w ".d[unique]" + + toplevel $w -class Dialog -cursor left_ptr + wm title $w $title + wm transient $w . + + wm withdraw $w + + frame $w.top -relief raised -bd 1 + frame $w.bot -relief raised -bd 1 + pack $w.top -side top -expand 1 -fill both -ipadx 4m -ipady 4m + pack $w.bot -side bottom -fill both -ipadx 2m -ipady 2m + + frame $w.bot.default -relief sunken -bd 1 + + button $w.bot.cancel -width 6 -text $cancel \ + -command "set gAnswer 0; [list destroy $w]" + if {[string length $ok] > 0} { + button $w.bot.ok -width 6 -text $ok \ + -command "set gAnswer 1; [list destroy $w]" + } + + if {$default} { + pack $w.bot.cancel -side left -expand 1;# -ipadx 2m -ipady 1m + pack $w.bot.default -side left -expand 1 -ipadx 1m -ipady 1m + pack $w.bot.ok -in $w.bot.default -expand 1;# -ipadx 2m -ipady 1m + bind $w [list $w.bot.ok invoke] + } else { + pack $w.bot.default -side left -expand 1 -ipadx 1m -ipady 1m + pack $w.bot.cancel -in $w.bot.default -expand 1;# -ipadx 2m -ipady 1m + if {[string length $ok] > 0} { + pack $w.bot.ok -side left -expand 1;# -ipadx 2m -ipady 1m + } + bind $w [list $w.bot.cancel invoke] + } + + return $w +} + +proc showdialog {w} { + showwindow $w + grab set $w +} + +proc alert {kind title text {aspect 100}} { + global gThinFont + + set w [dialog $title "OK"] + + frame $w.top.x + label $w.top.x.icon -bitmap $kind -anchor n + frame $w.top.x.space -width 2m + message $w.top.x.msg -aspect $aspect -font $gThinFont -text $text + pack $w.top.x.icon $w.top.x.space $w.top.x.msg -side left -fill y + + pack $w.top.x -expand 1 + + showdialog $w + tkwait window $w +} + +proc bgerror {error} { + alert stop "Error" "Sorry, $error." 1000 + return -code break +} + +proc fixtext {text} { + regsub -all "(\[^\n])\n\[ \t]*(\[^\n])" $text {\1 \2} text + regsub -all "(^|\n)\[ \t]+" $text {\1} text + regsub "\n*\$" $text "" text + + return $text +} + +proc copy_ufs_to_hfs {srcvol name mode dstvol} { + $dstvol copyin $mode "[$srcvol cwd]/$name" : +} + +proc copy_hfs_to_ufs {srcvol name mode dstvol} { + $srcvol copyout $mode $name "[$dstvol cwd]/." +} + +proc copy_hfs_to_hfs {srcvol name mode dstvol} { + $srcvol copy $name $dstvol : +} + +proc copy_ufs_to_ufs {srcvol name mode dstvol} { + $srcvol copy $name "[$dstvol cwd]/." +} + +proc copy_directory {srcvol name dstvol copyproc modeproc} { + global gXMode + + set srccwd [abspath [$srcvol sepchar] [$srcvol path]] + set dstcwd [abspath [$dstvol sepchar] [$dstvol path]] + + $dstvol mkdir $name + $srcvol chdir $name + $dstvol chdir $name + + foreach ind [$srcvol dir] { + array set item $ind + + if {$item(kind) == "directory"} { + copy_directory $srcvol $item(name) $dstvol $copyproc $modeproc + } else { + if {$gXMode == "auto"} { + set mode [$modeproc $srcvol item] + } else { + set mode $gXMode + } + + $copyproc $srcvol $item(name) $mode $dstvol + } + } + + $srcvol chdir $srccwd + $dstvol chdir $dstcwd +} + +proc delete_directory {vol name} { + set cwd [abspath [$vol sepchar] [$vol path]] + + $vol chdir $name + + foreach ind [$vol dir] { + array set item $ind + + if {$item(kind) == "directory"} { + delete_directory $vol $item(name) + } else { + $vol delete $item(name) + } + } + + $vol chdir $cwd + $vol rmdir $name +} + +proc do_open {side} { + set box $side.mid.lb + set sel [$box curselection] + set ind [lindex $sel 0] + + global vol + + set name [$box get $ind] + if {[regexp "(.*)[$vol($side) sepchar]\$" $name ignore name]} { + mchdir $side $name + } else { + show_file $side $name + } +} + +proc do_copy {side other} { + set box $side.mid.lb + set sel [$box curselection] + + global gXMode gAskDelete gAnswer + global fstype dir vol + + if {$side == ".l"} { + set other ".r" + } else { + set other ".l" + } + + set needupdate 0 + + foreach ind $sel { + array set item [lindex $dir($side) $ind] + + $box see $ind + + # if {$gAskDelete && [catch {$vol($other) stat $item(name)}] == 0} { + # set w [dialog "Overwrite" "Cancel" "OK"] + + # frame $w.top.x + # label $w.top.x.icon -bitmap caution -anchor n + # frame $w.top.x.space -width 2m + # message $w.top.x.msg -font $gThinFont -aspect 500 \ + # -text "Overwrite \"$item(name)\"?" + # pack $w.top.x.icon $w.top.x.space $w.top.x.msg -side left -fill y + + # pack $w.top.x -expand 1 + + # showdialog $w + # tkwait window $w + + # if {$gAnswer == 0} { + # break + # } + # } + + watch_cursor + + if {$item(kind) == "directory"} { + set update [after idle \ + "[list updatelist $side]; [list updatelist $other]"] + + copy_directory $vol($side) $item(name) $vol($other) \ + copy_$fstype($side)_to_$fstype($other) \ + $fstype($side)_automode + } else { + set update [after idle [list updatelist $other]] + + if {$gXMode == "auto"} { + set mode [$fstype($side)_automode $vol($side) item] + } else { + set mode $gXMode + } + + copy_$fstype($side)_to_$fstype($other) \ + $vol($side) $item(name) $mode $vol($other) + } + + after cancel $update + set needupdate 1 + + $box selection clear $ind + } + + if {$needupdate} { + updatelist $other; clearlists + } +} + +proc do_info {side} { + set box $side.mid.lb + set sel [$box curselection] + + global vol dir + + foreach ind $sel { + show_info $vol($side) [lindex $dir($side) $ind] + } +} + +proc do_rename {side} { + global dir + global gTypeFont + + set box $side.mid.lb + set sel [$box curselection] + array set item [lindex $dir($side) [lindex $sel 0]] + + set w [dialog "Rename" "Cancel" "Rename" 1] + $w.bot.ok config -command [list rename2 $w $side] + + frame $w.top.x + + label $w.top.x.lbl -text "Rename \"$item(name)\" to:" + label $w.top.x.srcname -text $item(name) + entry $w.top.x.dstname -width 25 -font $gTypeFont -exportsel 0 + + pack $w.top.x.lbl $w.top.x.dstname -side top -pady 0.5m + pack $w.top.x -expand 1 + + $w.top.x.dstname insert end $item(name) + $w.top.x.dstname selection range 0 end + + focus $w.top.x.dstname + showdialog $w +} + +proc rename2 {w side} { + global vol + + set srcname [$w.top.x.srcname cget -text] + set dstname [$w.top.x.dstname get] + + watch_cursor $w + destroy $w + + if {[string length $dstname] == 0} { + error "cannot rename to zero-length name" + } + + if {[string compare $srcname $dstname] != 0} { + $vol($side) rename $srcname $dstname + updatelist $side; clearlists + } +} + +proc do_delete {side} { + set box $side.mid.lb + set sel [$box curselection] + + global gThinFont gAskDelete gAnswer + global vol dir + + set sepchar [$vol($side) sepchar] + set needupdate 0 + + foreach ind $sel { + array set item [lindex $dir($side) $ind] + + $box see $ind + + set recursive [expr {$item(kind) == "directory" && $item(size) > 0}] + + if {$recursive || $gAskDelete} { + set w [dialog "Delete" "Cancel" "Delete"] + + if {$recursive} { + set p1 "Recursively delete \"$item(name)\"" + set p2 "containing $item(size) item[plural $item(size) "" "s"]" + set prompt "$p1 $p2?" + } else { + set prompt "Really delete \"$item(name)\"?" + } + + frame $w.top.x + label $w.top.x.icon -bitmap caution -anchor n + frame $w.top.x.space -width 2m + message $w.top.x.msg -font $gThinFont -aspect 500 -text $prompt + pack $w.top.x.icon $w.top.x.space $w.top.x.msg -side left -fill y + + pack $w.top.x -expand 1 + + showdialog $w + tkwait window $w + + if {$gAnswer == 0} { + break + } + } + + watch_cursor + set update [after idle "[list updatelist $side]; clearlists"] + + if {$item(kind) == "directory"} { + delete_directory $vol($side) $item(name) + } else { + $vol($side) delete $item(name) + } + + after cancel $update + set needupdate 1 + + $box selection clear $ind + } + + if {$needupdate} { + updatelist $side; clearlists + } +} + +proc format_disk {} { + global gTypeFont gThinFont gLowFormatCmd gDefaultDevice + + set w [dialog "Format Disk" "Cancel" "Format"] + $w.bot.ok config -command [list format2 $w] + + frame $w.top.x + + set low [frame $w.top.x.low] + label $low.icon -bitmap floppy + + frame $low.info + checkbutton $low.info.cb -var gLowFormat \ + -text "Low-level Format" -anchor w + entry $low.info.cmd -width 26 -font $gTypeFont + pack $low.info.cb $low.info.cmd -side top -fill x -pady 0.5m + + pack $low.icon $low.info -side left -padx 2m + + frame $w.top.x.space1 -height 2m + + label $w.top.x.hfs -text "Initialize as HFS Volume..." -anchor w + + set dev [frame $w.top.x.dev] + label $dev.lbl -width 6 -text "Device:" -font $gThinFont -anchor w + entry $dev.name -width 15 -font $gTypeFont + label $dev.parlbl -text "Partition:" -font $gThinFont + entry $dev.par -width 2 -font $gTypeFont + pack $dev.lbl $dev.name $dev.parlbl $dev.par -side left -padx 0.5m + + set vol [frame $w.top.x.vol] + label $vol.lbl -width 6 -text "Name:" -font $gThinFont -anchor w + entry $vol.name -width 27 -font $gTypeFont + pack $vol.lbl $vol.name -side left -padx 0.5m + + frame $w.top.x.space2 -height 2m + + set mount [frame $w.top.x.mount] + label $mount.lbl -text "Open:" -font $gThinFont + radiobutton $mount.l -var gMountSide -value ".l" \ + -text "On Left" -font $gThinFont + radiobutton $mount.r -var gMountSide -value ".r" \ + -text "On Right" -font $gThinFont + pack $mount.lbl $mount.l $mount.r -side left + + pack $low $w.top.x.space1 $w.top.x.hfs $dev $vol \ + -side top -fill x -pady 0.5m + pack $w.top.x.space2 $mount -side top -pady 0.5m + + pack $w.top.x -expand 1 + + $w.top.x.low.info.cmd insert end $gLowFormatCmd + $w.top.x.dev.name insert end $gDefaultDevice + $w.top.x.vol.name insert end "Untitled" + + # focus $w.top.x.dev.name + showdialog $w +} + +proc format2 {w} { + global gAppCWD gLowFormat gLowFormatCmd gMountSide + + set gLowFormatCmd [$w.top.x.low.info.cmd get] + + set path [$w.top.x.dev.name get] + set partno [$w.top.x.dev.par get] + set vname [$w.top.x.vol.name get] + + if {[string length $partno] == 0} { + set partno 0 + } + + watch_cursor $w + destroy $w + + cd $gAppCWD + + if {$gLowFormat && [string length $gLowFormatCmd] > 0} { + exec /bin/sh -c $gLowFormatCmd /dev/null + } + + if {[string length $path] > 0} { + hfs format $path $partno $vname + mountdev $gMountSide $path + } +} + +proc help_about {} { + global gThinFont gSmallFont gTitleFont + + set w [dialog "About" "OK"] + + set f [frame $w.top.t] + + regsub {\(C\)} [hfs copyright] "\251" copyright + + frame $f.banner + label $f.banner.icon -bitmap macdaemon + label $f.banner.title -text "xhfs" -font $gTitleFont + pack $f.banner.icon $f.banner.title -side left -padx 2m + + frame $f.space -height 2m + label $f.vers -text [hfs version] -font $gThinFont + label $f.copy -text $copyright -font $gSmallFont + label $f.auth -text [hfs author] -font $gSmallFont + + pack $f.banner $f.space $f.vers $f.copy $f.auth -side top + pack $f -expand 1 + + showdialog $w +} + +proc help_license {} { + alert note "Software License" [fixtext [hfs license]] 190 +} + +proc help_options {} { + set text "\ + Show All Files: Some files are normally hidden. To unhide\n\ + invisible files on HFS volumes, or to unhide UNIX files which\n\ + begin with a period, select this option.\n\n\ + \ + Ask Before Delete: To disable verification before deleting files\n\ + and empty directories, deselect this option.\n\n\ + \ + Auto Mode Select: The transfer mode is normally selected\n\ + automatically. To use manual selection, deselect this option.\n\n\ + " + + alert note "Options" [fixtext $text] 150 +} + +proc help_modes {} { + set text "\ + MacBinary II: A popular format for binary file transfer. Both\n\ + forks of the Macintosh file are preserved. This is the\n\ + recommended mode for transferring arbitrary Macintosh files.\n\n\ + \ + BinHex: An alternative format for ASCII file transfer. Both\n\ + forks of the Macintosh file are preserved.\n\n\ + \ + Text: Performs end-of-line translation. Only the data fork of\n\ + the Macintosh file is copied.\n\n\ + \ + Raw Data: Performs no translation. Only the data fork of the\n\ + Macintosh file is copied.\n\n\ + \ + Automatic: A mode will be chosen automatically for each\n\ + file based on a set of predefined heuristics.\n\n\ + " + + alert note "Transfer Modes" [fixtext $text] +} + +proc hfs_automode {vol array} { + upvar $array item + + if {$item(kind) == "directory"} { + return auto + } elseif {[regexp {^TEXT|ttro$} $item(type)]} { + return text + } elseif {$item(rsize) == 0} { + return raw + } else { + return macb + } +} + +proc ufs_automode {vol array} { + upvar $array item + + set name $item(name) + + if {$item(kind) == "directory"} { + return auto + } elseif {[regexp -nocase {\.bin$} $name]} { + return macb + } elseif {[regexp -nocase {\.hqx$} $name]} { + return binh + } elseif {[regexp -nocase {\.(txt|c|h|html?|rtf)$} $name]} { + return text + } elseif {[regexp -nocase {\.(sit|sea|cpt|tar|gz|Z|gif|jpg)$} $name]} { + return raw + } else { + cd [$vol cwd] + if {([catch {exec file -L $name} type] == 0 || \ + [catch {exec file $name} type] == 0) && \ + [regexp {text|commands} $type]} { + return text + } + + return raw + } +} + +proc watch_cursor {{w "."} {default "left_ptr"}} { + $w config -cursor watch + if {[string compare $w "."] != 0} { + . config -cursor watch + } + update idletasks + + $w config -cursor $default + if {[string compare $w "."] != 0} { + . config -cursor left_ptr + } +} + +proc updatelist {side} { + global gShowAllFiles + global dir vol fstype + + set box $side.mid.lb + $box delete 0 end + + if {! [info exists vol($side)]} { + return + } + + if {$fstype($side) == "hfs"} { + set chartrans {nulltrans} ;# {hfs charlocal} + } else { + set chartrans {nulltrans} + } + + set list [$vol($side) dir] + set dir($side) {} + + set sepchar [$vol($side) sepchar] + + foreach ent $list { + array set item $ent + + set name [eval $chartrans [list $item(name)]] + + if {$item(kind) == "directory"} { + set name "$name$sepchar" + } + + if {$gShowAllFiles || [lsearch $item(flags) "invis"] < 0} { + $box insert end $name + lappend dir($side) $ent + } + } + + set m $side.mb.pop.m + $m delete 0 end + + set path [$vol($side) path] + set last 0 + + foreach elt $path { + set partial [abspath [$vol($side) sepchar] [lrange $path 0 $last]] + incr last + + $m insert 0 command -label $elt -command [list mchdir $side $partial] + } + + if {$fstype($side) == "hfs"} { + set what "Folder" + } else { + set what "Directory" + } + + foreach menuitem { + {separator} + {command -label "New $what..." -command [list mmkdir $side]} + } {eval $m insert end $menuitem} + + global cwd + set cwd($side) [lindex $path [expr [llength $path] - 1]] + + set avail [lindex [$vol($side) size] 1] + + $side.info.x.msg config -text "[size $avail] available" +} + +proc clearlists {} { + .l.mid.lb selection clear 0 end + .r.mid.lb selection clear 0 end + + .m.1.ops.view config -state disabled -text "View" + .m.1.ops.copy config -state disabled -text "Copy" + + .m.2.ops.info config -state disabled + .m.2.ops.rena config -state disabled + .m.2.ops.dele config -state disabled +} + +proc mchdir {side path} { + global vol + + watch_cursor + + $vol($side) chdir $path + + updatelist $side; clearlists +} + +proc open_volume {side} { + global gDefaultDevice gMountSide gTypeFont gThinFont + + set gMountSide $side + + set w [dialog "Open" "Cancel" "Open" 1] + $w.bot.ok config -command [list open2 $w] + + frame $w.top.x + + set lbl1 [frame $w.top.x.lbl1] + label $lbl1.path -text "Path to HFS Device" + pack $lbl1.path + + set lbl2 [frame $w.top.x.lbl2] + # label $lbl2.or -text "or" + # label $lbl2.icon -bitmap sm_floppy + label $lbl2.dev -text "or UNIX Directory:" + pack $lbl2.dev -side left -padx 0.5m + + entry $w.top.x.entry -width 25 -font $gTypeFont + + frame $w.top.x.space -height 2m + + set mount [frame $w.top.x.mount] + label $mount.lbl -text "Open:" -font $gThinFont + radiobutton $mount.l -var gMountSide -value ".l" \ + -text "On Left" -font $gThinFont + radiobutton $mount.r -var gMountSide -value ".r" \ + -text "On Right" -font $gThinFont + pack $mount.lbl $mount.l $mount.r -side left + + pack $lbl1 $lbl2 $w.top.x.entry $w.top.x.space $mount \ + -side top -pady 0.5m + + pack $w.top.x -expand 1 + + $w.top.x.entry insert end $gDefaultDevice + $w.top.x.entry selection range 0 end + + focus $w.top.x.entry + showdialog $w +} + +proc open2 {w} { + global gMountSide + global vol + + set path [$w.top.x.entry get] + + watch_cursor $w + destroy $w + + if {[string length $path] == 0} { + error "missing path" + } + + mountdev $gMountSide $path +} + +proc close_volume {side} { + global vol + + watch_cursor + + $vol($side) umount + unset vol($side) + + pack forget $side.mb.pop $side.info.x.icon $side.info.x.lock + $side.info.x.msg config -text "" + + $side.sub.open config -text "Open..." -command [list open_volume $side] + $side.sub.info config -state disabled + + updatelist $side; clearlists +} + +proc ctime {secs} { + return [clock format $secs -format "%c"] +} + +proc volume_info {side} { + global gThinFont + global vol dev fstype media + + set v $vol($side) + + if {$side == ".l"} { + set eside "Left" + } else { + set eside "Right" + } + + set w [window "$eside Info" "Info"] + + set locked [$v islocked] + set icon $media($side) + set vname [$v vname] + + if {$fstype($side) == "ufs"} { + set ishfs 0 + set where [file dirname $vname] + set vname [file tail $vname] + set kind "UNIX Directory" + } else { + set ishfs 1 + set kind "HFS Volume" + set where $dev($side) + set crdate [$v crdate] + } + + wm iconbitmap $w $icon + wm iconmask $w ${icon}_mask + + frame $w.x + pack $w.x -expand 1 -fill both -ipadx 4m -ipady 4m + + set f [frame $w.x.f] + + frame $f.name + frame $f.name.space -width 5 + label $f.name.icon -bitmap $icon + label $f.name.text -text $vname -font $gThinFont + pack $f.name.space $f.name.icon $f.name.text -side left -padx 2m + + if {$locked} { + label $f.name.lock -bitmap padlock + pack $f.name.lock -side left + } + + frame $f.space1 -height 4m + + frame $f.kind + label $f.kind.lbl -width 9 -text "Kind: " -anchor e + label $f.kind.val -text $kind -font $gThinFont + pack $f.kind.lbl $f.kind.val -side left + + frame $f.size + label $f.size.lbl -width 9 -text "Size: " -anchor e + label $f.size.val -text [size [lindex [$v size] 0]] -font $gThinFont + pack $f.size.lbl $f.size.val -side left + + frame $f.space2 -height 4m + + frame $f.where + label $f.where.lbl -width 9 -text "Where: " -anchor e + label $f.where.val -text $where -font $gThinFont + pack $f.where.lbl $f.where.val -side left + + pack $f.name $f.space1 $f.kind $f.size $f.space2 $f.where -side top -fill x + + if {$ishfs} { + frame $f.space3 -height 4m + + frame $f.cr + label $f.cr.lbl -width 9 -text "Created: " -anchor e + label $f.cr.val -text [ctime $crdate] -font $gThinFont + pack $f.cr.lbl $f.cr.val -side left + + pack $f.space3 $f.cr -side top -fill x + } + + frame $f.md + label $f.md.lbl -width 9 -text "Modified: " -anchor e + label $f.md.val -text [ctime [$v mddate]] -font $gThinFont + pack $f.md.lbl $f.md.val -side left + + pack $f.md -side top -fill x + + pack $f -expand 1 + + showwindow $w +} + +proc mmkdir {side} { + global gTypeFont + global fstype + + if {$fstype($side) == "hfs"} { + set what "folder" + set title "New Folder" + } else { + set what "directory" + set title "New Directory" + } + + set w [dialog $title "Cancel" "Create" 1] + $w.bot.ok config -command [list mmkdir2 $w $side $what] + + frame $w.top.x + + frame $w.top.x.lbl + label $w.top.x.lbl.icon -bitmap sm_folder + label $w.top.x.lbl.text -text "Name for new $what:" + pack $w.top.x.lbl.icon $w.top.x.lbl.text -side left -padx 0.5m + + entry $w.top.x.entry -width 25 -font $gTypeFont -exportsel 0 + + pack $w.top.x.lbl $w.top.x.entry -side top -pady 0.5m + pack $w.top.x -expand 1 + + $w.top.x.entry insert end "untitled $what" + $w.top.x.entry selection range 0 end + + focus $w.top.x.entry + showdialog $w +} + +proc mmkdir2 {w side what} { + global vol + + set name [$w.top.x.entry get] + + watch_cursor $w + destroy $w + + if {[string length $name] == 0} { + error "cannot create $what with empty name" + } + + $vol($side) mkdir $name + $vol($side) chdir $name + + updatelist $side; clearlists +} + +proc plural {count single plural} { + if {$count == 1} { + return $single + } else { + return $plural + } +} + +proc nulltrans {str} { + return $str +} + +proc show_file {side fname} { + global gTypeFont + global vol fstype + + watch_cursor + + if {$fstype($side) == "hfs"} { + set charset macroman + } else { + set charset latin1 + } + + # Make sure we can open the file before building the interface + set fh [$vol($side) open $fname] + + set path [abspath [$vol($side) sepchar] \ + [concat [$vol($side) path] [list $fname]] 1] + set w [window $path $fname] + + frame $w.x + pack $w.x -expand 1 -fill both -padx 1m -pady 1m + + set f [frame $w.x.f] + + text $f.text -yscroll [list $f.scroll set] \ + -height 30 -width 80 -wrap word -font $gTypeFont + scrollbar $f.scroll -orient vert -command [list $f.text yview] + pack $f.scroll -side right -fill y + pack $f.text -fill both -expand 1 + + pack $f -fill both -expand 1 + + watch_cursor $f.text xterm + after idle "[list $f.text config -state disabled]; [list showwindow $w]" + + while {1} { + set buf [$fh read 512] + if {[string length $buf] == 0} { + $fh close + break + } + + regsub -all "\r\n?" $buf "\n" buf + + $f.text insert end [hfs chartrans $charset latin1 $buf] + } +} + +proc commas {number} { + while {[regexp \ + {([^,]+)(([0-9][0-9][0-9])(,[0-9][0-9][0-9])*([^0-9].*|$))} \ + $number ignore pre rest]} { + set number "$pre,$rest" + } + + return $number +} + +proc size {bytes} { + if {$bytes == -1} { + return "unknown K" + } + + set k 1024 + set mb [expr 1024 * $k] + + if {$bytes > $mb} { + set q $mb + set u "M" + } else { + set q $k + set u "K" + } + + set amount [commas [format "%.1f" [expr ${bytes}.0 / $q]]] + + regsub {\.0$} $amount "" amount + regsub {^0$} $amount "zero " amount + + return "$amount$u" +} + +proc show_info {vol list} { + global gTypeFont gThinFont + + array set info $list + + set isdir [expr [string compare $info(kind) "directory"] == 0] + set ishfs [info exists info(crdate)] + + if {$ishfs} { + set chartrans {nulltrans} ;# {hfs charlocal} + } else { + set chartrans {nulltrans} + } + + set name [eval $chartrans [list $info(name)]] + set w [window "$name Info" $name] + + set icon "document" + + if {$isdir} { + set icon "folder" + + if {$ishfs} { + set info(kind) "folder" + } + } else { + if {$ishfs} { + if {[string compare $info(type) "APPL"] == 0} { + set icon "application" + set info(kind) "application" + } else { + set info(kind) "document" + } + } else { + cd [$vol cwd] + if {[file executable $info(name)]} { + set icon "application" + } + } + } + + wm iconbitmap $w $icon + wm iconmask $w ${icon}_mask + + if {$ishfs} { + set where "[join [$vol path] ":"]:" + } else { + set where [$vol cwd] + + cd $where + if {[catch {exec file $info(name)} type] == 0} { + if {[string first "$info(name):" $type] == 0} { + set type [string range $type \ + [expr [string length $info(name)] + 1] end] + } + + regsub "^\[ \t]*" $type "" info(kind) + } + } + + frame $w.x + pack $w.x -expand 1 -fill both -ipadx 4m -ipady 4m + + set f [frame $w.x.f] + + frame $f.name + frame $f.name.space -width 5 + label $f.name.icon -bitmap $icon + label $f.name.text -text $name -font $gThinFont + pack $f.name.space $f.name.icon $f.name.text -side left -padx 2m + + if {[lsearch $info(flags) "locked"] >= 0} { + label $f.name.lock -bitmap padlock + pack $f.name.lock -side left + } + + frame $f.space1 -height 4m + + frame $f.kind + label $f.kind.lbl -width 9 -text "Kind: " -anchor e + label $f.kind.val -text $info(kind) -font $gThinFont + pack $f.kind.lbl $f.kind.val -side left + + set label "Size: " + if {$isdir} { + set label "Contains: " + set size $info(size) + set what [plural $size item items] + } elseif {$ishfs} { + set size [list $info(rsize) $info(dsize)] + set what [list \ + [plural $info(rsize) byte bytes] \ + [plural $info(dsize) byte bytes]] + } else { + set size $info(size) + set what [plural $size byte bytes] + } + + frame $f.size + + if {[llength $size] == 1} { + label $f.size.lbl -width 9 -text $label -anchor e + label $f.size.val -text "[commas $size] $what" -font $gThinFont + pack $f.size.lbl $f.size.val -side left + } else { + set rsize [lindex $size 0] + set rwhat [lindex $what 0] + set dsize [lindex $size 1] + set dwhat [lindex $what 1] + + frame $f.size.r + label $f.size.r.lbl -width 9 -text "Resource: " -anchor e + label $f.size.r.val -text "[commas $rsize] $rwhat" -font $gThinFont + pack $f.size.r.lbl $f.size.r.val -side left + + frame $f.size.d + label $f.size.d.lbl -width 9 -text "Data: " -anchor e + label $f.size.d.val -text "[commas $dsize] $dwhat" -font $gThinFont + pack $f.size.d.lbl $f.size.d.val -side left + + pack $f.size.r $f.size.d -side top -fill x + } + + frame $f.space2 -height 4m + + frame $f.where + label $f.where.lbl -width 9 -text "Where: " -anchor e + label $f.where.val -text $where -font $gThinFont + pack $f.where.lbl $f.where.val -side left + + pack $f.name $f.space1 $f.kind $f.size $f.space2 $f.where -side top -fill x + + if {$ishfs && ! $isdir} { + frame $f.space3 -height 4m + + frame $f.type + label $f.type.lbl -width 9 -text "Type: " -anchor e + label $f.type.val -text $info(type) -font $gTypeFont + pack $f.type.lbl $f.type.val -side left + + frame $f.crea + label $f.crea.lbl -width 9 -text "Creator: " -anchor e + label $f.crea.val -text $info(creator) -font $gTypeFont + pack $f.crea.lbl $f.crea.val -side left + + pack $f.space3 $f.type $f.crea -side top -fill x + } + + if {$ishfs} { + frame $f.space4 -height 4m + + frame $f.cr + label $f.cr.lbl -width 9 -text "Created: " -anchor e + label $f.cr.val -text [ctime $info(crdate)] -font $gThinFont + pack $f.cr.lbl $f.cr.val -side left + + pack $f.space4 $f.cr -side top -fill x + } + + frame $f.md + label $f.md.lbl -width 9 -text "Modified: " -anchor e + label $f.md.val -text [ctime $info(mddate)] -font $gThinFont + pack $f.md.lbl $f.md.val -side left + + pack $f.md -side top -fill x + + pack $f -expand 1 + + showwindow $w +} + +proc abspath {sepchar path {asfile 0}} { + switch $sepchar { + ":" { + if {$asfile} { + return [join $path ":"] + } else { + return "[join $path ":"]:" + } + } + + "/" { + if {[string compare [lindex $path 0] "/"] == 0} { + set path [lreplace $path 0 0 ""] + } + + if {[llength $path] == 1 && \ + [string length [lindex $path 0]] == 0} { + return "/" + } else { + return [join $path "/"] + } + } + } +} + +proc ufs_handle {dir} { + set handle "ufsvol[unique]" + + global ufsdir + + cd $dir + set ufsdir($handle) [pwd] + + proc $handle {verb args} { + set handle [lindex [info level [info level]] 0] + global gAppCWD gDfCmd + global ufsdir + + set cwd $ufsdir($handle) + + switch -glob $verb { + vname { + return $cwd + } + + umount { + cd $gAppCWD + + unset ufsdir($handle) + rename $handle "" + } + + path { + set path [split $cwd "/"] + + if {[string compare [lindex $path 0] ""] == 0} { + set path [lreplace $path 0 0 "/"] + } + + if {[llength $path] == 2 && \ + [string length [lindex $path 1]] == 0} { + set path [lreplace $path 1 1] + } + + return $path + } + + dir { + cd $cwd + + set list {} + set files [lsort [glob .* *]] + + foreach name $files { + if {[string compare $name "."] == 0 || \ + [string compare $name ".."] == 0} { + continue + } + + if {[catch {file stat $name stat}]} { + continue + } + + set kind [file type $name] + set mddate $stat(mtime) + + if {[file isdirectory $name]} { + set kind "directory" + set size "?" + catch { + set size [expr [llength \ + [glob $name/.* $name/*]] - 2] + } + } elseif {$kind == "file"} { + set size $stat(size) + } else { + continue + } + + set flags {} + + if {! [file writable $name]} { + lappend flags "locked" + } + + if {[regexp {^\.} $name]} { + lappend flags "invis" + } + + lappend list [list \ + name $name \ + kind $kind \ + size $size \ + flags $flags \ + mddate $mddate] + } + + return $list + } + + chdir { + set path [lindex $args 0] + + if {! [regexp {^/} $path]} { + cd $cwd + } + + cd $path + set ufsdir($handle) [pwd] + } + + cwd { + return $cwd + } + + sepchar { + return "/" + } + + mkdir { + set path [lindex $args 0] + + cd $cwd + exec mkdir $path + } + + rmdir { + set path [lindex $args 0] + + cd $cwd + exec rmdir $path + } + + open { + set path [lindex $args 0] + + cd $cwd + set fh [open $path] + + proc $fh {verb args} { + set fh [lindex [info level [info level]] 0] + + switch -glob $verb { + read { + set len [lindex $args 0] + + return [read $fh $len] + } + + close { + close $fh + rename $fh "" + } + + * { + error "unknown call to $fh $verb $args" + } + } + } + + return $fh + } + + copy { + set path [lindex $args 0] + set dest [lindex $args 1] + + cd $cwd + exec cp -f $path $dest + } + + rename { + set path [lindex $args 0] + set dest [lindex $args 1] + + cd $cwd + exec mv -f $path $dest + } + + delete { + set path [lindex $args 0] + + cd $cwd + exec rm -f $path + } + + size { + if {[catch {eval exec [join $gDfCmd " "] [list $cwd]} info]} { + return [list -1 -1] + } + + set s "\[ \t]+" + set d "\[0-9]+" + + if {[catch { + regexp "(.*)\n(\[^\n]*)\$" $info ignore head data + regexp "${s}($d)$s$d${s}($d)" $data ignore size avail + }]} { + return [list -1 -1] + } + + set block 512 + if {[regexp -nocase {kbyte|1024} $head]} { + set block 1024 + } + + return [list [expr $size * $block] [expr $avail * $block]] + } + + mddate { + return [file mtime $cwd] + } + + islocked { + return 0 + } + + * { + error "unknown call to $handle $verb $args" + } + } + } + + return $handle +} + +proc ufs {verb args} { + switch $verb { + mount { + set dir [lindex $args 0] + + return [ufs_handle $dir] + } + + * { + error "bad arg 1 to ufs" + } + } +} + +proc mountdev {side device} { + global gAppCWD gXMode + global vol dev fstype media + + if {[info exists vol($side)]} { + $vol($side) umount + } + + cd $gAppCWD + + if {! [file exists $device]} { + error "can't open $device (no such file or directory)" + } + + if {[file isdirectory $device]} { + set type ufs + } else { + set type hfs + } + + set v [$type mount $device] + set fstype($side) $type + set locked [$v islocked] + + if {$type == "ufs"} { + set icon "folder" + } else { + set size [lindex [$v size] 0] + + if {$size <= [expr 1440 * 1024]} { + set icon "floppy" + } elseif {$locked && \ + $size >= [expr 100 * 1024 * 1024] && \ + $size <= [expr 800 * 1024 * 1024]} { + set icon "cdrom" + } else { + set icon "harddisk" + } + } + + $side.info.x.icon config -bitmap sm_$icon + pack $side.info.x.icon -before $side.info.x.msg -side left -padx 0.5m + + if {$locked} { + pack $side.info.x.lock -side left -padx 0.5m + } else { + pack forget $side.info.x.lock + } + + pack $side.mb.pop + + $side.sub.open config -text "Close" -command [list close_volume $side] + $side.sub.info config -state normal + + set vol($side) $v + set dev($side) $device + set media($side) $icon + + updatelist $side; clearlists + + if {$fstype(.l) == $fstype(.r)} { + set state disabled + set gXMode auto + } else { + set state normal + } + + foreach mode {auto macb binh text raw} { + .m.3.mode.$mode config -state $state + } +} + +proc flushtask {} { + global gFlushInterval + + after $gFlushInterval flushtask + + watch_cursor + hfs flushall +} + +############################################################################### + +# Apparently some versions of Tk don't grok this +if {$argc > 0 && [string compare [lindex $argv 0] "--"] == 0} { + incr argc -1 + set argv [lreplace $argv 0 0] +} + +if {[string compare [lindex $argv 0] "--license"] == 0} { + puts -nonewline stdout "\n[hfs license]" + exit +} + +if {[string compare [lindex $argv 0] "--version"] == 0} { + puts stdout "[hfs version] - [hfs copyright]" + puts stdout "`$argv0 --license' for licensing information." + exit +} + +############################################################################### + +# Build the user interface + +proc makelist {w click other} { + global gSmallFont + + frame $w + + frame $w.mb -height 30 + menubutton $w.mb.pop -textvariable cwd($w) \ + -indicatoron 1 -menu $w.mb.pop.m \ + -relief raised -bd 2 -padx 4p -pady 4p \ + -highlightthickness 2 -anchor c + menu $w.mb.pop.m -tearoff 0 + + frame $w.info -height 16 + pack propagate $w.info 0 + + frame $w.info.x + pack $w.info.x -expand 1 + + label $w.info.x.icon + label $w.info.x.msg -font $gSmallFont -text "" + label $w.info.x.lock -bitmap padlock + + pack $w.info.x.msg -side left -padx 0.5m + + frame $w.mid -bd 0 + + listbox $w.mid.lb -xscroll "$w.bot.sb set" -yscroll "$w.mid.sb set" \ + -exportselection 0 -height 16 -width 25 -selectmode extended + scrollbar $w.mid.sb -orient vert -command [list $w.mid.lb yview] + + bind $w.mid.lb [list $other selection clear 0 end] + bind $w.mid.lb [list $other selection clear 0 end] + bind $w.mid.lb [list wclick $w $click] + bind $w.mid.lb [list wclick $w $click] + bind $w.mid.lb ".m.1.ops.view invoke" + bind $w.mid.lb ".m.1.ops.view invoke" + + pack $w.mid.sb -side right -fill y + pack $w.mid.lb -side right -fill both -expand 1 + + frame $w.bot + + scrollbar $w.bot.sb -orient horiz -command [list $w.mid.lb xview] + frame $w.bot.c -height 24 -width 24 + + # label $w.bot.c.icon + # pack $w.bot.c.icon -expand 1 + # pack propagate $w.bot.c 0 + + pack $w.bot.c -side right + pack $w.bot.sb -side bottom -fill x + + frame $w.sub -relief sunken -bd 1 + + frame $w.sub.space1 -width 1m + button $w.sub.open -width 8 -text "Open..." \ + -command [list open_volume $w] + button $w.sub.info -width 8 -text "Volume Info" -state disabled \ + -command [list volume_info $w] + + frame $w.sub.space2 -width 1m + pack $w.sub.space1 -side left + pack $w.sub.open $w.sub.info \ + -side left -pady 1m -padx 0.5m -fill x -expand 1 + pack $w.sub.space2 -side left + + frame $w.space -height 2m + + pack $w.space -side bottom + pack $w.mb -side top + pack $w.sub -side bottom -fill x -pady 1m -ipadx 2m + pack $w.info $w.bot -side bottom -fill x + pack $w.mid -side top -fill both -expand 1 +} + +proc wclick {side copytext} { + set box $side.mid.lb + set sel [$box curselection] + + global gXMode gAutoModeSelect + global vol dir fstype + + if {$side == ".l"} { + set other ".r" + } else { + set other ".l" + } + + set cstate normal + if {! [info exists vol($other)]} { + set cstate disabled + } + + if {[llength $sel] == 0} { + .m.1.ops.view config -state disabled -text "View" + .m.1.ops.copy config -state disabled -text "Copy" + + .m.2.ops.info config -state disabled + .m.2.ops.rena config -state disabled + .m.2.ops.dele config -state disabled + } elseif {[llength $sel] == 1} { + array set item [lindex $dir($side) [lindex $sel 0]] + + switch -glob $item(kind) { + directory { + set viewtext "Open" + set vstate normal + } + + file { + set viewtext "View" + set mode [$fstype($side)_automode $vol($side) item] + + if {$mode == "text" || $mode == "binh"} { + set vstate normal + } else { + set vstate disabled + } + } + } + + .m.1.ops.view config -state $vstate -text $viewtext \ + -command [list do_open $side] + .m.1.ops.copy config -state $cstate -text $copytext \ + -command [list do_copy $side $other] + + .m.2.ops.info config -state normal -command [list do_info $side] + .m.2.ops.rena config -state normal -command [list do_rename $side] + .m.2.ops.dele config -state normal -command [list do_delete $side] + + if {$gAutoModeSelect && \ + [.m.3.mode.auto cget -state] != "disabled"} { + if {[info exists mode]} { + set gXMode $mode + } else { + set gXMode [$fstype($side)_automode $vol($side) item] + } + } + } else { + .m.1.ops.view config -state disabled -text "View" + .m.1.ops.copy config -state $cstate -text $copytext \ + -command [list do_copy $side $other] + + .m.2.ops.info config -state normal -command [list do_info $side] + .m.2.ops.rena config -state disabled + .m.2.ops.dele config -state normal -command [list do_delete $side] + + if {$gAutoModeSelect && \ + [.m.3.mode.auto cget -state] != "disabled"} { + foreach ind $sel { + array set item [lindex $dir($side) $ind] + set auto [$fstype($side)_automode $vol($side) item] + + if {[info exists mode]} { + if {$mode != $auto} { + set mode auto + break + } + } else { + set mode $auto + } + } + + set gXMode $mode + } + } +} + + +# Middle controls + +frame .m + +frame .m.1 +frame .m.1.ops -relief sunken -bd 1 +frame .m.1.ops.space1 -height 0.5m +button .m.1.ops.view -state disabled -text "View" +button .m.1.ops.copy -state disabled -text "Copy" +frame .m.1.ops.space2 -height 0.5m + +pack \ + .m.1.ops.space1 \ + .m.1.ops.view .m.1.ops.copy \ + .m.1.ops.space2 \ + -side top -fill x -padx 1m -pady 0.5m +pack .m.1.ops -side top -fill x + +frame .m.2 +frame .m.2.ops -relief sunken -bd 1 +frame .m.2.ops.space1 -height 0.5m +button .m.2.ops.info -state disabled -text "Get Info" +button .m.2.ops.rena -state disabled -text "Rename..." +button .m.2.ops.dele -state disabled -text "Delete" +frame .m.2.ops.space2 -height 0.5m + +pack \ + .m.2.ops.space1 \ + .m.2.ops.info .m.2.ops.rena .m.2.ops.dele \ + .m.2.ops.space2 \ + -side top -fill x -padx 1m -pady 0.5m +pack .m.2.ops -side top -fill x + +frame .m.3 +label .m.3.l -text "Mode" +frame .m.3.mode -relief groove -bd 2 + +frame .m.3.mode.space1 -height 1m +radiobutton .m.3.mode.macb -font $gThinFont -text "MacBinary II" \ + -var gXMode -value macb -anchor w +radiobutton .m.3.mode.binh -font $gThinFont -text "BinHex" \ + -var gXMode -value binh -anchor w +radiobutton .m.3.mode.text -font $gThinFont -text "Text" \ + -var gXMode -value text -anchor w +radiobutton .m.3.mode.raw -font $gThinFont -text "Raw Data" \ + -var gXMode -value raw -anchor w +radiobutton .m.3.mode.auto -font $gThinFont -text "Automatic" \ + -var gXMode -value auto -anchor w +frame .m.3.mode.space2 -height 1m + +set gXMode auto + +pack \ + .m.3.mode.space1 \ + .m.3.mode.macb \ + .m.3.mode.binh \ + .m.3.mode.text \ + .m.3.mode.raw \ + .m.3.mode.auto \ + .m.3.mode.space2 \ + -side top -fill x -padx 1m +pack .m.3.l .m.3.mode -side top -fill x + +pack .m.1 .m.2 .m.3 -side top -fill x -padx 2m -pady 2m + + +# Menu bar + +frame .mb -relief raised -bd 2 +frame .mbpad -height 3m +menubutton .mb.file -text "File" -menu .mb.file.m +menubutton .mb.opts -text "Options" -menu .mb.opts.m +menubutton .mb.help -bitmap help -menu .mb.help.m + +pack .mb.file .mb.opts -side left +pack .mb.help -side right -fill y -ipadx 2m + +menu .mb.file.m -tearoff 0 +foreach menuitem { + {command -label "Open..." -command {open_volume $gMountSide}} + {separator} + {command -label "Format Disk..." -command "format_disk"} + {command -label "Eject Disk" -command "hfs flushall; exec eject"} + {separator} + {command -label "Quit" -command "destroy ."} +} {eval .mb.file.m add $menuitem} + +menu .mb.opts.m -tearoff 0 +foreach menuitem { + {checkbutton -label "Show All Files" -variable gShowAllFiles \ + -command "watch_cursor; updatelist .l; updatelist .r; clearlists"} + {separator} + {checkbutton -label "Ask Before Delete" -variable gAskDelete} + {checkbutton -label "Auto Mode Select" -variable gAutoModeSelect} +} {eval .mb.opts.m add $menuitem} + +set gAutoModeSelect 1 +set gAskDelete 1 + +menu .mb.help.m -tearoff 0 +foreach menuitem { + {command -label "About" -command "help_about"} + {command -label "Software License" -command "help_license"} + {separator} + {command -label "Options" -command "help_options"} + {command -label "Transfer Modes" -command "help_modes"} +} {eval .mb.help.m add $menuitem} + + +# Put it together + +makelist .l ">> Copy >>" .r.mid.lb +makelist .r "<< Copy <<" .l.mid.lb + +rename makelist "" + +pack .mb .mbpad -side top -fill x +pack .l -side left -fill both -expand 1 -padx 3m +pack .r -side right -fill both -expand 1 -padx 3m +pack .m -side left + +wm title . "Macintosh HFS Volume Browser" + +wm iconbitmap . macdaemon +wm iconmask . macdaemon_mask +wm iconname . "HFS Browser" + +. config -cursor left_ptr + +############################################################################### + +tk appname xhfs + +set gAppCWD [pwd] + +set fstype(.l) none +set fstype(.r) none + +if {$argc > 2} { + puts stderr "Usage: $argv0 \[left-vol \[right-vol]]" + exit 1 +} + +if {$argc > 0} { + mountdev .l [lindex $argv 0] + + if {$argc > 1} { + mountdev .r [lindex $argv 1] + } +} + +if {$argc < 2} { + mountdev .r . +} + +after $gFlushInterval flushtask