commit 60d1b2c29363f464f506e1cf7ca492812fcaed44 Author: Pablo Lezaeta Reyes Date: Wed Sep 27 00:10:03 2017 -0300 Initial import from https://www.mars.org/home/rob/proj/hfs/ Signed-off-by: Pablo Lezaeta Reyes 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