db/schema.rb
vendor/plugins/*
!vendor/plugins/.gitkeep
+!vendor/plugins/acts_as_paranoid
+!vendor/plugins/double_submit_protection
+!vendor/plugins/image_submit_tag_ext
+!vendor/plugins/mbmail
+!vendor/plugins/rails-active-form
+!vendor/plugins/ssl_requirement
var/run/*
!var/run/.gitkeep
config/database.yml
--- /dev/null
+--- !ruby/object:Gem::Specification
+name: lhalib
+version: !ruby/object:Gem::Version
+ version: 0.8.1
+ prerelease:
+ segments:
+ - 0
+ - 8
+ - 1
+platform: ruby
+authors:
+- arton
+autorequire:
+bindir: bin
+cert_chain:
+date: 2006-09-18 00:00:00.000000000 Z
+dependencies: []
+description: LhaLib is an utility that unpack the LHa archived file.
+email: artonx@gmail.com
+executables: []
+extensions:
+- ext/extconf.rb
+extra_rdoc_files: []
+files:
+- ext/extconf.rb
+homepage: http://arton.no-ip.info/collabo/backyard/?LhaLib
+licenses: []
+post_install_message:
+rdoc_options: []
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ! '>='
+ - !ruby/object:Gem::Version
+ version: 1.8.2
+required_rubygems_version: !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ! '>='
+ - !ruby/object:Gem::Version
+ version: '0'
+requirements:
+- none
+rubyforge_project:
+rubygems_version: 1.8.23
+signing_key:
+specification_version: 1
+summary: Ruby LHa Lib
+test_files: []
+has_rdoc:
+
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+\f
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null
+Mon Sep 18 17:56:00 2006 arton\r
+ *lha.h\r
+ change extern macro for some arrays. because of OS X's linker reports confliction.\r
+Sun Sep 17 23:47:00 2006 arton\r
+ initial reelase\r
+ *bitio.c\r
+ *crcio.c\r
+ *dhuf.c\r
+ *extract.c\r
+ *header.c\r
+ *huf.c\r
+ *larc.c\r
+ *lhext.c\r
+ *maketbl.c\r
+ *shuf.c\r
+ *slide.c\r
+ *util.c\r
+ copy and modifying from lha-1.14i-ac20050924\r
+ *lha.h\r
+ *lharc.c\r
+ reconfig for lhalib\r
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* bitio.c -- bit stream */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Separated from crcio.c 2002.10.26 Koji Arai */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+static unsigned char subbitbuf, bitcount;
+
+void
+fillbuf(unsigned char n) /* Shift bitbuf n bits left, read n bits */
+{
+ while (n > bitcount) {
+ n -= bitcount;
+ bitbuf = (bitbuf << bitcount) + (subbitbuf >> (CHAR_BIT - bitcount));
+ if (compsize != 0) {
+ compsize--;
+ subbitbuf = (unsigned char) getc(infile);
+ }
+ else
+ subbitbuf = 0;
+ bitcount = CHAR_BIT;
+ }
+ bitcount -= n;
+ bitbuf = (bitbuf << n) + (subbitbuf >> (CHAR_BIT - n));
+ subbitbuf <<= n;
+}
+
+unsigned short
+getbits(unsigned char n)
+{
+ unsigned short x;
+
+ x = bitbuf >> (2 * CHAR_BIT - n);
+ fillbuf(n);
+ return x;
+}
+
+void
+putcode(unsigned char n, unsigned short x) /* Write leftmost n bits of x */
+{
+ while (n >= bitcount) {
+ n -= bitcount;
+ subbitbuf += x >> (USHRT_BIT - bitcount);
+ x <<= bitcount;
+ if (compsize < origsize) {
+ if (fwrite(&subbitbuf, 1, 1, outfile) == 0) {
+ fatal_error("Write error in bitio.c(putcode)");
+ }
+ compsize++;
+ }
+ else
+ unpackable = 1;
+ subbitbuf = 0;
+ bitcount = CHAR_BIT;
+ }
+ subbitbuf += x >> (USHRT_BIT - bitcount);
+ bitcount -= n;
+}
+
+void
+putbits(unsigned char n, unsigned short x) /* Write rightmost n bits of x */
+{
+ x <<= USHRT_BIT - n;
+ putcode(n, x);
+}
+
+void
+init_getbits( /* void */ )
+{
+ bitbuf = 0;
+ subbitbuf = 0;
+ bitcount = 0;
+ fillbuf(2 * CHAR_BIT);
+}
+
+void
+init_putbits( /* void */ )
+{
+ bitcount = CHAR_BIT;
+ subbitbuf = 0;
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* crcio.c -- crc input / output */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+/* ------------------------------------------------------------------------ */
+#ifdef EUC
+static int putc_euc_cache;
+#endif
+static int getc_euc_cache;
+
+/* ------------------------------------------------------------------------ */
+void
+make_crctable( /* void */ )
+{
+ unsigned int i, j, r;
+
+ for (i = 0; i <= UCHAR_MAX; i++) {
+ r = i;
+ for (j = 0; j < CHAR_BIT; j++)
+ if (r & 1)
+ r = (r >> 1) ^ CRCPOLY;
+ else
+ r >>= 1;
+ crctable[i] = r;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+unsigned int
+calccrc(crc, p, n)
+ unsigned int crc;
+ unsigned char *p;
+ unsigned int n;
+{
+ while (n-- > 0)
+ crc = UPDATE_CRC(crc, *p++);
+ return crc;
+}
+
+/* ------------------------------------------------------------------------ */
+int
+fread_crc(crcp, p, n, fp)
+ unsigned int *crcp;
+ unsigned char *p;
+ int n;
+ FILE *fp;
+{
+ if (text_mode)
+ n = fread_txt(p, n, fp);
+ else
+ n = fread(p, 1, n, fp);
+
+ *crcp = calccrc(*crcp, p, n);
+#ifdef NEED_INCREMENTAL_INDICATOR
+ put_indicator(n);
+#endif
+ return n;
+}
+
+/* ------------------------------------------------------------------------ */
+void
+fwrite_crc(crcp, p, n, fp)
+ unsigned int *crcp;
+ unsigned char *p;
+ int n;
+ FILE *fp;
+{
+ *crcp = calccrc(*crcp, p, n);
+#ifdef NEED_INCREMENTAL_INDICATOR
+ put_indicator(n);
+#endif
+ if (fp) {
+ if (text_mode) {
+ if (fwrite_txt(p, n, fp))
+ fatal_error("File write error");
+ }
+ else {
+ if (fwrite(p, 1, n, fp) < n)
+ fatal_error("File write error");
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+void
+init_code_cache( /* void */ )
+{ /* called from copyfile() in util.c */
+#ifdef EUC
+ putc_euc_cache = EOF;
+#endif
+ getc_euc_cache = EOF;
+}
+
+/* ------------------------------------------------------------------------ */
+#ifdef EUC
+int
+putc_euc(c, fd)
+ int c;
+ FILE *fd;
+{
+ int d;
+
+ if (putc_euc_cache == EOF) {
+ if (!euc_mode || c < 0x81 || c > 0xFC) {
+ return putc(c, fd);
+ }
+ if (c >= 0xA0 && c < 0xE0) {
+ if (putc(0x8E, fd) == EOF) return EOF; /* single shift */
+ return putc(c, fd);
+ }
+ putc_euc_cache = c; /* save first byte */
+ return c;
+ }
+ d = putc_euc_cache;
+ putc_euc_cache = EOF;
+ if (d >= 0xA0)
+ d -= 0xE0 - 0xA0;
+ if (c > 0x9E) {
+ c = c - 0x9F + 0x21;
+ d = (d - 0x81) * 2 + 0x22;
+ }
+ else {
+ if (c > 0x7E)
+ c--;
+ c -= 0x1F;
+ d = (d - 0x81) * 2 + 0x21;
+ }
+ if (putc(0x80 | d, fd) == EOF) return EOF;
+ return putc(0x80 | c, fd);
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+int
+fwrite_txt(p, n, fp)
+ unsigned char *p;
+ int n;
+ FILE *fp;
+{
+ while (--n >= 0) {
+ if (*p != '\015' && *p != '\032') {
+#ifdef EUC
+ if (putc_euc(*p, fp) == EOF)
+ break;
+#else
+ if (putc(*p, fp) == EOF)
+ break;
+#endif
+ }
+ p++;
+ }
+ return (ferror(fp));
+}
+
+/* ------------------------------------------------------------------------ */
+int
+fread_txt(p, n, fp)
+ unsigned char *p;
+ int n;
+ FILE *fp;
+{
+ int c;
+ int cnt = 0;
+
+ while (cnt < n) {
+ if (getc_euc_cache != EOF) {
+ c = getc_euc_cache;
+ getc_euc_cache = EOF;
+ }
+ else {
+ if ((c = fgetc(fp)) == EOF)
+ break;
+ if (c == '\n') {
+ getc_euc_cache = c;
+ ++origsize;
+ c = '\r';
+ }
+#ifdef EUC
+ else if (euc_mode && (c == 0x8E || (0xA0 < c && c < 0xFF))) {
+ int d = fgetc(fp);
+ if (d == EOF) {
+ *p++ = c;
+ cnt++;
+ break;
+ }
+ if (c == 0x8E) { /* single shift (KANA) */
+ if ((0x20 < d && d < 0x7F) || (0xA0 < d && d < 0xFF))
+ c = d | 0x80;
+ else
+ getc_euc_cache = d;
+ }
+ else {
+ if (0xA0 < d && d < 0xFF) { /* if GR */
+ c &= 0x7F; /* convert to MS-kanji */
+ d &= 0x7F;
+ if (!(c & 1)) {
+ c--;
+ d += 0x7F - 0x21;
+ }
+ if ((d += 0x40 - 0x21) > 0x7E)
+ d++;
+ if ((c = (c >> 1) + 0x71) >= 0xA0)
+ c += 0xE0 - 0xA0;
+ }
+ getc_euc_cache = d;
+ }
+ }
+#endif
+ }
+ *p++ = c;
+ cnt++;
+ }
+ return cnt;
+}
--- /dev/null
+lhalib.o : lhalib.c $(hdrdir)/ruby.h $(hdrdir)/config.h $(hdrdir)/defines.h lha.h lha_macro.h
+dhuf.o : dhuf.c lha.h
+extract.o : extract.c lha.h
+header.o : header.c lha.h
+huf.o : huf.c lha.h
+shuf.o : shuf.c lha.h
+slide.o : slide.c lha.h
+lharc.o : lharc.c lha.h
+larc.o : larc.c lha.h
+crcio.o : crcio.c lha.h
+bitio.o : bitio.c lha.h
+maketbl.o : maketbl.c lha.h
+maketree.o : maketree.c lha.h
+util.o : util.c lha.h
+
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* dhuf.c -- Dynamic Hufffman routine */
+/* */
+/* Modified H.Yoshizaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+/* ------------------------------------------------------------------------ */
+static short child[TREESIZE], parent[TREESIZE], block[TREESIZE], edge[TREESIZE], stock[TREESIZE],
+ s_node[TREESIZE / 2]; /* Changed N.Watazaki */
+/* node[..] -> s_node[..] */
+
+static unsigned short freq[TREESIZE];
+
+static unsigned short total_p;
+static int avail, n1;
+static int most_p, nn;
+static unsigned long nextcount;
+/* ------------------------------------------------------------------------ */
+void
+start_c_dyn( /* void */ )
+{
+ int i, j, f;
+
+ n1 = (n_max >= 256 + maxmatch - THRESHOLD + 1) ? 512 : n_max - 1;
+ for (i = 0; i < TREESIZE_C; i++) {
+ stock[i] = i;
+ block[i] = 0;
+ }
+ for (i = 0, j = n_max * 2 - 2; i < n_max; i++, j--) {
+ freq[j] = 1;
+ child[j] = ~i;
+ s_node[i] = j;
+ block[j] = 1;
+ }
+ avail = 2;
+ edge[1] = n_max - 1;
+ i = n_max * 2 - 2;
+ while (j >= 0) {
+ f = freq[j] = freq[i] + freq[i - 1];
+ child[j] = i;
+ parent[i] = parent[i - 1] = j;
+ if (f == freq[j + 1]) {
+ edge[block[j] = block[j + 1]] = j;
+ }
+ else {
+ edge[block[j] = stock[avail++]] = j;
+ }
+ i -= 2;
+ j--;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+start_p_dyn( /* void */ )
+{
+ freq[ROOT_P] = 1;
+ child[ROOT_P] = ~(N_CHAR);
+ s_node[N_CHAR] = ROOT_P;
+ edge[block[ROOT_P] = stock[avail++]] = ROOT_P;
+ most_p = ROOT_P;
+ total_p = 0;
+ nn = 1 << dicbit;
+ nextcount = 64;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh2 */
+void
+decode_start_dyn( /* void */ )
+{
+ n_max = 286;
+ maxmatch = MAXMATCH;
+ init_getbits();
+ init_code_cache();
+ start_c_dyn();
+ start_p_dyn();
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+reconst(start, end)
+ int start;
+ int end;
+{
+ int i, j, k, l, b;
+ unsigned int f, g;
+
+ for (i = j = start; i < end; i++) {
+ if ((k = child[i]) < 0) {
+ freq[j] = (freq[i] + 1) / 2;
+ child[j] = k;
+ j++;
+ }
+ if (edge[b = block[i]] == i) {
+ stock[--avail] = b;
+ }
+ }
+ j--;
+ i = end - 1;
+ l = end - 2;
+ while (i >= start) {
+ while (i >= l) {
+ freq[i] = freq[j];
+ child[i] = child[j];
+ i--, j--;
+ }
+ f = freq[l] + freq[l + 1];
+ for (k = start; f < freq[k]; k++);
+ while (j >= k) {
+ freq[i] = freq[j];
+ child[i] = child[j];
+ i--, j--;
+ }
+ freq[i] = f;
+ child[i] = l + 1;
+ i--;
+ l -= 2;
+ }
+ f = 0;
+ for (i = start; i < end; i++) {
+ if ((j = child[i]) < 0)
+ s_node[~j] = i;
+ else
+ parent[j] = parent[j - 1] = i;
+ if ((g = freq[i]) == f) {
+ block[i] = b;
+ }
+ else {
+ edge[b = block[i] = stock[avail++]] = i;
+ f = g;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static int
+swap_inc(p)
+ int p;
+{
+ int b, q, r, s;
+
+ b = block[p];
+ if ((q = edge[b]) != p) { /* swap for leader */
+ r = child[p];
+ s = child[q];
+ child[p] = s;
+ child[q] = r;
+ if (r >= 0)
+ parent[r] = parent[r - 1] = q;
+ else
+ s_node[~r] = q;
+ if (s >= 0)
+ parent[s] = parent[s - 1] = p;
+ else
+ s_node[~s] = p;
+ p = q;
+ goto Adjust;
+ }
+ else if (b == block[p + 1]) {
+Adjust:
+ edge[b]++;
+ if (++freq[p] == freq[p - 1]) {
+ block[p] = block[p - 1];
+ }
+ else {
+ edge[block[p] = stock[avail++]] = p; /* create block */
+ }
+ }
+ else if (++freq[p] == freq[p - 1]) {
+ stock[--avail] = b; /* delete block */
+ block[p] = block[p - 1];
+ }
+ return parent[p];
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+update_c(p)
+ int p;
+{
+ int q;
+
+ if (freq[ROOT_C] == 0x8000) {
+ reconst(0, n_max * 2 - 1);
+ }
+ freq[ROOT_C]++;
+ q = s_node[p];
+ do {
+ q = swap_inc(q);
+ } while (q != ROOT_C);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+update_p(p)
+ int p;
+{
+ int q;
+
+ if (total_p == 0x8000) {
+ reconst(ROOT_P, most_p + 1);
+ total_p = freq[ROOT_P];
+ freq[ROOT_P] = 0xffff;
+ }
+ q = s_node[p + N_CHAR];
+ while (q != ROOT_P) {
+ q = swap_inc(q);
+ }
+ total_p++;
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+make_new_node(p)
+ int p;
+{
+ int q, r;
+
+ r = most_p + 1;
+ q = r + 1;
+ s_node[~(child[r] = child[most_p])] = r;
+ child[q] = ~(p + N_CHAR);
+ child[most_p] = q;
+ freq[r] = freq[most_p];
+ freq[q] = 0;
+ block[r] = block[most_p];
+ if (most_p == ROOT_P) {
+ freq[ROOT_P] = 0xffff;
+ edge[block[ROOT_P]]++;
+ }
+ parent[r] = parent[q] = most_p;
+ edge[block[q] = stock[avail++]] = s_node[p + N_CHAR] = most_p = q;
+ update_p(p);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+encode_c_dyn(c)
+ unsigned int c;
+{
+ unsigned int bits;
+ int p, d, cnt;
+
+ d = c - n1;
+ if (d >= 0) {
+ c = n1;
+ }
+ cnt = bits = 0;
+ p = s_node[c];
+ do {
+ bits >>= 1;
+ if (p & 1) {
+ bits |= 0x80000000L;
+ }
+ cnt++;
+ } while ((p = parent[p]) != ROOT_C);
+ if (cnt <= 16) {
+ putcode(cnt, bits >> 16);
+ } else {
+ putcode(16, bits >> 16);
+ putbits(cnt - 16, bits);
+ }
+ if (d >= 0)
+ putbits(8, d);
+ update_c(c);
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1, 2 */
+unsigned short
+decode_c_dyn( /* void */ )
+{
+ int c;
+ short buf, cnt;
+
+ c = child[ROOT_C];
+ buf = bitbuf;
+ cnt = 0;
+ do {
+ c = child[c - (buf < 0)];
+ buf <<= 1;
+ if (++cnt == 16) {
+ fillbuf(16);
+ buf = bitbuf;
+ cnt = 0;
+ }
+ } while (c > 0);
+ fillbuf(cnt);
+ c = ~c;
+ update_c(c);
+ if (c == n1)
+ c += getbits(8);
+ return c;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh2 */
+unsigned short
+decode_p_dyn( /* void */ )
+{
+ int c;
+ short buf, cnt;
+
+ while (decode_count > nextcount) {
+ make_new_node(nextcount / 64);
+ if ((nextcount += 64) >= nn)
+ nextcount = 0xffffffff;
+ }
+ c = child[ROOT_P];
+ buf = bitbuf;
+ cnt = 0;
+ while (c > 0) {
+ c = child[c - (buf < 0)];
+ buf <<= 1;
+ if (++cnt == 16) {
+ fillbuf(16);
+ buf = bitbuf;
+ cnt = 0;
+ }
+ }
+ fillbuf(cnt);
+ c = (~c) - N_CHAR;
+ update_p(c);
+
+ return (c << 6) + getbits(6);
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1 */
+void
+output_dyn(code, pos)
+ unsigned int code;
+ unsigned int pos;
+{
+ encode_c_dyn(code);
+ if (code >= 0x100) {
+ encode_p_st0(pos);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1 */
+void
+encode_end_dyn( /* void */ )
+{
+ putcode(7, 0);
+}
--- /dev/null
+#----------------------------------
+# extconf.rb
+# $Revision: $
+# $Date: $
+#----------------------------------
+require 'mkmf'
+
+class Path
+
+ def initialize()
+ if File::ALT_SEPARATOR.nil?
+ @file_separator = File::SEPARATOR
+ else
+ @file_separator = File::ALT_SEPARATOR
+ end
+ end
+
+ def include(parent, child)
+ inc = joint(parent, child)
+ $INCFLAGS += " -I#{inc}"
+ $CFLAGS += " -I#{inc}"
+ inc
+ end
+
+ def joint(parent, child)
+ parent + @file_separator + child
+ end
+
+end
+
+def create_lhalib_makefile
+ create_makefile("lhalib")
+end
+
+case RUBY_PLATFORM
+when /mswin32/
+ $CFLAGS += ' /W3'
+when /cygwin/, /mingw/
+ $defs << '-DNONAMELESSUNION'
+end
+create_lhalib_makefile
+
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* extract.c -- extrcat from archive */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+int
+decode_lzhuf(infp, outfp, original_size, packed_size, name, method, read_sizep)
+ FILE *infp;
+ FILE *outfp;
+ size_t original_size;
+ size_t packed_size;
+ char *name;
+ int method;
+ size_t *read_sizep;
+{
+ unsigned int crc;
+ struct interfacing intf;
+
+ intf.method = method;
+ intf.infile = infp;
+ intf.outfile = outfp;
+ intf.original = original_size;
+ intf.packed = packed_size;
+ intf.read_size = 0;
+
+ switch (method) {
+ case LZHUFF0_METHOD_NUM: /* -lh0- */
+ intf.dicbit = LZHUFF0_DICBIT;
+ break;
+ case LZHUFF1_METHOD_NUM: /* -lh1- */
+ intf.dicbit = LZHUFF1_DICBIT;
+ break;
+ case LZHUFF2_METHOD_NUM: /* -lh2- */
+ intf.dicbit = LZHUFF2_DICBIT;
+ break;
+ case LZHUFF3_METHOD_NUM: /* -lh2- */
+ intf.dicbit = LZHUFF3_DICBIT;
+ break;
+ case LZHUFF4_METHOD_NUM: /* -lh4- */
+ intf.dicbit = LZHUFF4_DICBIT;
+ break;
+ case LZHUFF5_METHOD_NUM: /* -lh5- */
+ intf.dicbit = LZHUFF5_DICBIT;
+ break;
+ case LZHUFF6_METHOD_NUM: /* -lh6- */
+ intf.dicbit = LZHUFF6_DICBIT;
+ break;
+ case LZHUFF7_METHOD_NUM: /* -lh7- */
+ intf.dicbit = LZHUFF7_DICBIT;
+ break;
+ case LARC_METHOD_NUM: /* -lzs- */
+ intf.dicbit = LARC_DICBIT;
+ break;
+ case LARC5_METHOD_NUM: /* -lz5- */
+ intf.dicbit = LARC5_DICBIT;
+ break;
+ case LARC4_METHOD_NUM: /* -lz4- */
+ intf.dicbit = LARC4_DICBIT;
+ break;
+ default:
+ warning("unknown method %d", method);
+ intf.dicbit = LZHUFF5_DICBIT; /* for backward compatibility */
+ break;
+ }
+
+ if (intf.dicbit == 0) { /* LZHUFF0_DICBIT or LARC4_DICBIT */
+ start_indicator(name,
+ original_size, "Melting ",
+ 2048);
+ *read_sizep = copyfile(infp, outfp,
+ original_size, 2, &crc);
+ }
+ else {
+ start_indicator(name,
+ original_size, "Melting ",
+ 1 << intf.dicbit);
+ crc = decode(&intf);
+ *read_sizep = intf.read_size;
+ }
+
+ finish_indicator(name, "Melted ");
+
+ return crc;
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* header.c -- header manipulate functions */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Original Y.Tagawa */
+/* modified 1991.12.16 M.Oki */
+/* Ver. 1.10 Symbolic Link added 1993.10.01 N.Watazaki */
+/* Ver. 1.13b Symbolic Link Bug Fix 1994.08.22 N.Watazaki */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14i bug fixed 2000.10.06 t.okamoto */
+/* Ver. 1.14i Contributed UTF-8 convertion for Mac OS X */
+/* 2002.06.29 Hiroto Sakai */
+/* Ver. 1.14i autoconfiscated & rewritten 2003.02.23 Koji Arai */
+/* Modified arton for LhaLib */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+#define DUMP_HEADER 1 /* for debugging */
+
+static char *get_ptr;
+#define GET_BYTE() (*get_ptr++ & 0xff)
+
+#if DUMP_HEADER
+static char *start_ptr;
+#define setup_get(PTR) (start_ptr = get_ptr = (PTR))
+#define get_byte() dump_get_byte()
+#define skip_bytes(len) dump_skip_bytes(len)
+#else
+#define setup_get(PTR) (get_ptr = (PTR))
+#define get_byte() GET_BYTE()
+#define skip_bytes(len) (get_ptr += (len))
+#endif
+#define put_ptr get_ptr
+#define setup_put(PTR) (put_ptr = (PTR))
+#define put_byte(c) (*put_ptr++ = (char)(c))
+
+int optional_archive_kanji_code = NONE;
+int optional_system_kanji_code = NONE;
+char *optional_archive_delim = NULL;
+char *optional_system_delim = NULL;
+int optional_filename_case = NONE;
+
+#ifdef MULTIBYTE_FILENAME
+int default_system_kanji_code = MULTIBYTE_FILENAME;
+#else
+int default_system_kanji_code = NONE;
+#endif
+
+int
+calc_sum(p, len)
+ char *p;
+ int len;
+{
+ int sum = 0;
+
+ while (len--) sum += *p++;
+
+ return sum & 0xff;
+}
+
+#if DUMP_HEADER
+static int
+dump_get_byte()
+{
+ int c;
+
+ if (verbose_listing && verbose > 1)
+ printf("%02d %2d: ", get_ptr - start_ptr, 1);
+ c = GET_BYTE();
+ if (verbose_listing && verbose > 1) {
+ if (isprint(c))
+ printf("%d(0x%02x) '%c'\n", c, c, c);
+ else
+ printf("%d(0x%02x)\n", c, c);
+ }
+ return c;
+}
+
+static void
+dump_skip_bytes(len)
+ int len;
+{
+ if (len == 0) return;
+ if (verbose_listing && verbose > 1) {
+ printf("%02d %2d: ", get_ptr - start_ptr, len);
+ while (len--)
+ printf("0x%02x ", GET_BYTE());
+ printf("... ignored\n");
+ }
+ else
+ get_ptr += len;
+}
+#endif
+
+static int
+get_word()
+{
+ int b0, b1;
+ int w;
+
+#if DUMP_HEADER
+ if (verbose_listing && verbose > 1)
+ printf("%02d %2d: ", get_ptr - start_ptr, 2);
+#endif
+ b0 = GET_BYTE();
+ b1 = GET_BYTE();
+ w = (b1 << 8) + b0;
+#if DUMP_HEADER
+ if (verbose_listing && verbose > 1)
+ printf("%d(0x%04x)\n", w, w);
+#endif
+ return w;
+}
+
+static void
+put_word(v)
+ unsigned int v;
+{
+ put_byte(v);
+ put_byte(v >> 8);
+}
+
+static long
+get_longword()
+{
+ long b0, b1, b2, b3;
+ long l;
+
+#if DUMP_HEADER
+ if (verbose_listing && verbose > 1)
+ printf("%02d %2d: ", get_ptr - start_ptr, 4);
+#endif
+ b0 = GET_BYTE();
+ b1 = GET_BYTE();
+ b2 = GET_BYTE();
+ b3 = GET_BYTE();
+ l = (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+#if DUMP_HEADER
+ if (verbose_listing && verbose > 1)
+ printf("%ld(0x%08lx)\n", l, l);
+#endif
+ return l;
+}
+
+static void
+put_longword(v)
+ long v;
+{
+ put_byte(v);
+ put_byte(v >> 8);
+ put_byte(v >> 16);
+ put_byte(v >> 24);
+}
+
+static int
+get_bytes(buf, len, size)
+ char *buf;
+ int len, size;
+{
+ int i;
+
+#if DUMP_HEADER
+ if (verbose_listing && verbose > 1)
+ printf("%02d %2d: \"", get_ptr - start_ptr, len);
+
+ for (i = 0; i < len; i++) {
+ if (i < size) buf[i] = get_ptr[i];
+
+ if (verbose_listing && verbose > 1) {
+ if (isprint(buf[i]))
+ printf("%c", buf[i]);
+ else
+ printf("\\x%02x", (unsigned char)buf[i]);
+ }
+ }
+
+ if (verbose_listing && verbose > 1)
+ printf("\"\n");
+#else
+ for (i = 0; i < len && i < size; i++)
+ buf[i] = get_ptr[i];
+#endif
+
+ get_ptr += len;
+ return i;
+}
+
+static void
+put_bytes(buf, len)
+ char *buf;
+ int len;
+{
+ int i;
+ for (i = 0; i < len; i++)
+ put_byte(buf[i]);
+}
+
+/* added by Koji Arai */
+void
+convert_filename(name, len, size,
+ from_code, to_code,
+ from_delim, to_delim,
+ case_to)
+ char *name;
+ int len; /* length of name */
+ int size; /* size of name buffer */
+ int from_code, to_code, case_to;
+ char *from_delim, *to_delim;
+
+{
+ int i;
+#ifdef MULTIBYTE_FILENAME
+ char tmp[FILENAME_LENGTH];
+ int to_code_save = NONE;
+
+ if (from_code == CODE_CAP) {
+ len = cap_to_sjis(tmp, name, sizeof(tmp));
+ strncpy(name, tmp, size);
+ name[size-1] = 0;
+ len = strlen(name);
+ from_code = CODE_SJIS;
+ }
+
+ if (to_code == CODE_CAP) {
+ to_code_save = CODE_CAP;
+ to_code = CODE_SJIS;
+ }
+
+ if (from_code == CODE_SJIS && to_code == CODE_UTF8) {
+ for (i = 0; i < len; i++)
+ /* FIXME: provisionally fix for the Mac OS CoreFoundation */
+ if ((unsigned char)name[i] == LHA_PATHSEP) name[i] = '/';
+ sjis_to_utf8(tmp, name, sizeof(tmp));
+ strncpy(name, tmp, size);
+ name[size-1] = 0;
+ len = strlen(name);
+ for (i = 0; i < len; i++)
+ if (name[i] == '/') name[i] = LHA_PATHSEP;
+ from_code = CODE_UTF8;
+ }
+ else if (from_code == CODE_UTF8 && to_code == CODE_SJIS) {
+ for (i = 0; i < len; i++)
+ /* FIXME: provisionally fix for the Mac OS CoreFoundation */
+ if ((unsigned char)name[i] == LHA_PATHSEP) name[i] = '/';
+ utf8_to_sjis(tmp, name, sizeof(tmp));
+ strncpy(name, tmp, size);
+ name[size-1] = 0;
+ len = strlen(name);
+ for (i = 0; i < len; i++)
+ if (name[i] == '/') name[i] = LHA_PATHSEP;
+ from_code = CODE_SJIS;
+ }
+#endif
+
+ /* special case: if `name' has small lettter, not convert case. */
+ if (from_code == CODE_SJIS && case_to == TO_LOWER) {
+ for (i = 0; i < len; i++) {
+#ifdef MULTIBYTE_FILENAME
+ if (SJIS_FIRST_P(name[i]) && SJIS_SECOND_P(name[i+1]))
+ i++;
+ else
+#endif
+ if (islower(name[i])) {
+ case_to = NONE;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < len; i ++) {
+#ifdef MULTIBYTE_FILENAME
+ if (from_code == CODE_EUC &&
+ (unsigned char)name[i] == 0x8e) {
+ if (to_code != CODE_SJIS) {
+ i++;
+ continue;
+ }
+
+ /* X0201 KANA */
+ memmove(name + i, name + i + 1, len - i);
+ len--;
+ continue;
+ }
+ if (from_code == CODE_SJIS && X0201_KANA_P(name[i])) {
+ if (to_code != CODE_EUC) {
+ continue;
+ }
+
+ if (len == size - 1) /* check overflow */
+ len--;
+ memmove(name+i+1, name+i, len-i);
+ name[i] = 0x8e;
+ i++;
+ len++;
+ continue;
+ }
+ if (from_code == CODE_EUC && (name[i] & 0x80) && (name[i+1] & 0x80)) {
+ int c1, c2;
+ if (to_code != CODE_SJIS) {
+ i++;
+ continue;
+ }
+
+ c1 = (unsigned char)name[i];
+ c2 = (unsigned char)name[i+1];
+ euc2sjis(&c1, &c2);
+ name[i] = c1;
+ name[i+1] = c2;
+ i++;
+ continue;
+ }
+ if (from_code == CODE_SJIS &&
+ SJIS_FIRST_P(name[i]) &&
+ SJIS_SECOND_P(name[i+1])) {
+ int c1, c2;
+
+ if (to_code != CODE_EUC) {
+ i++;
+ continue;
+ }
+
+ c1 = (unsigned char)name[i];
+ c2 = (unsigned char)name[i+1];
+ sjis2euc(&c1, &c2);
+ name[i] = c1;
+ name[i+1] = c2;
+ i++;
+ continue;
+ }
+#endif /* MULTIBYTE_FILENAME */
+ {
+ char *ptr;
+
+ /* transpose from_delim to to_delim */
+
+ if ((ptr = strchr(from_delim, name[i])) != NULL) {
+ name[i] = to_delim[ptr - from_delim];
+ continue;
+ }
+ }
+
+ if (case_to == TO_UPPER && islower(name[i])) {
+ name[i] = toupper(name[i]);
+ continue;
+ }
+ if (case_to == TO_LOWER && isupper(name[i])) {
+ name[i] = tolower(name[i]);
+ continue;
+ }
+ }
+
+#ifdef MULTIBYTE_FILENAME
+ if (to_code_save == CODE_CAP) {
+ len = sjis_to_cap(tmp, name, sizeof(tmp));
+ strncpy(name, tmp, size);
+ name[size-1] = 0;
+ len = strlen(name);
+ }
+#endif /* MULTIBYTE_FILENAME */
+}
+
+/*
+ * Generic (MS-DOS style) time stamp format (localtime):
+ *
+ * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ * |<---- year-1980 --->|<- month ->|<--- day ---->|
+ *
+ * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ * |<--- hour --->|<---- minute --->|<- second/2 ->|
+ *
+ */
+
+static time_t
+generic_to_unix_stamp(t)
+ long t;
+{
+ struct tm tm;
+
+#define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1))
+
+ tm.tm_sec = subbits(t, 0, 5) * 2;
+ tm.tm_min = subbits(t, 5, 6);
+ tm.tm_hour = subbits(t, 11, 5);
+ tm.tm_mday = subbits(t, 16, 5);
+ tm.tm_mon = subbits(t, 21, 4) - 1;
+ tm.tm_year = subbits(t, 25, 7) + 80;
+ tm.tm_isdst = -1;
+
+#if HAVE_MKTIME
+ return mktime(&tm);
+#else
+ return timelocal(&tm);
+#endif
+}
+
+static long
+unix_to_generic_stamp(t)
+ time_t t;
+{
+ struct tm *tm = localtime(&t);
+
+ tm->tm_year -= 80;
+ tm->tm_mon += 1;
+
+ return ((long)(tm->tm_year << 25) +
+ (tm->tm_mon << 21) +
+ (tm->tm_mday << 16) +
+ (tm->tm_hour << 11) +
+ (tm->tm_min << 5) +
+ (tm->tm_sec / 2));
+}
+
+static unsigned long
+wintime_to_unix_stamp()
+{
+#if HAVE_UINT64_T
+ uint64_t t;
+ uint64_t epoch = ((uint64_t)0x019db1de << 32) + 0xd53e8000;
+ /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */
+
+ t = (unsigned long)get_longword();
+ t |= (uint64_t)(unsigned long)get_longword() << 32;
+ t = (t - epoch) / 10000000;
+ return t;
+#else
+ int i, borrow;
+ unsigned long t, q, x;
+ unsigned long wintime[8];
+ unsigned long epoch[8] = {0x01,0x9d,0xb1,0xde, 0xd5,0x3e,0x80,0x00};
+ /* 1970-01-01 00:00:00 (UTC) */
+ /* wintime -= epoch */
+ borrow = 0;
+ for (i = 7; i >= 0; i--) {
+ wintime[i] = (unsigned)get_byte() - epoch[i] - borrow;
+ borrow = (wintime[i] > 0xff) ? 1 : 0;
+ wintime[i] &= 0xff;
+ }
+
+ /* q = wintime / 10000000 */
+ t = q = 0;
+ x = 10000000; /* x: 24bit */
+ for (i = 0; i < 8; i++) {
+ t = (t << 8) + wintime[i]; /* 24bit + 8bit. t must be 32bit variable */
+ q <<= 8; /* q must be 32bit (time_t) */
+ q += t / x;
+ t %= x; /* 24bit */
+ }
+ return q;
+#endif
+}
+
+/*
+ * extended header
+ *
+ * size field name
+ * --------------------------------
+ * base header: :
+ * 2 or 4 next-header size [*1]
+ * --------------------------------------
+ * ext header: 1 ext-type ^
+ * ? contents | [*1] next-header size
+ * 2 or 4 next-header size v
+ * --------------------------------------
+ *
+ * on level 1, 2 header:
+ * size field is 2 bytes
+ * on level 3 header:
+ * size field is 4 bytes
+ */
+
+static ssize_t
+get_extended_header(fp, hdr, header_size, hcrc)
+ FILE *fp;
+ LzHeader *hdr;
+ size_t header_size;
+ unsigned int *hcrc;
+{
+ char data[LZHEADER_STORAGE];
+ int name_length;
+ char dirname[FILENAME_LENGTH];
+ int dir_length = 0;
+ int i;
+ ssize_t whole_size = header_size;
+ int ext_type;
+ int n = 1 + hdr->size_field_length; /* `ext-type' + `next-header size' */
+
+ if (hdr->header_level == 0)
+ return 0;
+
+ name_length = strlen(hdr->name);
+
+ while (header_size) {
+ setup_get(data);
+ if (sizeof(data) < header_size) {
+ error("header size (%ld) too large.", header_size);
+ exit(1);
+ }
+
+ if (fread(data, header_size, 1, fp) == 0) {
+ error("Invalid header (LHa file ?)");
+ return -1;
+ }
+
+ ext_type = get_byte();
+ switch (ext_type) {
+ case 0:
+ /* header crc (CRC-16) */
+ hdr->header_crc = get_word();
+ /* clear buffer for CRC calculation. */
+ data[1] = data[2] = 0;
+ skip_bytes(header_size - n - 2);
+ break;
+ case 1:
+ /* filename */
+ name_length =
+ get_bytes(hdr->name, header_size-n, sizeof(hdr->name)-1);
+ hdr->name[name_length] = 0;
+ break;
+ case 2:
+ /* directory */
+ dir_length = get_bytes(dirname, header_size-n, sizeof(dirname)-1);
+ dirname[dir_length] = 0;
+ break;
+ case 0x40:
+ /* MS-DOS attribute */
+ hdr->attribute = get_word();
+ break;
+ case 0x41:
+ /* Windows time stamp (FILETIME structure) */
+ /* it is time in 100 nano seconds since 1601-01-01 00:00:00 */
+
+ skip_bytes(8); /* create time is ignored */
+
+ /* set last modified time */
+ if (hdr->header_level >= 2)
+ skip_bytes(8); /* time_t has been already set */
+ else
+ hdr->unix_last_modified_stamp = wintime_to_unix_stamp();
+
+ skip_bytes(8); /* last access time is ignored */
+
+ break;
+ case 0x50:
+ /* UNIX permission */
+ hdr->unix_mode = get_word();
+ break;
+ case 0x51:
+ /* UNIX gid and uid */
+ hdr->unix_gid = get_word();
+ hdr->unix_uid = get_word();
+ break;
+ case 0x52:
+ /* UNIX group name */
+ i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
+ hdr->group[i] = '\0';
+ break;
+ case 0x53:
+ /* UNIX user name */
+ i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
+ hdr->user[i] = '\0';
+ break;
+ case 0x54:
+ /* UNIX last modified time */
+ hdr->unix_last_modified_stamp = (time_t) get_longword();
+ break;
+ default:
+ /* other headers */
+ /* 0x39: multi-disk header
+ 0x3f: uncompressed comment
+ 0x42: 64bit large file size
+ 0x48-0x4f(?): reserved for authenticity verification
+ 0x7d: encapsulation
+ 0x7e: extended attribute - platform information
+ 0x7f: extended attribute - permission, owner-id and timestamp
+ (level 3 on OS/2)
+ 0xc4: compressed comment (dict size: 4096)
+ 0xc5: compressed comment (dict size: 8192)
+ 0xc6: compressed comment (dict size: 16384)
+ 0xc7: compressed comment (dict size: 32768)
+ 0xc8: compressed comment (dict size: 65536)
+ 0xd0-0xdf(?): operating systemm specific information
+ 0xfc: encapsulation (another opinion)
+ 0xfe: extended attribute - platform information(another opinion)
+ 0xff: extended attribute - permission, owner-id and timestamp
+ (level 3 on UNLHA32) */
+ if (verbose)
+ warning("unknown extended header 0x%02x", ext_type);
+ skip_bytes(header_size - n);
+ break;
+ }
+
+ if (hcrc)
+ *hcrc = calccrc(*hcrc, data, header_size);
+
+ if (hdr->size_field_length == 2)
+ whole_size += header_size = get_word();
+ else
+ whole_size += header_size = get_longword();
+ }
+
+ /* concatenate dirname and filename */
+ if (dir_length) {
+ if (name_length + dir_length >= sizeof(hdr->name)) {
+ warning("the length of pathname \"%s%s\" is too long.",
+ dirname, hdr->name);
+ name_length = sizeof(hdr->name) - dir_length - 1;
+ hdr->name[name_length] = 0;
+ }
+ strcat(dirname, hdr->name); /* ok */
+ strcpy(hdr->name, dirname); /* ok */
+ name_length += dir_length;
+ }
+
+ return whole_size;
+}
+
+#define I_HEADER_SIZE 0 /* level 0,1,2 */
+#define I_HEADER_CHECKSUM 1 /* level 0,1 */
+#define I_METHOD 2 /* level 0,1,2,3 */
+#define I_PACKED_SIZE 7 /* level 0,1,2,3 */
+#define I_ATTRIBUTE 19 /* level 0,1,2,3 */
+#define I_HEADER_LEVEL 20 /* level 0,1,2,3 */
+
+#define COMMON_HEADER_SIZE 21 /* size of common part */
+
+#define I_GENERIC_HEADER_SIZE 24 /* + name_length */
+#define I_LEVEL0_HEADER_SIZE 36 /* + name_length (unix extended) */
+#define I_LEVEL1_HEADER_SIZE 27 /* + name_length */
+#define I_LEVEL2_HEADER_SIZE 26 /* + padding */
+#define I_LEVEL3_HEADER_SIZE 32
+
+/*
+ * level 0 header
+ *
+ *
+ * offset size field name
+ * ----------------------------------
+ * 0 1 header size [*1]
+ * 1 1 header sum
+ * ---------------------------------------
+ * 2 5 method ID ^
+ * 7 4 packed size [*2] |
+ * 11 4 original size |
+ * 15 2 time |
+ * 17 2 date |
+ * 19 1 attribute | [*1] header size (X+Y+22)
+ * 20 1 level (0x00 fixed) |
+ * 21 1 name length |
+ * 22 X pathname |
+ * X +22 2 file crc (CRC-16) |
+ * X +24 Y ext-header(old style) v
+ * -------------------------------------------------
+ * X+Y+24 data ^
+ * : | [*2] packed size
+ * : v
+ * -------------------------------------------------
+ *
+ * ext-header(old style)
+ * 0 1 ext-type ('U')
+ * 1 1 minor version
+ * 2 4 UNIX time
+ * 6 2 mode
+ * 8 2 uid
+ * 10 2 gid
+ *
+ * attribute (MS-DOS)
+ * bit1 read only
+ * bit2 hidden
+ * bit3 system
+ * bit4 volume label
+ * bit5 directory
+ * bit6 archive bit (need to backup)
+ *
+ */
+static int
+get_header_level0(fp, hdr, data)
+ FILE *fp;
+ LzHeader *hdr;
+ char *data;
+{
+ size_t header_size;
+ ssize_t extend_size;
+ int checksum;
+ int name_length;
+ int i;
+
+ hdr->size_field_length = 2; /* in bytes */
+ hdr->header_size = header_size = get_byte();
+ checksum = get_byte();
+
+ if (fread(data + COMMON_HEADER_SIZE,
+ header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
+ error("Invalid header (LHarc file ?)");
+ return FALSE; /* finish */
+ }
+
+ if (calc_sum(data + I_METHOD, header_size) != checksum) {
+ error("Checksum error (LHarc file?)");
+ return FALSE;
+ }
+
+ get_bytes(hdr->method, 5, sizeof(hdr->method));
+ hdr->packed_size = get_longword();
+ hdr->original_size = get_longword();
+ hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
+ hdr->attribute = get_byte(); /* MS-DOS attribute */
+ hdr->header_level = get_byte();
+ name_length = get_byte();
+ i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
+ hdr->name[i] = '\0';
+
+ /* defaults for other type */
+ hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+ hdr->unix_gid = 0;
+ hdr->unix_uid = 0;
+
+ extend_size = header_size+2 - name_length - 24;
+
+ if (extend_size < 0) {
+ if (extend_size == -2) {
+ /* CRC field is not given */
+ hdr->extend_type = EXTEND_GENERIC;
+ hdr->has_crc = FALSE;
+
+ return TRUE;
+ }
+
+ error("Unkonwn header (lha file?)");
+ exit(1);
+ }
+
+ hdr->has_crc = TRUE;
+ hdr->crc = get_word();
+
+ if (extend_size == 0)
+ return TRUE;
+
+ hdr->extend_type = get_byte();
+ extend_size--;
+
+ if (hdr->extend_type == EXTEND_UNIX) {
+ if (extend_size >= 11) {
+ hdr->minor_version = get_byte();
+ hdr->unix_last_modified_stamp = (time_t) get_longword();
+ hdr->unix_mode = get_word();
+ hdr->unix_uid = get_word();
+ hdr->unix_gid = get_word();
+ extend_size -= 11;
+ } else {
+ hdr->extend_type = EXTEND_GENERIC;
+ }
+ }
+ if (extend_size > 0)
+ skip_bytes(extend_size);
+
+ hdr->header_size += 2;
+ return TRUE;
+}
+
+/*
+ * level 1 header
+ *
+ *
+ * offset size field name
+ * -----------------------------------
+ * 0 1 header size [*1]
+ * 1 1 header sum
+ * -------------------------------------
+ * 2 5 method ID ^
+ * 7 4 skip size [*2] |
+ * 11 4 original size |
+ * 15 2 time |
+ * 17 2 date |
+ * 19 1 attribute (0x20 fixed) | [*1] header size (X+Y+25)
+ * 20 1 level (0x01 fixed) |
+ * 21 1 name length |
+ * 22 X filename |
+ * X+ 22 2 file crc (CRC-16) |
+ * X+ 24 1 OS ID |
+ * X +25 Y ??? |
+ * X+Y+25 2 next-header size v
+ * -------------------------------------------------
+ * X+Y+27 Z ext-header ^
+ * : |
+ * ----------------------------------- | [*2] skip size
+ * X+Y+Z+27 data |
+ * : v
+ * -------------------------------------------------
+ *
+ */
+static int
+get_header_level1(fp, hdr, data)
+ FILE *fp;
+ LzHeader *hdr;
+ char *data;
+{
+ size_t header_size;
+ ssize_t extend_size;
+ int checksum;
+ int name_length;
+ int i, dummy;
+
+ hdr->size_field_length = 2; /* in bytes */
+ hdr->header_size = header_size = get_byte();
+ checksum = get_byte();
+
+ if (fread(data + COMMON_HEADER_SIZE,
+ header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
+ error("Invalid header (LHarc file ?)");
+ return FALSE; /* finish */
+ }
+
+ if (calc_sum(data + I_METHOD, header_size) != checksum) {
+ error("Checksum error (LHarc file?)");
+ return FALSE;
+ }
+
+ get_bytes(hdr->method, 5, sizeof(hdr->method));
+ hdr->packed_size = get_longword(); /* skip size */
+ hdr->original_size = get_longword();
+ hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
+ hdr->attribute = get_byte(); /* 0x20 fixed */
+ hdr->header_level = get_byte();
+
+ name_length = get_byte();
+ i = get_bytes(hdr->name, name_length, sizeof(hdr->name)-1);
+ hdr->name[i] = '\0';
+
+ /* defaults for other type */
+ hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+ hdr->unix_gid = 0;
+ hdr->unix_uid = 0;
+
+ hdr->has_crc = TRUE;
+ hdr->crc = get_word();
+ hdr->extend_type = get_byte();
+
+ dummy = header_size+2 - name_length - I_LEVEL1_HEADER_SIZE;
+ if (dummy > 0)
+ skip_bytes(dummy); /* skip old style extend header */
+
+ extend_size = get_word();
+ extend_size = get_extended_header(fp, hdr, extend_size, 0);
+ if (extend_size == -1)
+ return FALSE;
+
+ /* On level 1 header, size fields should be adjusted. */
+ /* the `packed_size' field contains the extended header size. */
+ /* the `header_size' field does not. */
+ hdr->packed_size -= extend_size;
+ hdr->header_size += extend_size + 2;
+
+ return TRUE;
+}
+
+/*
+ * level 2 header
+ *
+ *
+ * offset size field name
+ * --------------------------------------------------
+ * 0 2 total header size [*1] ^
+ * ----------------------- |
+ * 2 5 method ID |
+ * 7 4 packed size [*2] |
+ * 11 4 original size |
+ * 15 4 time |
+ * 19 1 RESERVED (0x20 fixed) | [*1] total header size
+ * 20 1 level (0x02 fixed) | (X+26+(1))
+ * 21 2 file crc (CRC-16) |
+ * 23 1 OS ID |
+ * 24 2 next-header size |
+ * ----------------------------------- |
+ * 26 X ext-header |
+ * : |
+ * ----------------------------------- |
+ * X +26 (1) padding v
+ * -------------------------------------------------
+ * X +26+(1) data ^
+ * : | [*2] packed size
+ * : v
+ * -------------------------------------------------
+ *
+ */
+static int
+get_header_level2(fp, hdr, data)
+ FILE *fp;
+ LzHeader *hdr;
+ char *data;
+{
+ size_t header_size;
+ ssize_t extend_size;
+ int padding;
+ unsigned int hcrc;
+
+ hdr->size_field_length = 2; /* in bytes */
+ hdr->header_size = header_size = get_word();
+
+ if (fread(data + COMMON_HEADER_SIZE,
+ I_LEVEL2_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
+ error("Invalid header (LHarc file ?)");
+ return FALSE; /* finish */
+ }
+
+ get_bytes(hdr->method, 5, sizeof(hdr->method));
+ hdr->packed_size = get_longword();
+ hdr->original_size = get_longword();
+ hdr->unix_last_modified_stamp = get_longword();
+ hdr->attribute = get_byte(); /* reserved */
+ hdr->header_level = get_byte();
+
+ /* defaults for other type */
+ hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+ hdr->unix_gid = 0;
+ hdr->unix_uid = 0;
+
+ hdr->has_crc = TRUE;
+ hdr->crc = get_word();
+ hdr->extend_type = get_byte();
+ extend_size = get_word();
+
+ INITIALIZE_CRC(hcrc);
+ hcrc = calccrc(hcrc, data, get_ptr - data);
+
+ extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
+ if (extend_size == -1)
+ return FALSE;
+
+ padding = header_size - I_LEVEL2_HEADER_SIZE - extend_size;
+ while (padding--) /* padding should be 0 or 1 */
+ hcrc = UPDATE_CRC(hcrc, fgetc(fp));
+
+ if (hdr->header_crc != hcrc)
+ error("header CRC error");
+
+ return TRUE;
+}
+
+/*
+ * level 3 header
+ *
+ *
+ * offset size field name
+ * --------------------------------------------------
+ * 0 2 size field length (4 fixed) ^
+ * 2 5 method ID |
+ * 7 4 packed size [*2] |
+ * 11 4 original size |
+ * 15 4 time |
+ * 19 1 RESERVED (0x20 fixed) | [*1] total header size
+ * 20 1 level (0x03 fixed) | (X+32)
+ * 21 2 file crc (CRC-16) |
+ * 23 1 OS ID |
+ * 24 4 total header size [*1] |
+ * 28 4 next-header size |
+ * ----------------------------------- |
+ * 32 X ext-header |
+ * : v
+ * -------------------------------------------------
+ * X +32 data ^
+ * : | [*2] packed size
+ * : v
+ * -------------------------------------------------
+ *
+ */
+static int
+get_header_level3(fp, hdr, data)
+ FILE *fp;
+ LzHeader *hdr;
+ char *data;
+{
+ size_t header_size;
+ ssize_t extend_size;
+ int padding;
+ unsigned int hcrc;
+
+ hdr->size_field_length = get_word();
+
+ if (fread(data + COMMON_HEADER_SIZE,
+ I_LEVEL3_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
+ error("Invalid header (LHarc file ?)");
+ return FALSE; /* finish */
+ }
+
+ get_bytes(hdr->method, 5, sizeof(hdr->method));
+ hdr->packed_size = get_longword();
+ hdr->original_size = get_longword();
+ hdr->unix_last_modified_stamp = get_longword();
+ hdr->attribute = get_byte(); /* reserved */
+ hdr->header_level = get_byte();
+
+ /* defaults for other type */
+ hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+ hdr->unix_gid = 0;
+ hdr->unix_uid = 0;
+
+ hdr->has_crc = TRUE;
+ hdr->crc = get_word();
+ hdr->extend_type = get_byte();
+ hdr->header_size = header_size = get_longword();
+ extend_size = get_longword();
+
+ INITIALIZE_CRC(hcrc);
+ hcrc = calccrc(hcrc, data, get_ptr - data);
+
+ extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
+ if (extend_size == -1)
+ return FALSE;
+
+ padding = header_size - I_LEVEL3_HEADER_SIZE - extend_size;
+ while (padding--) /* padding should be 0 */
+ hcrc = UPDATE_CRC(hcrc, fgetc(fp));
+
+ if (hdr->header_crc != hcrc)
+ error("header CRC error");
+
+ return TRUE;
+}
+
+boolean
+get_header(fp, hdr)
+ FILE *fp;
+ LzHeader *hdr;
+{
+ char data[LZHEADER_STORAGE];
+
+ int archive_kanji_code = CODE_SJIS;
+ int system_kanji_code = default_system_kanji_code;
+ char *archive_delim = "\377\\"; /* `\' is for level 0 header and
+ broken archive. */
+ char *system_delim = "//";
+ int filename_case = NONE;
+ int end_mark;
+
+ memset(hdr, 0, sizeof(LzHeader));
+
+ setup_get(data);
+
+ if ((end_mark = getc(fp)) == EOF || end_mark == 0) {
+ return FALSE; /* finish */
+ }
+ data[0] = end_mark;
+
+ if (fread(data + 1, COMMON_HEADER_SIZE - 1, 1, fp) == 0) {
+ error("Invalid header (LHarc file ?)");
+ return FALSE; /* finish */
+ }
+
+ switch (data[I_HEADER_LEVEL]) {
+ case 0:
+ if (get_header_level0(fp, hdr, data) == FALSE)
+ return FALSE;
+ break;
+ case 1:
+ if (get_header_level1(fp, hdr, data) == FALSE)
+ return FALSE;
+ break;
+ case 2:
+ if (get_header_level2(fp, hdr, data) == FALSE)
+ return FALSE;
+ break;
+ case 3:
+ if (get_header_level3(fp, hdr, data) == FALSE)
+ return FALSE;
+ break;
+ default:
+ error("Unknown level header (level %d)", data[I_HEADER_LEVEL]);
+ return FALSE;
+ }
+
+ /* filename conversion */
+ switch (hdr->extend_type) {
+ case EXTEND_MSDOS:
+ filename_case = noconvertcase ? NONE : TO_LOWER;
+ break;
+ case EXTEND_HUMAN:
+ case EXTEND_OS68K:
+ case EXTEND_XOSK:
+ case EXTEND_UNIX:
+ case EXTEND_JAVA:
+ filename_case = NONE;
+ break;
+
+ case EXTEND_MACOS:
+ archive_delim = "\377/:\\";
+ /* `\' is for level 0 header and broken archive. */
+ system_delim = "/://";
+ filename_case = NONE;
+ break;
+
+ default:
+ filename_case = noconvertcase ? NONE : TO_LOWER;
+ break;
+ }
+
+ if (optional_archive_kanji_code)
+ archive_kanji_code = optional_archive_kanji_code;
+ if (optional_system_kanji_code)
+ system_kanji_code = optional_system_kanji_code;
+ if (optional_archive_delim)
+ archive_delim = optional_archive_delim;
+ if (optional_system_delim)
+ system_delim = optional_system_delim;
+ if (optional_filename_case)
+ filename_case = optional_filename_case;
+
+ /* kanji code and delimiter conversion */
+ convert_filename(hdr->name, strlen(hdr->name), sizeof(hdr->name),
+ archive_kanji_code,
+ system_kanji_code,
+ archive_delim, system_delim, filename_case);
+
+ if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+ char *p;
+ /* split symbolic link */
+ p = strchr(hdr->name, '|');
+ if (p) {
+ /* hdr->name is symbolic link name */
+ /* hdr->realname is real name */
+ *p = 0;
+ strcpy(hdr->realname, p+1); /* ok */
+ }
+ else
+ error("unknown symlink name \"%s\"", hdr->name);
+ }
+
+ return TRUE;
+}
+
+/* skip SFX header */
+int
+seek_lha_header(fp)
+ FILE *fp;
+{
+ unsigned char buffer[64 * 1024]; /* max seek size */
+ unsigned char *p;
+ int n;
+
+ n = fread(buffer, 1, sizeof(buffer), fp);
+
+ for (p = buffer; p < buffer + n; p++) {
+ if (! (p[I_METHOD]=='-' && p[I_METHOD+1]=='l' && p[I_METHOD+4]=='-'))
+ continue;
+ /* found "-l??-" keyword (as METHOD type string) */
+
+ /* level 0 or 1 header */
+ if ((p[I_HEADER_LEVEL] == 0 || p[I_HEADER_LEVEL] == 1)
+ && p[I_HEADER_SIZE] > 20
+ && p[I_HEADER_CHECKSUM] == calc_sum(p+2, p[I_HEADER_SIZE])) {
+ if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
+ fatal_error("cannot seek header");
+ return 0;
+ }
+
+ /* level 2 header */
+ if (p[I_HEADER_LEVEL] == 2
+ && p[I_HEADER_SIZE] >= 24
+ && p[I_ATTRIBUTE] == 0x20) {
+ if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
+ fatal_error("cannot seek header");
+ return 0;
+ }
+ }
+
+ if (fseeko(fp, -n, SEEK_CUR) == -1)
+ fatal_error("cannot seek header");
+ return -1;
+}
+
+
+/* remove leading `xxxx/..' */
+static char *
+remove_leading_dots(char *path)
+{
+ char *first = path;
+ char *ptr = 0;
+
+ if (strcmp(first, "..") == 0) {
+ warning("Removing leading `..' from member name.");
+ return first+1; /* change to "." */
+ }
+
+ if (strstr(first, "..") == 0)
+ return first;
+
+ while (path && *path) {
+
+ if (strcmp(path, "..") == 0)
+ ptr = path = path+2;
+ else if (strncmp(path, "../", 3) == 0)
+ ptr = path = path+3;
+ else
+ path = strchr(path, '/');
+
+ if (path && *path == '/') {
+ path++;
+ }
+ }
+
+ if (ptr) {
+ warning("Removing leading `%.*s' from member name.", ptr-first, first);
+ return ptr;
+ }
+
+ return first;
+}
+
+void
+init_header(name, v_stat, hdr)
+ char *name;
+ struct stat *v_stat;
+ LzHeader *hdr;
+{
+ int len;
+
+ memset(hdr, 0, sizeof(LzHeader));
+
+ /* the `method' member is rewrote by the encoding function.
+ but need set for empty files */
+ memcpy(hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STORAGE);
+
+ hdr->packed_size = 0;
+ hdr->original_size = v_stat->st_size;
+ hdr->attribute = GENERIC_ATTRIBUTE;
+ hdr->header_level = header_level;
+ len = str_safe_copy(hdr->name,
+ remove_leading_dots(name),
+ sizeof(hdr->name));
+ hdr->crc = 0x0000;
+ hdr->extend_type = EXTEND_UNIX;
+ hdr->unix_last_modified_stamp = v_stat->st_mtime;
+ /* since 00:00:00 JAN.1.1970 */
+#ifdef NOT_COMPATIBLE_MODE
+ /* Please need your modification in this space. */
+#else
+ hdr->unix_mode = v_stat->st_mode;
+#endif
+
+ hdr->unix_uid = v_stat->st_uid;
+ hdr->unix_gid = v_stat->st_gid;
+
+#if INCLUDE_OWNER_NAME_IN_HEADER
+#if HAVE_GETPWUID
+ {
+ struct passwd *ent = getpwuid(hdr->unix_uid);
+
+ if (ent) {
+ strncpy(hdr->user, ent->pw_name, sizeof(hdr->user));
+ if (hdr->user[sizeof(hdr->user)-1])
+ hdr->user[sizeof(hdr->user)-1] = 0;
+ }
+ }
+#endif
+#if HAVE_GETGRGID
+ {
+ struct group *ent = getgrgid(hdr->unix_gid);
+
+ if (ent) {
+ strncpy(hdr->group, ent->gr_name, sizeof(hdr->group));
+ if (hdr->group[sizeof(hdr->group)-1])
+ hdr->group[sizeof(hdr->group)-1] = 0;
+ }
+ }
+#endif
+#endif /* INCLUDE_OWNER_NAME_IN_HEADER */
+ if (is_directory(v_stat)) {
+ memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
+ hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
+ hdr->original_size = 0;
+ if (len > 0 && hdr->name[len - 1] != '/') {
+ if (len < sizeof(hdr->name)-1)
+ strcpy(&hdr->name[len++], "/"); /* ok */
+ else
+ warning("the length of dirname \"%s\" is too long.",
+ hdr->name);
+ }
+ }
+
+#ifdef S_IFLNK
+ if (is_symlink(v_stat)) {
+ memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
+ hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
+ hdr->original_size = 0;
+ readlink(name, hdr->realname, sizeof(hdr->realname));
+ }
+#endif
+}
+
+static void
+write_unix_info(hdr)
+ LzHeader *hdr;
+{
+ /* UNIX specific informations */
+
+ put_word(5); /* size */
+ put_byte(0x50); /* permission */
+ put_word(hdr->unix_mode);
+
+ put_word(7); /* size */
+ put_byte(0x51); /* gid and uid */
+ put_word(hdr->unix_gid);
+ put_word(hdr->unix_uid);
+
+ if (hdr->group[0]) {
+ int len = strlen(hdr->group);
+ put_word(len + 3); /* size */
+ put_byte(0x52); /* group name */
+ put_bytes(hdr->group, len);
+ }
+
+ if (hdr->user[0]) {
+ int len = strlen(hdr->user);
+ put_word(len + 3); /* size */
+ put_byte(0x53); /* user name */
+ put_bytes(hdr->user, len);
+ }
+
+ if (hdr->header_level == 1) {
+ put_word(7); /* size */
+ put_byte(0x54); /* time stamp */
+ put_longword(hdr->unix_last_modified_stamp);
+ }
+}
+
+static size_t
+write_header_level0(data, hdr, pathname)
+ LzHeader *hdr;
+ char *data, *pathname;
+{
+ int limit;
+ int name_length;
+ size_t header_size;
+
+ setup_put(data);
+ memset(data, 0, LZHEADER_STORAGE);
+
+ put_byte(0x00); /* header size */
+ put_byte(0x00); /* check sum */
+ put_bytes(hdr->method, 5);
+ put_longword(hdr->packed_size);
+ put_longword(hdr->original_size);
+ put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
+ put_byte(hdr->attribute);
+ put_byte(hdr->header_level); /* level 0 */
+
+ /* write pathname (level 0 header contains the directory part) */
+ name_length = strlen(pathname);
+ if (generic_format)
+ limit = 255 - I_GENERIC_HEADER_SIZE + 2;
+ else
+ limit = 255 - I_LEVEL0_HEADER_SIZE + 2;
+
+ if (name_length > limit) {
+ warning("the length of pathname \"%s\" is too long.", pathname);
+ name_length = limit;
+ }
+ put_byte(name_length);
+ put_bytes(pathname, name_length);
+ put_word(hdr->crc);
+
+ if (generic_format) {
+ header_size = I_GENERIC_HEADER_SIZE + name_length - 2;
+ data[I_HEADER_SIZE] = header_size;
+ data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
+ } else {
+ /* write old-style extend header */
+ put_byte(EXTEND_UNIX);
+ put_byte(CURRENT_UNIX_MINOR_VERSION);
+ put_longword(hdr->unix_last_modified_stamp);
+ put_word(hdr->unix_mode);
+ put_word(hdr->unix_uid);
+ put_word(hdr->unix_gid);
+
+ /* size of extended header is 12 */
+ header_size = I_LEVEL0_HEADER_SIZE + name_length - 2;
+ data[I_HEADER_SIZE] = header_size;
+ data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
+ }
+
+ return header_size + 2;
+}
+
+static size_t
+write_header_level1(data, hdr, pathname)
+ LzHeader *hdr;
+ char *data, *pathname;
+{
+ int name_length, dir_length, limit;
+ char *basename, *dirname;
+ size_t header_size;
+ char *extend_header_top;
+ size_t extend_header_size;
+
+ basename = strrchr(pathname, LHA_PATHSEP);
+ if (basename) {
+ basename++;
+ name_length = strlen(basename);
+ dirname = pathname;
+ dir_length = basename - dirname;
+ }
+ else {
+ basename = pathname;
+ name_length = strlen(basename);
+ dirname = "";
+ dir_length = 0;
+ }
+
+ setup_put(data);
+ memset(data, 0, LZHEADER_STORAGE);
+
+ put_byte(0x00); /* header size */
+ put_byte(0x00); /* check sum */
+ put_bytes(hdr->method, 5);
+ put_longword(hdr->packed_size);
+ put_longword(hdr->original_size);
+ put_longword(unix_to_generic_stamp(hdr->unix_last_modified_stamp));
+ put_byte(0x20);
+ put_byte(hdr->header_level); /* level 1 */
+
+ /* level 1 header: write filename (basename only) */
+ limit = 255 - I_LEVEL1_HEADER_SIZE + 2;
+ if (name_length > limit) {
+ put_byte(0); /* name length */
+ }
+ else {
+ put_byte(name_length);
+ put_bytes(basename, name_length);
+ }
+
+ put_word(hdr->crc);
+
+ if (generic_format)
+ put_byte(0x00);
+ else
+ put_byte(EXTEND_UNIX);
+
+ /* write extend header from here. */
+
+ extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
+ header_size = extend_header_top - data - 2;
+
+ /* write filename and dirname */
+
+ if (name_length > limit) {
+ put_word(name_length + 3); /* size */
+ put_byte(0x01); /* filename */
+ put_bytes(basename, name_length);
+ }
+
+ if (dir_length > 0) {
+ put_word(dir_length + 3); /* size */
+ put_byte(0x02); /* dirname */
+ put_bytes(dirname, dir_length);
+ }
+
+ if (!generic_format)
+ write_unix_info(hdr);
+
+ put_word(0x0000); /* next header size */
+
+ extend_header_size = put_ptr - extend_header_top;
+ /* On level 1 header, the packed size field is contains the ext-header */
+ hdr->packed_size += put_ptr - extend_header_top;
+
+ /* put `skip size' */
+ setup_put(data + I_PACKED_SIZE);
+ put_longword(hdr->packed_size);
+
+ data[I_HEADER_SIZE] = header_size;
+ data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
+
+ return header_size + extend_header_size + 2;
+}
+
+static size_t
+write_header_level2(data, hdr, pathname)
+ LzHeader *hdr;
+ char *data, *pathname;
+{
+ int name_length, dir_length;
+ char *basename, *dirname;
+ size_t header_size;
+ char *extend_header_top;
+ char *headercrc_ptr;
+ unsigned int hcrc;
+
+ basename = strrchr(pathname, LHA_PATHSEP);
+ if (basename) {
+ basename++;
+ name_length = strlen(basename);
+ dirname = pathname;
+ dir_length = basename - dirname;
+ }
+ else {
+ basename = pathname;
+ name_length = strlen(basename);
+ dirname = "";
+ dir_length = 0;
+ }
+
+ setup_put(data);
+ memset(data, 0, LZHEADER_STORAGE);
+
+ put_word(0x0000); /* header size */
+ put_bytes(hdr->method, 5);
+ put_longword(hdr->packed_size);
+ put_longword(hdr->original_size);
+ put_longword(hdr->unix_last_modified_stamp);
+ put_byte(0x20);
+ put_byte(hdr->header_level); /* level 2 */
+
+ put_word(hdr->crc);
+
+ if (generic_format)
+ put_byte(0x00);
+ else
+ put_byte(EXTEND_UNIX);
+
+ /* write extend header from here. */
+
+ extend_header_top = put_ptr+2; /* +2 for the field `next header size' */
+
+ /* write common header */
+ put_word(5);
+ put_byte(0x00);
+ headercrc_ptr = put_ptr;
+ put_word(0x0000); /* header CRC */
+
+ /* write filename and dirname */
+ /* must have this header, even if the name_length is 0. */
+ put_word(name_length + 3); /* size */
+ put_byte(0x01); /* filename */
+ put_bytes(basename, name_length);
+
+ if (dir_length > 0) {
+ put_word(dir_length + 3); /* size */
+ put_byte(0x02); /* dirname */
+ put_bytes(dirname, dir_length);
+ }
+
+ if (!generic_format)
+ write_unix_info(hdr);
+
+ put_word(0x0000); /* next header size */
+
+ header_size = put_ptr - data;
+ if ((header_size & 0xff) == 0) {
+ /* cannot put zero at the first byte on level 2 header. */
+ /* adjust header size. */
+ put_byte(0); /* padding */
+ header_size++;
+ }
+
+ /* put header size */
+ setup_put(data + I_HEADER_SIZE);
+ put_word(header_size);
+
+ /* put header CRC in extended header */
+ INITIALIZE_CRC(hcrc);
+ hcrc = calccrc(hcrc, data, (unsigned int) header_size);
+ setup_put(headercrc_ptr);
+ put_word(hcrc);
+
+ return header_size;
+}
+
+void
+write_header(fp, hdr)
+ FILE *fp;
+ LzHeader *hdr;
+{
+ size_t header_size;
+ char data[LZHEADER_STORAGE];
+
+ int archive_kanji_code = CODE_SJIS;
+ int system_kanji_code = default_system_kanji_code;
+ char *archive_delim = "\377";
+ char *system_delim = "/";
+ int filename_case = NONE;
+ char pathname[FILENAME_LENGTH];
+
+ if (optional_archive_kanji_code)
+ archive_kanji_code = optional_archive_kanji_code;
+ if (optional_system_kanji_code)
+ system_kanji_code = optional_system_kanji_code;
+
+ if (generic_format)
+ filename_case = TO_UPPER;
+
+ if (hdr->header_level == 0) {
+ archive_delim = "\\";
+ }
+
+ if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+ char *p;
+ p = strchr(hdr->name, '|');
+ if (p) {
+ error("symlink name \"%s\" contains '|' char. change it into '_'",
+ hdr->name);
+ *p = '_';
+ }
+ if (xsnprintf(pathname, sizeof(pathname),
+ "%s|%s", hdr->name, hdr->realname) == -1)
+ error("file name is too long (%s -> %s)", hdr->name, hdr->realname);
+ }
+ else {
+ strncpy(pathname, hdr->name, sizeof(pathname));
+ pathname[sizeof(pathname)-1] = 0;
+ }
+
+ convert_filename(pathname, strlen(pathname), sizeof(pathname),
+ system_kanji_code,
+ archive_kanji_code,
+ system_delim, archive_delim, filename_case);
+
+ switch (hdr->header_level) {
+ case 0:
+ header_size = write_header_level0(data, hdr, pathname);
+ break;
+ case 1:
+ header_size = write_header_level1(data, hdr, pathname);
+ break;
+ case 2:
+ header_size = write_header_level2(data, hdr, pathname);
+ break;
+ default:
+ error("Unknown level header (level %d)", hdr->header_level);
+ exit(1);
+ }
+
+ if (fwrite(data, header_size, 1, fp) == 0)
+ fatal_error("Cannot write to temporary file");
+}
+
+#if MULTIBYTE_FILENAME
+
+#if defined(__APPLE__) /* Added by Hiroto Sakai */
+
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFStringEncodingExt.h>
+
+/* this is not need for Mac OS X v 10.2 later */
+enum {
+ kCFStringEncodingAllowLossyConversion = 1,
+ kCFStringEncodingBasicDirectionLeftToRight = (1 << 1),
+ kCFStringEncodingBasicDirectionRightToLeft = (1 << 2),
+ kCFStringEncodingSubstituteCombinings = (1 << 3),
+ kCFStringEncodingComposeCombinings = (1 << 4),
+ kCFStringEncodingIgnoreCombinings = (1 << 5),
+ kCFStringEncodingUseCanonical = (1 << 6),
+ kCFStringEncodingUseHFSPlusCanonical = (1 << 7),
+ kCFStringEncodingPrependBOM = (1 << 8),
+ kCFStringEncodingDisableCorporateArea = (1 << 9),
+ kCFStringEncodingASCIICompatibleConversion = (1 << 10),
+};
+
+static int
+ConvertEncodingToUTF8(const char* inCStr,
+ char* outUTF8Buffer,
+ int outUTF8BufferLength,
+ unsigned long scriptEncoding,
+ unsigned long flags)
+{
+ unsigned long unicodeChars;
+ unsigned long srcCharsUsed;
+ unsigned long usedByteLen = 0;
+ UniChar uniStr[512];
+ unsigned long cfResult;
+
+ cfResult = CFStringEncodingBytesToUnicode(scriptEncoding,
+ flags,
+ (char *)inCStr,
+ strlen(inCStr),
+ &srcCharsUsed,
+ uniStr,
+ 512,
+ &unicodeChars);
+ if (cfResult == 0) {
+ cfResult = CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8,
+ flags,
+ uniStr,
+ unicodeChars,
+ &srcCharsUsed,
+ (char*)outUTF8Buffer,
+ outUTF8BufferLength - 1,
+ &usedByteLen);
+ outUTF8Buffer[usedByteLen] = '\0';
+ }
+
+ return cfResult;
+}
+
+static int
+ConvertUTF8ToEncoding(const char* inUTF8Buf,
+ int inUTF8BufLength,
+ char* outCStrBuffer,
+ int outCStrBufferLength,
+ unsigned long scriptEncoding,
+ unsigned long flags)
+{
+ unsigned long unicodeChars;
+ unsigned long srcCharsUsed;
+ unsigned long usedByteLen = 0;
+ UniChar uniStr[256];
+ unsigned long cfResult;
+
+ cfResult = CFStringEncodingBytesToUnicode(kCFStringEncodingUTF8,
+ flags,
+ (char*)inUTF8Buf,
+ inUTF8BufLength,
+ &srcCharsUsed,
+ uniStr,
+ 255,
+ &unicodeChars);
+ if (cfResult == 0) {
+ cfResult = CFStringEncodingUnicodeToBytes(scriptEncoding,
+ flags,
+ uniStr,
+ unicodeChars,
+ &srcCharsUsed,
+ (char*)outCStrBuffer,
+ outCStrBufferLength - 1,
+ &usedByteLen);
+ outCStrBuffer[usedByteLen] = '\0';
+ }
+
+ return cfResult;
+}
+
+#elif HAVE_ICONV
+#include <iconv.h>
+
+static int
+ConvertEncodingByIconv(const char *src, char *dst, int dstsize,
+ const char *srcEnc, const char *dstEnc)
+{
+ iconv_t ic;
+ static char szTmpBuf[2048];
+ char *src_p;
+ char *dst_p;
+ size_t sLen;
+ size_t iLen;
+
+ dst_p = &szTmpBuf[0];
+ iLen = (size_t)sizeof(szTmpBuf)-1;
+ src_p = (char *)src;
+ sLen = (size_t)strlen(src);
+ memset(szTmpBuf, 0, sizeof(szTmpBuf));
+ memset(dst, 0, dstsize);
+
+ ic = iconv_open(dstEnc, srcEnc);
+ if (ic == (iconv_t)-1) {
+ error("iconv_open() failure: %s", strerror(errno));
+ return -1;
+ }
+
+ if (iconv(ic, &src_p, &sLen, &dst_p, &iLen) == (size_t)-1) {
+ error("iconv() failure: %s", strerror(errno));
+ iconv_close(ic);
+ return -1;
+ }
+
+ strncpy(dst, szTmpBuf, dstsize);
+
+ iconv_close(ic);
+
+ return 0;
+}
+#endif /* defined(__APPLE__) */
+
+char *
+sjis_to_utf8(char *dst, const char *src, size_t dstsize)
+{
+#if defined(__APPLE__)
+ dst[0] = '\0';
+ if (ConvertEncodingToUTF8(src, dst, dstsize,
+ kCFStringEncodingDOSJapanese,
+ kCFStringEncodingUseHFSPlusCanonical) == 0)
+ return dst;
+#elif HAVE_ICONV
+ if (ConvertEncodingByIconv(src, dst, dstsize, "SJIS", "UTF-8") != -1)
+ return dst;
+#else
+ error("not support utf-8 conversion");
+#endif
+
+ if (dstsize < 1) return dst;
+ dst[dstsize-1] = 0;
+ return strncpy(dst, src, dstsize-1);
+}
+
+char *
+utf8_to_sjis(char *dst, const char *src, size_t dstsize)
+{
+#if defined(__APPLE__)
+ int srclen;
+
+ dst[0] = '\0';
+ srclen = strlen(src);
+ if (ConvertUTF8ToEncoding(src, srclen, dst, dstsize,
+ kCFStringEncodingDOSJapanese,
+ kCFStringEncodingUseHFSPlusCanonical) == 0)
+ return dst;
+#elif HAVE_ICONV
+ if (ConvertEncodingByIconv(src, dst, dstsize, "UTF-8", "SJIS") != -1)
+ return dst;
+#else
+ error("not support utf-8 conversion");
+#endif
+
+ if (dstsize < 1) return dst;
+ dst[dstsize-1] = 0;
+ return strncpy(dst, src, dstsize-1);
+}
+
+/*
+ * SJIS <-> EUC ÊÑ´¹´Ø¿ô
+ * ¡ÖÆüËܸì¾ðÊó½èÍý¡× ¥½¥Õ¥È¥Ð¥ó¥¯(³ô)
+ * ¤è¤êÈ´¿è(by Koji Arai)
+ */
+void
+euc2sjis(int *p1, int *p2)
+{
+ unsigned char c1 = *p1 & 0x7f;
+ unsigned char c2 = *p2 & 0x7f;
+ int rowoff = c1 < 0x5f ? 0x70 : 0xb0;
+ int celoff = c1 % 2 ? (c2 > 0x5f ? 0x20 : 0x1f) : 0x7e;
+ *p1 = ((c1 + 1) >> 1) + rowoff;
+ *p2 += celoff - 0x80;
+}
+
+void
+sjis2euc(int *p1, int *p2)
+{
+ unsigned char c1 = *p1;
+ unsigned char c2 = *p2;
+ int adjust = c2 < 0x9f;
+ int rowoff = c1 < 0xa0 ? 0x70 : 0xb0;
+ int celoff = adjust ? (c2 > 0x7f ? 0x20 : 0x1f) : 0x7e;
+ *p1 = ((c1 - rowoff) << 1) - adjust;
+ *p2 -= celoff;
+
+ *p1 |= 0x80;
+ *p2 |= 0x80;
+}
+
+static int
+hex2int(int c)
+{
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return c - '0';
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return c - 'a' + 10;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return c - 'A' + 10;
+ default:
+ return -1;
+ }
+}
+
+static int
+int2hex(int c)
+{
+ switch (c) {
+ case 0: case 1: case 2: case 3: case 4:
+ case 5: case 6: case 7: case 8: case 9:
+ return c + '0';
+
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ return c + 'a' - 10;
+
+ default:
+ return -1;
+ }
+}
+
+int
+cap_to_sjis(char *dst, const char *src, size_t dstsize)
+{
+ int i, j;
+ size_t len = strlen(src);
+ int a, b;
+
+ for (i = j = 0; i < len && i < dstsize; i++) {
+ if (src[i] != ':') {
+ dst[j++] = src[i];
+ continue;
+ }
+
+ i++;
+ a = hex2int((unsigned char)src[i]);
+ b = hex2int((unsigned char)src[i+1]);
+
+ if (a == -1 || b == -1) {
+ /* leave as it */
+ dst[j++] = ':';
+ strncpy(dst+j, src+i, dstsize-j);
+ dst[dstsize-1] = 0;
+ return strlen(dst);
+ }
+
+ i++;
+
+ dst[j++] = a * 16 + b;
+ }
+ dst[j] = 0;
+ return j;
+}
+
+int
+sjis_to_cap(char *dst, const char *src, size_t dstsize)
+{
+ int i, j;
+ size_t len = strlen(src);
+ int a, b;
+
+ for (i = j = 0; i < len && i < dstsize; i++) {
+ if (src[i] == ':') {
+ strncpy(dst+j, ":3a", dstsize-j);
+ dst[dstsize-1] = 0;
+ j = strlen(dst);
+ continue;
+ }
+ if (isprint(src[i])) {
+ dst[j++] = src[i];
+ continue;
+ }
+
+ if (j + 3 >= dstsize) {
+ dst[j] = 0;
+ return j;
+ }
+
+ a = int2hex((unsigned char)src[i] / 16);
+ b = int2hex((unsigned char)src[i] % 16);
+
+ dst[j++] = ':';
+ dst[j++] = a;
+ dst[j++] = b;
+ }
+ dst[j] = 0;
+ return j;
+}
+#endif /* MULTIBYTE_FILENAME */
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* huf.c -- new static Huffman */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14i Support LH7 & Bug Fixed 2000.10. 6 t.okamoto */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+extern char *malloc ();
+#endif
+
+/* ------------------------------------------------------------------------ */
+unsigned short left[2 * NC - 1], right[2 * NC - 1];
+
+unsigned short c_code[NC]; /* encode */
+unsigned short pt_code[NPT]; /* encode */
+
+unsigned short c_table[4096]; /* decode */
+unsigned short pt_table[256]; /* decode */
+
+unsigned short c_freq[2 * NC - 1]; /* encode */
+unsigned short p_freq[2 * NP - 1]; /* encode */
+unsigned short t_freq[2 * NT - 1]; /* encode */
+
+unsigned char c_len[NC];
+unsigned char pt_len[NPT];
+
+static unsigned char *buf; /* encode */
+static unsigned int bufsiz; /* encode */
+static unsigned short blocksize; /* decode */
+static unsigned short output_pos, output_mask; /* encode */
+
+static int pbit;
+static int np;
+/* ------------------------------------------------------------------------ */
+/* Encording */
+/* ------------------------------------------------------------------------ */
+static void
+count_t_freq(/*void*/)
+{
+ short i, k, n, count;
+
+ for (i = 0; i < NT; i++)
+ t_freq[i] = 0;
+ n = NC;
+ while (n > 0 && c_len[n - 1] == 0)
+ n--;
+ i = 0;
+ while (i < n) {
+ k = c_len[i++];
+ if (k == 0) {
+ count = 1;
+ while (i < n && c_len[i] == 0) {
+ i++;
+ count++;
+ }
+ if (count <= 2)
+ t_freq[0] += count;
+ else if (count <= 18)
+ t_freq[1]++;
+ else if (count == 19) {
+ t_freq[0]++;
+ t_freq[1]++;
+ }
+ else
+ t_freq[2]++;
+ } else
+ t_freq[k + 2]++;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+write_pt_len(n, nbit, i_special)
+ short n;
+ short nbit;
+ short i_special;
+{
+ short i, k;
+
+ while (n > 0 && pt_len[n - 1] == 0)
+ n--;
+ putbits(nbit, n);
+ i = 0;
+ while (i < n) {
+ k = pt_len[i++];
+ if (k <= 6)
+ putbits(3, k);
+ else
+ /* k=7 -> 1110 k=8 -> 11110 k=9 -> 111110 ... */
+ putbits(k - 3, USHRT_MAX << 1);
+ if (i == i_special) {
+ while (i < 6 && pt_len[i] == 0)
+ i++;
+ putbits(2, i - 3);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+write_c_len(/*void*/)
+{
+ short i, k, n, count;
+
+ n = NC;
+ while (n > 0 && c_len[n - 1] == 0)
+ n--;
+ putbits(CBIT, n);
+ i = 0;
+ while (i < n) {
+ k = c_len[i++];
+ if (k == 0) {
+ count = 1;
+ while (i < n && c_len[i] == 0) {
+ i++;
+ count++;
+ }
+ if (count <= 2) {
+ for (k = 0; k < count; k++)
+ putcode(pt_len[0], pt_code[0]);
+ }
+ else if (count <= 18) {
+ putcode(pt_len[1], pt_code[1]);
+ putbits(4, count - 3);
+ }
+ else if (count == 19) {
+ putcode(pt_len[0], pt_code[0]);
+ putcode(pt_len[1], pt_code[1]);
+ putbits(4, 15);
+ }
+ else {
+ putcode(pt_len[2], pt_code[2]);
+ putbits(CBIT, count - 20);
+ }
+ }
+ else
+ putcode(pt_len[k + 2], pt_code[k + 2]);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+encode_c(c)
+ short c;
+{
+ putcode(c_len[c], c_code[c]);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+encode_p(p)
+ unsigned short p;
+{
+ unsigned short c, q;
+
+ c = 0;
+ q = p;
+ while (q) {
+ q >>= 1;
+ c++;
+ }
+ putcode(pt_len[c], pt_code[c]);
+ if (c > 1)
+ putbits(c - 1, p);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+send_block( /* void */ )
+{
+ unsigned char flags;
+ unsigned short i, k, root, pos, size;
+
+ root = make_tree(NC, c_freq, c_len, c_code);
+ size = c_freq[root];
+ putbits(16, size);
+ if (root >= NC) {
+ count_t_freq();
+ root = make_tree(NT, t_freq, pt_len, pt_code);
+ if (root >= NT) {
+ write_pt_len(NT, TBIT, 3);
+ } else {
+ putbits(TBIT, 0);
+ putbits(TBIT, root);
+ }
+ write_c_len();
+ } else {
+ putbits(TBIT, 0);
+ putbits(TBIT, 0);
+ putbits(CBIT, 0);
+ putbits(CBIT, root);
+ }
+ root = make_tree(np, p_freq, pt_len, pt_code);
+ if (root >= np) {
+ write_pt_len(np, pbit, -1);
+ }
+ else {
+ putbits(pbit, 0);
+ putbits(pbit, root);
+ }
+ pos = 0;
+ for (i = 0; i < size; i++) {
+ if (i % CHAR_BIT == 0)
+ flags = buf[pos++];
+ else
+ flags <<= 1;
+ if (flags & (1 << (CHAR_BIT - 1))) {
+ encode_c(buf[pos++] + (1 << CHAR_BIT));
+ k = buf[pos++] << CHAR_BIT;
+ k += buf[pos++];
+ encode_p(k);
+ } else
+ encode_c(buf[pos++]);
+ if (unpackable)
+ return;
+ }
+ for (i = 0; i < NC; i++)
+ c_freq[i] = 0;
+ for (i = 0; i < np; i++)
+ p_freq[i] = 0;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+void
+output_st1(c, p)
+ unsigned short c;
+ unsigned short p;
+{
+ static unsigned short cpos;
+
+ output_mask >>= 1;
+ if (output_mask == 0) {
+ output_mask = 1 << (CHAR_BIT - 1);
+ if (output_pos >= bufsiz - 3 * CHAR_BIT) {
+ send_block();
+ if (unpackable)
+ return;
+ output_pos = 0;
+ }
+ cpos = output_pos++;
+ buf[cpos] = 0;
+ }
+ buf[output_pos++] = (unsigned char) c;
+ c_freq[c]++;
+ if (c >= (1 << CHAR_BIT)) {
+ buf[cpos] |= output_mask;
+ buf[output_pos++] = (unsigned char) (p >> CHAR_BIT);
+ buf[output_pos++] = (unsigned char) p;
+ c = 0;
+ while (p) {
+ p >>= 1;
+ c++;
+ }
+ p_freq[c]++;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+unsigned char *
+alloc_buf( /* void */ )
+{
+ bufsiz = 16 * 1024 *2; /* 65408U; */ /* t.okamoto */
+ while ((buf = (unsigned char *) malloc(bufsiz)) == NULL) {
+ bufsiz = (bufsiz / 10) * 9;
+ if (bufsiz < 4 * 1024)
+ fatal_error("Not enough memory");
+ }
+ return buf;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+void
+encode_start_st1( /* void */ )
+{
+ int i;
+
+ switch (dicbit) {
+ case LZHUFF4_DICBIT:
+ case LZHUFF5_DICBIT: pbit = 4; np = LZHUFF5_DICBIT + 1; break;
+ case LZHUFF6_DICBIT: pbit = 5; np = LZHUFF6_DICBIT + 1; break;
+ case LZHUFF7_DICBIT: pbit = 5; np = LZHUFF7_DICBIT + 1; break;
+ default:
+ fatal_error("Cannot use %d bytes dictionary", 1 << dicbit);
+ }
+
+ for (i = 0; i < NC; i++)
+ c_freq[i] = 0;
+ for (i = 0; i < np; i++)
+ p_freq[i] = 0;
+ output_pos = output_mask = 0;
+ init_putbits();
+ init_code_cache();
+ buf[0] = 0;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+void
+encode_end_st1( /* void */ )
+{
+ if (!unpackable) {
+ send_block();
+ putbits(CHAR_BIT - 1, 0); /* flush remaining bits */
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* decoding */
+/* ------------------------------------------------------------------------ */
+static void
+read_pt_len(nn, nbit, i_special)
+ short nn;
+ short nbit;
+ short i_special;
+{
+ int i, c, n;
+
+ n = getbits(nbit);
+ if (n == 0) {
+ c = getbits(nbit);
+ for (i = 0; i < nn; i++)
+ pt_len[i] = 0;
+ for (i = 0; i < 256; i++)
+ pt_table[i] = c;
+ }
+ else {
+ i = 0;
+ while (i < n) {
+ c = peekbits(3);
+ if (c != 7)
+ fillbuf(3);
+ else {
+ unsigned short mask = 1 << (16 - 4);
+ while (mask & bitbuf) {
+ mask >>= 1;
+ c++;
+ }
+ fillbuf(c - 3);
+ }
+
+ pt_len[i++] = c;
+ if (i == i_special) {
+ c = getbits(2);
+ while (--c >= 0)
+ pt_len[i++] = 0;
+ }
+ }
+ while (i < nn)
+ pt_len[i++] = 0;
+ make_table(nn, pt_len, 8, pt_table);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+read_c_len( /* void */ )
+{
+ short i, c, n;
+
+ n = getbits(CBIT);
+ if (n == 0) {
+ c = getbits(CBIT);
+ for (i = 0; i < NC; i++)
+ c_len[i] = 0;
+ for (i = 0; i < 4096; i++)
+ c_table[i] = c;
+ } else {
+ i = 0;
+ while (i < n) {
+ c = pt_table[peekbits(8)];
+ if (c >= NT) {
+ unsigned short mask = 1 << (16 - 9);
+ do {
+ if (bitbuf & mask)
+ c = right[c];
+ else
+ c = left[c];
+ mask >>= 1;
+ } while (c >= NT);
+ }
+ fillbuf(pt_len[c]);
+ if (c <= 2) {
+ if (c == 0)
+ c = 1;
+ else if (c == 1)
+ c = getbits(4) + 3;
+ else
+ c = getbits(CBIT) + 20;
+ while (--c >= 0)
+ c_len[i++] = 0;
+ }
+ else
+ c_len[i++] = c - 2;
+ }
+ while (i < NC)
+ c_len[i++] = 0;
+ make_table(NC, c_len, 12, c_table);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+unsigned short
+decode_c_st1( /*void*/ )
+{
+ unsigned short j, mask;
+
+ if (blocksize == 0) {
+ blocksize = getbits(16);
+ read_pt_len(NT, TBIT, 3);
+ read_c_len();
+ read_pt_len(np, pbit, -1);
+ }
+ blocksize--;
+ j = c_table[peekbits(12)];
+ if (j < NC)
+ fillbuf(c_len[j]);
+ else {
+ fillbuf(12);
+ mask = 1 << (16 - 1);
+ do {
+ if (bitbuf & mask)
+ j = right[j];
+ else
+ j = left[j];
+ mask >>= 1;
+ } while (j >= NC);
+ fillbuf(c_len[j] - 12);
+ }
+ return j;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+unsigned short
+decode_p_st1( /* void */ )
+{
+ unsigned short j, mask;
+
+ j = pt_table[peekbits(8)];
+ if (j < np)
+ fillbuf(pt_len[j]);
+ else {
+ fillbuf(8);
+ mask = 1 << (16 - 1);
+ do {
+ if (bitbuf & mask)
+ j = right[j];
+ else
+ j = left[j];
+ mask >>= 1;
+ } while (j >= np);
+ fillbuf(pt_len[j] - 8);
+ }
+ if (j != 0)
+ j = (1 << (j - 1)) + getbits(j - 1);
+ return j;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh4, 5, 6, 7 */
+void
+decode_start_st1( /* void */ )
+{
+ switch (dicbit) {
+ case LZHUFF4_DICBIT:
+ case LZHUFF5_DICBIT: pbit = 4; np = LZHUFF5_DICBIT + 1; break;
+ case LZHUFF6_DICBIT: pbit = 5; np = LZHUFF6_DICBIT + 1; break;
+ case LZHUFF7_DICBIT: pbit = 5; np = LZHUFF7_DICBIT + 1; break;
+ default:
+ fatal_error("Cannot use %d bytes dictionary", 1 << dicbit);
+ }
+
+ init_getbits();
+ init_code_cache();
+ blocksize = 0;
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* larc.c -- extra *.lzs */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+/* ------------------------------------------------------------------------ */
+static int flag, flagcnt, matchpos;
+/* ------------------------------------------------------------------------ */
+/* lzs */
+unsigned short
+decode_c_lzs( /*void*/ )
+{
+ if (getbits(1)) {
+ return getbits(8);
+ }
+ else {
+ matchpos = getbits(11);
+ return getbits(4) + 0x100;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* lzs */
+unsigned short
+decode_p_lzs( /*void*/ )
+{
+ return (loc - matchpos - MAGIC0) & 0x7ff;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lzs */
+void
+decode_start_lzs( /*void*/ )
+{
+ init_getbits();
+ init_code_cache();
+}
+
+/* ------------------------------------------------------------------------ */
+/* lz5 */
+unsigned short
+decode_c_lz5( /*void*/ )
+{
+ int c;
+
+ if (flagcnt == 0) {
+ flagcnt = 8;
+ flag = getc(infile);
+ }
+ flagcnt--;
+ c = getc(infile);
+ if ((flag & 1) == 0) {
+ matchpos = c;
+ c = getc(infile);
+ matchpos += (c & 0xf0) << 4;
+ c &= 0x0f;
+ c += 0x100;
+ }
+ flag >>= 1;
+ return c;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lz5 */
+unsigned short
+decode_p_lz5( /*void*/ )
+{
+ return (loc - matchpos - MAGIC5) & 0xfff;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lz5 */
+void
+decode_start_lz5( /*void*/ )
+{
+ int i;
+
+ flagcnt = 0;
+ for (i = 0; i < 256; i++)
+ memset(&text[i * 13 + 18], i, 13);
+ for (i = 0; i < 256; i++)
+ text[256 * 13 + 18 + i] = i;
+ for (i = 0; i < 256; i++)
+ text[256 * 13 + 256 + 18 + i] = 255 - i;
+ memset(&text[256 * 13 + 512 + 18], 0, 128);
+ memset(&text[256 * 13 + 512 + 128 + 18], ' ', 128 - 18);
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX Archiver Driver */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Soruce All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14i Modified and bug fixed 2000.10.06 t.okamoto */
+/* ------------------------------------------------------------------------ */
+/* Modified arton for LhaLib */
+/*
+ Included...
+ lharc.h interface.h slidehuf.h
+*/
+
+#include "ruby.h"
+
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+#include <signal.h>
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcmp(s1, s2, n) bcmp ((s1), (s2), (n))
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+
+#ifndef NULL
+#define NULL ((char *)0)
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if STDC_HEADERS
+# include <stdarg.h>
+# define va_init(a,b) va_start(a,b)
+#else
+# include <varargs.h>
+# define va_init(a,b) va_start(a)
+#endif
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+#if HAVE_GRP_H
+# include <grp.h>
+#endif
+
+#if !HAVE_SETUID
+typedef int uid_t;
+#endif
+#if !HAVE_SETGID
+typedef int gid_t;
+#endif
+
+#if !HAVE_UINT64_T
+# define HAVE_UINT64_T 1
+# if SIZEOF_LONG == 8
+ typedef unsigned long uint64_t;
+# elif _WIN32
+ typedef unsigned __int64 uint64_t;
+# elif HAVE_LONG_LONG
+ typedef unsigned long long uint64_t;
+# else
+# undef HAVE_UINT64_T
+# endif
+#endif
+
+#if _WIN32
+ #if !HAVE_SSIZE_T
+ typedef long ssize_t;
+ #endif
+#endif
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+#if !defined(_WIN32)
+ #if HAVE_UTIME_H
+ #include <utime.h>
+ #else
+ struct utimbuf {
+ time_t actime;
+ time_t modtime;
+ };
+ int utime(const char *, struct utimbuf *);
+ #endif
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# ifdef NONSYSTEM_DIR_LIBRARY /* no use ?? */
+# include "lhdir.h"
+# endif
+#endif
+
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#else
+int fnmatch(const char *pattern, const char *string, int flags);
+# define FNM_PATHNAME 1
+# define FNM_NOESCAPE 2
+# define FNM_PERIOD 4
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif /* SEEK_SET */
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#else
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef UCHAR_MAX
+#define UCHAR_MAX ((1<<(sizeof(unsigned char)*8))-1)
+#endif
+
+#ifndef USHRT_MAX
+#define USHRT_MAX ((1<<(sizeof(unsigned short)*8))-1)
+#endif
+
+#ifndef SHRT_MAX
+#define SHRT_MAX ((1<<(sizeof(short)*8-1))-1)
+#endif
+
+#ifndef SHRT_MIN
+#define SHRT_MIN (SHRT_MAX-USHRT_MAX)
+#endif
+
+#ifndef ULONG_MAX
+#define ULONG_MAX ((1<<(sizeof(unsigned long)*8))-1)
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX ((1<<(sizeof(long)*8-1))-1)
+#endif
+
+#ifndef LONG_MIN
+#define LONG_MIN (LONG_MAX-ULONG_MAX)
+#endif
+
+#endif /* HAVE_LIMITS_H */
+
+#if !HAVE_FSEEKO
+# define fseeko fseek
+#endif
+#if !HAVE_FTELLO
+# define ftello ftell
+#endif
+
+#if defined(NT)
+ #define WNT NT
+ #undef NT
+#endif
+
+#include "lha_macro.h"
+
+#define exit(n) lha_exit(n)
+
+struct encode_option {
+#if defined(__STDC__) || defined(AIX)
+ void (*output) ();
+ void (*encode_start) ();
+ void (*encode_end) ();
+#else
+ int (*output) ();
+ int (*encode_start) ();
+ int (*encode_end) ();
+#endif
+};
+
+struct decode_option {
+ unsigned short (*decode_c) ();
+ unsigned short (*decode_p) ();
+#if defined(__STDC__) || defined(AIX)
+ void (*decode_start) ();
+#else
+ int (*decode_start) ();
+#endif
+};
+
+/* ------------------------------------------------------------------------ */
+/* LHa File Type Definition */
+/* ------------------------------------------------------------------------ */
+#if !defined(_WIN32)
+typedef int boolean; /* TRUE or FALSE */
+#endif
+
+struct string_pool {
+ int used;
+ int size;
+ int n;
+ char *buffer;
+};
+
+typedef struct LzHeader {
+ size_t header_size;
+ int size_field_length;
+ char method[METHOD_TYPE_STORAGE];
+ size_t packed_size;
+ size_t original_size;
+ unsigned char attribute;
+ unsigned char header_level;
+ char name[FILENAME_LENGTH];
+ char realname[FILENAME_LENGTH];/* real name for symbolic link */
+ unsigned int crc; /* file CRC */
+ boolean has_crc; /* file CRC */
+ unsigned int header_crc; /* header CRC */
+ unsigned char extend_type;
+ unsigned char minor_version;
+
+ /* extend_type == EXTEND_UNIX and convert from other type. */
+ time_t unix_last_modified_stamp;
+ unsigned short unix_mode;
+ unsigned short unix_uid;
+ unsigned short unix_gid;
+ char user[256];
+ char group[256];
+} LzHeader;
+
+struct interfacing {
+ FILE *infile;
+ FILE *outfile;
+ size_t original;
+ size_t packed;
+ size_t read_size;
+ int dicbit;
+ int method;
+};
+
+
+typedef void addfile_handler(const LzHeader*);
+
+#define LHALIB_EXTERN extern
+
+LHALIB_EXTERN void cmd_extract(const char* archive_name,
+ addfile_handler hnd);
+
+#define fatal_error rb_fatal
+#define error rb_warn
+#define message rb_warn
+#define warning rb_warning
+
+LHALIB_EXTERN FILE * open_old_archive(const char* archive);
+LHALIB_EXTERN boolean need_file(const char*);
+LHALIB_EXTERN boolean archive_is_msdos_sfx1(const char*);
+LHALIB_EXTERN void output_dyn();
+LHALIB_EXTERN void encode_start_fix();
+LHALIB_EXTERN void encode_end_dyn();
+LHALIB_EXTERN void output_st1();
+LHALIB_EXTERN void encode_start_st0();
+LHALIB_EXTERN void encode_end_st0();
+LHALIB_EXTERN void encode_start_st1();
+LHALIB_EXTERN void encode_end_st1();
+LHALIB_EXTERN unsigned short decode_c_dyn();
+LHALIB_EXTERN unsigned short decode_p_dyn();
+LHALIB_EXTERN void decode_start_fix();
+LHALIB_EXTERN void decode_start_dyn();
+LHALIB_EXTERN void decode_start_st0();
+LHALIB_EXTERN void decode_start_st1();
+LHALIB_EXTERN unsigned short decode_c_st0();
+LHALIB_EXTERN unsigned short decode_c_st1();
+LHALIB_EXTERN unsigned short decode_p_st0();
+LHALIB_EXTERN unsigned short decode_p_st1();
+LHALIB_EXTERN unsigned short decode_c_lzs();
+LHALIB_EXTERN unsigned short decode_p_lzs();
+LHALIB_EXTERN void decode_start_lzs();
+LHALIB_EXTERN unsigned short decode_c_lz5();
+LHALIB_EXTERN unsigned short decode_p_lz5();
+LHALIB_EXTERN void decode_start_lz5();
+LHALIB_EXTERN void make_crctable();
+LHALIB_EXTERN size_t copyfile(FILE* f1, FILE* f2, size_t size, int text_flg, unsigned int* crcp);
+LHALIB_EXTERN void init_getbits();
+LHALIB_EXTERN void init_code_cache();
+LHALIB_EXTERN void putcode(unsigned char n, unsigned short x);
+LHALIB_EXTERN void putbits(unsigned char n, unsigned short x);
+LHALIB_EXTERN unsigned short getbits(unsigned char n);
+LHALIB_EXTERN void fillbuf(unsigned char n);
+LHALIB_EXTERN void encode_p_st0(unsigned short j);
+
+
+
+#define start_indicator(name, size, ing, len) 0
+#define finish_indicator(name, ed) 0
+
+#if defined(LHALIB_VERSION)
+ #define LHA_EXTERN
+#else
+ #define LHA_EXTERN LHALIB_EXTERN
+#endif
+
+LHALIB_EXTERN boolean lha_force;
+LHALIB_EXTERN boolean lha_verbose;
+LHALIB_EXTERN boolean lha_ignore_directory;
+LHALIB_EXTERN boolean extract_broken_archive;
+LHALIB_EXTERN boolean lha_noconvertcase;
+LHALIB_EXTERN boolean lha_generic_format;
+LHALIB_EXTERN boolean lha_text_mode;
+LHALIB_EXTERN int lha_n_max;
+LHALIB_EXTERN unsigned short lha_maxmatch;
+LHALIB_EXTERN int lha_overwrite;
+LHALIB_EXTERN unsigned short lha_c_freq[], lha_c_table[], lha_c_code[];
+LHALIB_EXTERN unsigned short lha_p_freq[], lha_pt_table[], lha_pt_code[], lha_t_freq[];
+LHALIB_EXTERN unsigned char lha_c_len[], lha_pt_len[];
+LHALIB_EXTERN unsigned short lha_left[], lha_right[];
+
+LHA_EXTERN unsigned short bitbuf;
+LHA_EXTERN unsigned short dicbit;
+LHA_EXTERN size_t lha_decode_count;
+LHA_EXTERN size_t lha_origsize;
+LHA_EXTERN size_t lha_compsize;
+LHA_EXTERN int lha_unpackable;
+LHA_EXTERN unsigned long lha_loc;
+LHA_EXTERN unsigned char* lha_text;
+LHA_EXTERN unsigned int crctable[UCHAR_MAX + 1];
+LHA_EXTERN FILE* infile;
+LHA_EXTERN FILE* outfile;
+LHA_EXTERN int archive_file_gid;
+LHA_EXTERN int archive_file_mode;
+LHA_EXTERN int header_level;
+
+#define force lha_force
+#define verbose lha_verbose
+#define verbose_listing lha_verbose
+#define ignore_directory lha_ignore_directory
+#define n_max lha_n_max
+#define maxmatch lha_maxmatch
+#define decode_count lha_decode_count
+#define unpackable lha_unpackable
+#define left lha_left
+#define right lha_right
+#define c_len lha_c_len
+#define pt_len lha_pt_len
+#define c_freq lha_c_freq
+#define c_table lha_c_table
+#define c_code lha_c_code
+#define p_freq lha_pt_freq
+#define pt_table lha_pt_table
+#define pt_code lha_pt_code
+#define t_freq lha_t_freq
+#define loc lha_loc
+#define text lha_text
+#define origsize lha_origsize
+#define compsize lha_compsize
+#define noconvertcase lha_noconvertcase
+#define generic_format lha_generic_format
+#define text_mode lha_text_mode
+#define overwrite lha_overwrite
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX Archiver Driver macro define */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Soruce All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14g modified 2000.05.06 T.OKAMOTO */
+/* ------------------------------------------------------------------------ */
+
+#define FALSE 0
+#define TRUE 1
+
+#define FILENAME_LENGTH 1024
+
+#if defined __MINGW32__
+# define getuid() 0
+# define chown(file, uid, gid) 0
+# define kill(pid, sig) 0
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* YOUR CUSTOMIZIES */
+/* ------------------------------------------------------------------------ */
+
+#ifndef ARCHIVENAME_EXTENTION
+#define ARCHIVENAME_EXTENTION ".lzh"
+#endif
+#ifndef BACKUPNAME_EXTENTION
+#define BACKUPNAME_EXTENTION ".bak"
+#endif
+
+#define SJIS_FIRST_P(c) \
+ (((unsigned char)(c) >= 0x80 && (unsigned char)(c) < 0xa0) || \
+ ((unsigned char)(c) >= 0xe0 && (unsigned char)(c) < 0xfd))
+#define SJIS_SECOND_P(c) \
+ (((unsigned char)(c) >= 0x40 && (unsigned char)(c) < 0xfd) && \
+ (unsigned char)(c) != 0x7f)
+
+#define X0201_KANA_P(c)\
+ (0xa0 < (unsigned char)(c) && (unsigned char)(c) < 0xe0)
+
+/* for filename conversion */
+#define NONE 0
+#define CODE_EUC 1
+#define CODE_SJIS 2
+#define CODE_UTF8 3
+#define CODE_CAP 4 /* Columbia AppleTalk Program */
+#define TO_LOWER 1
+#define TO_UPPER 2
+
+/* ------------------------------------------------------------------------ */
+/* LHa File Definitions */
+/* ------------------------------------------------------------------------ */
+#ifdef S_IFLNK
+#define GETSTAT lstat
+#else
+#define GETSTAT stat
+#endif
+
+#define LZHUFF0_METHOD "-lh0-"
+#define LZHUFF1_METHOD "-lh1-"
+#define LZHUFF2_METHOD "-lh2-"
+#define LZHUFF3_METHOD "-lh3-"
+#define LZHUFF4_METHOD "-lh4-"
+#define LZHUFF5_METHOD "-lh5-"
+#define LZHUFF6_METHOD "-lh6-"
+#define LZHUFF7_METHOD "-lh7-"
+#define LARC_METHOD "-lzs-"
+#define LARC5_METHOD "-lz5-"
+#define LARC4_METHOD "-lz4-"
+#define LZHDIRS_METHOD "-lhd-"
+
+#define METHOD_TYPE_STORAGE 5
+
+/* Added N.Watazaki ..V */
+#define LZHUFF0_METHOD_NUM 0
+#define LZHUFF1_METHOD_NUM 1
+#define LZHUFF2_METHOD_NUM 2
+#define LZHUFF3_METHOD_NUM 3
+#define LZHUFF4_METHOD_NUM 4
+#define LZHUFF5_METHOD_NUM 5
+#define LZHUFF6_METHOD_NUM 6
+#define LZHUFF7_METHOD_NUM 7
+#define LARC_METHOD_NUM 8
+#define LARC5_METHOD_NUM 9
+#define LARC4_METHOD_NUM 10
+#define LZHDIRS_METHOD_NUM 11
+/* Added N.Watazaki ..^ */
+
+#define LZHUFF0_DICBIT 0 /* no compress */
+#define LZHUFF1_DICBIT 12 /* 2^12 = 4KB sliding dictionary */
+#define LZHUFF2_DICBIT 13 /* 2^13 = 8KB sliding dictionary */
+#define LZHUFF3_DICBIT 13 /* 2^13 = 8KB sliding dictionary */
+#define LZHUFF4_DICBIT 12 /* 2^12 = 4KB sliding dictionary */
+#define LZHUFF5_DICBIT 13 /* 2^13 = 8KB sliding dictionary */
+#define LZHUFF6_DICBIT 15 /* 2^15 = 32KB sliding dictionary */
+#define LZHUFF7_DICBIT 16 /* 2^16 = 64KB sliding dictionary */
+#define LARC_DICBIT 11 /* 2^11 = 2KB sliding dictionary */
+#define LARC5_DICBIT 12 /* 2^12 = 4KB sliding dictionary */
+#define LARC4_DICBIT 0 /* no compress */
+
+#ifdef SUPPORT_LH7
+#define MAX_DICBIT LZHUFF7_DICBIT /* lh7 use 16bits */
+#endif
+#ifndef SUPPORT_LH7
+#define MAX_DICBIT LZHUFF6_DICBIT /* lh6 use 15bits */
+#endif
+
+#define MAX_DICSIZ (1L << MAX_DICBIT)
+
+#define EXTEND_GENERIC 0
+#define EXTEND_UNIX 'U'
+#define EXTEND_MSDOS 'M'
+#define EXTEND_MACOS 'm'
+#define EXTEND_OS9 '9'
+#define EXTEND_OS2 '2'
+#define EXTEND_OS68K 'K'
+#define EXTEND_OS386 '3' /* OS-9000??? */
+#define EXTEND_HUMAN 'H'
+#define EXTEND_CPM 'C'
+#define EXTEND_FLEX 'F'
+#define EXTEND_RUNSER 'R'
+
+/* this OS type is not official */
+
+#define EXTEND_TOWNSOS 'T'
+#define EXTEND_XOSK 'X' /* OS-9 for X68000 (?) */
+#define EXTEND_JAVA 'J'
+
+/*---------------------------------------------------------------------------*/
+
+#define GENERIC_ATTRIBUTE 0x20
+#define GENERIC_DIRECTORY_ATTRIBUTE 0x10
+
+#define CURRENT_UNIX_MINOR_VERSION 0x00
+
+#define LHA_PATHSEP 0xff /* path separator of the
+ filename in lha header.
+ it should compare with
+ `unsigned char' or `int',
+ that is not '\xff', but 0xff. */
+
+#define OSK_RW_RW_RW 0000033
+#define OSK_FILE_REGULAR 0000000
+#define OSK_DIRECTORY_PERM 0000200
+#define OSK_SHARED_PERM 0000100
+#define OSK_OTHER_EXEC_PERM 0000040
+#define OSK_OTHER_WRITE_PERM 0000020
+#define OSK_OTHER_READ_PERM 0000010
+#define OSK_OWNER_EXEC_PERM 0000004
+#define OSK_OWNER_WRITE_PERM 0000002
+#define OSK_OWNER_READ_PERM 0000001
+
+#define UNIX_FILE_TYPEMASK 0170000
+#define UNIX_FILE_REGULAR 0100000
+#define UNIX_FILE_DIRECTORY 0040000
+#define UNIX_FILE_SYMLINK 0120000
+#define UNIX_SETUID 0004000
+#define UNIX_SETGID 0002000
+#define UNIX_STICKYBIT 0001000
+#define UNIX_OWNER_READ_PERM 0000400
+#define UNIX_OWNER_WRITE_PERM 0000200
+#define UNIX_OWNER_EXEC_PERM 0000100
+#define UNIX_GROUP_READ_PERM 0000040
+#define UNIX_GROUP_WRITE_PERM 0000020
+#define UNIX_GROUP_EXEC_PERM 0000010
+#define UNIX_OTHER_READ_PERM 0000004
+#define UNIX_OTHER_WRITE_PERM 0000002
+#define UNIX_OTHER_EXEC_PERM 0000001
+#define UNIX_RW_RW_RW 0000666
+
+#define LZHEADER_STORAGE 4096
+
+/* ------------------------------------------------------------------------ */
+/* FILE Attribute */
+/* ------------------------------------------------------------------------ */
+#define is_directory(statp) (((statp)->st_mode & S_IFMT) == S_IFDIR)
+#define is_symlink(statp) (((statp)->st_mode & S_IFMT) == S_IFLNK)
+#define is_regularfile(statp) (((statp)->st_mode & S_IFMT) == S_IFREG)
+
+#if 1 /* assume that fopen() will accepts "b" as binary mode on all systems. */
+#define WRITE_BINARY "wb"
+#define READ_BINARY "rb"
+#else
+#define WRITE_BINARY "w"
+#define READ_BINARY "r"
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* Individual macro define */
+/* ------------------------------------------------------------------------ */
+
+/* bitio.c */
+#define peekbits(n) (bitbuf >> (sizeof(bitbuf)*8 - (n)))
+
+/* crcio.c */
+#define CRCPOLY 0xA001 /* CRC-16 (x^16+x^15+x^2+1) */
+#define INITIALIZE_CRC(crc) ((crc) = 0)
+#define UPDATE_CRC(crc, c) \
+ (crctable[((crc) ^ (c)) & 0xFF] ^ ((crc) >> CHAR_BIT))
+
+/* dhuf.c */
+#define N_CHAR (256 + 60 - THRESHOLD + 1)
+#define TREESIZE_C (N_CHAR * 2)
+#define TREESIZE_P (128 * 2)
+#define TREESIZE (TREESIZE_C + TREESIZE_P)
+#define ROOT_C 0
+#define ROOT_P TREESIZE_C
+
+/* huf.c */
+#define USHRT_BIT 16 /* (CHAR_BIT * sizeof(ushort)) */
+#define NP (MAX_DICBIT + 1)
+#define NT (USHRT_BIT + 3)
+#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
+
+#define PBIT 5 /* smallest integer such that (1 << PBIT) > * NP */
+#define TBIT 5 /* smallest integer such that (1 << TBIT) > * NT */
+#define CBIT 9 /* smallest integer such that (1 << CBIT) > * NC */
+
+/* #if NT > NP #define NPT NT #else #define NPT NP #endif */
+#define NPT 0x80
+
+/* larc.c */
+#define MAGIC0 18
+#define MAGIC5 19
+
+/* lharc.c */
+#define CMD_UNKNOWN 0
+#define CMD_EXTRACT 1
+#define CMD_ADD 2
+#define CMD_LIST 3
+#define CMD_DELETE 4
+
+#define STREQU(a,b) (((a)[0] == (b)[0]) ? (strcmp ((a),(b)) == 0) : FALSE)
+
+/* shuf.c */
+#define N1 286 /* alphabet size */
+#define N2 (2 * N1 - 1) /* # of nodes in Huffman tree */
+#define EXTRABITS 8 /* >= log2(F-THRESHOLD+258-N1) */
+#define BUFBITS 16 /* >= log2(MAXBUF) */
+#define LENFIELD 4 /* bit size of length field for tree output */
+
+/* util.c */
+#define BUFFERSIZE 2048
+
+/* slide.c */
+/*
+#define PERCOLATE 1
+#define NIL 0
+#define HASH(p, c) ((p) + ((c) << hash1) + hash2)
+*/
+
+/* slide.c */
+#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */
+#define THRESHOLD 3 /* choose optimal value */
--- /dev/null
+/*
+ * LhaLib for Ruby
+ * Copyright(c) 2006 arton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * $Id:$
+ */
+
+#define LHALIB_VERSION "0.8.1"
+
+#include "lha.h"
+#include "st.h"
+
+static VALUE lhalib;
+
+boolean lha_force = FALSE;
+boolean lha_verbose = FALSE;
+boolean lha_ignore_directory = FALSE;
+boolean extract_broken_archive = FALSE;
+boolean lha_noconvertcase = FALSE;
+boolean lha_generic_format = FALSE;
+boolean lha_text_mode = FALSE;
+int overwrite = 1;
+int lha_n_max;
+unsigned short lha_maxmatch;
+unsigned short bitbuf;
+size_t lha_decode_count;
+int lha_unpackable;
+unsigned long lha_loc;
+unsigned char* lha_text;
+FILE* infile;
+FILE* outfile;
+int archive_file_gid;
+int archive_file_mode;
+
+static VALUE proc;
+static VALUE id_name;
+static VALUE id_permission;
+static VALUE id_original_size;
+static VALUE id_packed_size;
+static VALUE id_stamp;
+static int file_count;
+
+void lha_exit(int n)
+{
+ rb_raise(rb_eRuntimeError, "LhaLib invalid operation %d", n);
+}
+
+void add_file(const LzHeader* hdr)
+{
+ file_count++;
+ if (!NIL_P(proc))
+ {
+ VALUE hash = rb_hash_new();
+ rb_hash_aset(hash, ID2SYM(id_name), rb_str_new2(hdr->name));
+ rb_hash_aset(hash, ID2SYM(id_permission), INT2FIX(hdr->unix_mode));
+ rb_hash_aset(hash, ID2SYM(id_original_size), INT2FIX(hdr->original_size));
+ rb_hash_aset(hash, ID2SYM(id_packed_size), INT2FIX(hdr->packed_size));
+ rb_hash_aset(hash, ID2SYM(id_stamp), rb_time_new(hdr->unix_last_modified_stamp, 0));
+ rb_yield(hash);
+ }
+}
+
+static VALUE s_ex(int argc, VALUE* argv, VALUE self)
+{
+ VALUE file;
+ proc = Qnil;
+ rb_scan_args(argc, argv, "1&", &file, &proc);
+ rb_check_safe_str(file);
+ rb_secure(4);
+
+ file_count = 0;
+ make_crctable();
+ cmd_extract(StringValueCStr(file), add_file);
+ return INT2FIX(file_count);
+}
+
+
+/*
+ * Class initializer called by Ruby while requiring this library
+ */
+void Init_lhalib()
+{
+ header_level = 2;
+ id_name = rb_intern("name");
+ id_permission = rb_intern("permission");
+ id_original_size = rb_intern("original_size");
+ id_packed_size = rb_intern("packed_size");
+ id_stamp = rb_intern("stamp");
+
+ lhalib = rb_define_module("LhaLib");
+ rb_define_module_function(lhalib, "x", s_ex, -1);
+ rb_define_const(lhalib, "VERSION", rb_str_new2(LHALIB_VERSION));
+
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* lharc.c -- append to archive */
+/* */
+/* Copyright (C) MCMLXXXIX Yooichi.Tagawa */
+/* Modified Nobutaka Watazaki */
+/* Thanks to H.Yoshizaki. (MS-DOS LHarc) */
+/* */
+/* Ver. 0.00 Original 1988.05.23 Y.Tagawa */
+/* Ver. 0.01 Alpha Version (for 4.2BSD) 1989.05.28 Y.Tagawa */
+/* Ver. 0.02 Alpha Version Rel.2 1989.05.29 Y.Tagawa */
+/* Ver. 0.03 Release #3 Beta Version 1989.07.02 Y.Tagawa */
+/* Ver. 0.03a Debug 1989.07.03 Y.Tagawa */
+/* Ver. 0.03b Modified 1989.07.13 Y.Tagawa */
+/* Ver. 0.03c Debug (Thanks to void@rena.dit.junet) */
+/* 1989.08.09 Y.Tagawa */
+/* Ver. 0.03d Modified (quiet and verbose) 1989.09.14 Y.Tagawa */
+/* V1.00 Fixed 1989.09.22 Y.Tagawa */
+/* V1.01 Bug Fixed 1989.12.25 Y.Tagawa */
+/* */
+/* DOS-Version Original LHx V C2.01 (C) H.Yohizaki */
+/* */
+/* V2.00 UNIX Lharc + DOS LHx -> OSK LHx 1990.11.01 Momozou */
+/* V2.01 Minor Modified 1990.11.24 Momozou */
+/* */
+/* Ver. 0.02 LHx for UNIX 1991.11.18 M.Oki */
+/* Ver. 0.03 LHa for UNIX 1991.12.17 M.Oki */
+/* Ver. 0.04 LHa for UNIX beta version 1992.01.20 M.Oki */
+/* Ver. 1.00 LHa for UNIX Fixed 1992.03.19 M.Oki */
+/* */
+/* Ver. 1.10 for Symbolic Link 1993.06.25 N.Watazaki */
+/* Ver. 1.11 for Symbolic Link Bug Fixed 1993.08.18 N.Watazaki */
+/* Ver. 1.12 for File Date Check 1993.10.28 N.Watazaki */
+/* Ver. 1.13 Bug Fixed (Idicator calcurate) 1994.02.21 N.Watazaki */
+/* Ver. 1.13a Bug Fixed (Sym. Link delete) 1994.03.11 N.Watazaki */
+/* Ver. 1.13b Bug Fixed (Sym. Link delete) 1994.07.29 N.Watazaki */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14b,c Bug Fixed 1996.03.07 t.okamoto */
+/* Ver. 1.14d Version up 1997.01.12 t.okamoto */
+/* Ver. 1.14g Bug Fixed 2000.05.06 t.okamoto */
+/* Ver. 1.14i Modified 2000.10.06 t.okamoto */
+/* Modified arton for LhaLib */
+/* ------------------------------------------------------------------------ */
+
+#include "lha.h"
+
+static int cmd = CMD_UNKNOWN;
+static int error_occurred;
+
+/* ------------------------------------------------------------------------ */
+/* */
+/* ------------------------------------------------------------------------ */
+static boolean
+open_old_archive_1(name, v_fp)
+ char *name;
+ FILE **v_fp;
+{
+ FILE *fp;
+ struct stat stbuf;
+
+ if (stat(name, &stbuf) >= 0 &&
+ is_regularfile(&stbuf) &&
+ (fp = fopen(name, READ_BINARY)) != NULL) {
+ *v_fp = fp;
+ archive_file_gid = stbuf.st_gid;
+ archive_file_mode = stbuf.st_mode;
+ return TRUE;
+ }
+
+ *v_fp = NULL;
+ archive_file_gid = -1;
+ return FALSE;
+}
+
+/* ------------------------------------------------------------------------ */
+FILE *
+open_old_archive(const char* archive_name)
+{
+ FILE *fp;
+ char *p;
+ static char expanded_archive_name[FILENAME_LENGTH];
+
+ if (!strcmp(archive_name, "-")) {
+ if (cmd == CMD_EXTRACT || cmd == CMD_LIST) {
+#if __MINGW32__
+ setmode(fileno(stdin), O_BINARY);
+#endif
+ return stdin;
+ }
+ else
+ return NULL;
+ }
+ p = strrchr(archive_name, '.');
+ if (p) {
+ if (strcasecmp(".LZH", p) == 0
+ || strcasecmp(".LZS", p) == 0
+ || strcasecmp(".COM", p) == 0 /* DOS SFX */
+ || strcasecmp(".EXE", p) == 0
+ || strcasecmp(".X", p) == 0 /* HUMAN SFX */
+ || strcasecmp(".BAK", p) == 0) { /* for BackUp */
+ open_old_archive_1(archive_name, &fp);
+ return fp;
+ }
+ }
+
+ if (open_old_archive_1(archive_name, &fp))
+ return fp;
+ xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
+ "%s.lzh", archive_name);
+ if (open_old_archive_1(expanded_archive_name, &fp)) {
+ archive_name = expanded_archive_name;
+ return fp;
+ }
+ /*
+ * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
+ * expanded_archive_name; return NULL; }
+ */
+ xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
+ "%s.lzs", archive_name);
+ if (open_old_archive_1(expanded_archive_name, &fp)) {
+ archive_name = expanded_archive_name;
+ return fp;
+ }
+ return NULL;
+}
+
+boolean
+need_file(const char* name)
+{
+ return TRUE;
+}
+
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* lhext.c -- LHarc extract */
+/* */
+/* Copyright (C) MCMLXXXIX Yooichi.Tagawa */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 0.00 Original 1988.05.23 Y.Tagawa */
+/* Ver. 1.00 Fixed 1989.09.22 Y.Tagawa */
+/* Ver. 0.03 LHa for UNIX 1991.12.17 M.Oki */
+/* Ver. 1.12 LHa for UNIX 1993.10.01 N.Watazaki */
+/* Ver. 1.13b Symbolic Link Update Bug Fix 1994.06.21 N.Watazaki */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14e bugfix 1999.04.30 T.Okamoto */
+/* Modified arton for LhaLib */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+/* ------------------------------------------------------------------------ */
+static int skip_flg = FALSE; /* FALSE..No Skip , TRUE..Skip */
+static char *methods[] =
+{
+ LZHUFF0_METHOD, LZHUFF1_METHOD, LZHUFF2_METHOD, LZHUFF3_METHOD,
+ LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
+ LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
+ LZHDIRS_METHOD,
+ NULL
+};
+
+static void add_dirinfo(char* name, LzHeader* hdr);
+static void adjust_dirinfo();
+
+/* ------------------------------------------------------------------------ */
+static boolean
+inquire_extract(name)
+ char *name;
+{
+ struct stat stbuf;
+
+ skip_flg = FALSE;
+ if (stat(name, &stbuf) >= 0) {
+ if (!is_regularfile(&stbuf)) {
+ error("\"%s\" already exists (not a file)", name);
+ return FALSE;
+ }
+
+ if (!force) {
+ if (!isatty(0)) {
+ warning("skip to extract %s.", name);
+ return FALSE;
+ }
+
+ switch (overwrite) {
+ case 0:
+ case 1:/* Y/y */
+ break;
+ case 2:
+ case 3:/* N/n */
+ case 8:/* Return */
+ return FALSE;
+ case 4:
+ case 5:/* A/a */
+ force = TRUE;
+ break;
+ case 6:
+ case 7:/* S/s */
+ skip_flg = TRUE;
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* ------------------------------------------------------------------------ */
+static boolean
+make_parent_path(name)
+ char *name;
+{
+ char path[FILENAME_LENGTH];
+ struct stat stbuf;
+ register char *p;
+
+ /* make parent directory name into PATH for recursive call */
+ str_safe_copy(path, name, sizeof(path));
+ for (p = path + strlen(path); p > path; p--)
+ if (p[-1] == '/') {
+ *--p = '\0';
+ break;
+ }
+
+ if (p == path) {
+ message("invalid path name \"%s\"", name);
+ return FALSE; /* no more parent. */
+ }
+
+ if (GETSTAT(path, &stbuf) >= 0) {
+ if (is_directory(&stbuf))
+ return TRUE;
+ }
+
+ if (verbose)
+ message("Making directory \"%s\".", path);
+
+#if defined __MINGW32__
+ if (mkdir(path) >= 0)
+ return TRUE;
+#else
+ if (mkdir(path, 0777) >= 0) /* try */
+ return TRUE; /* successful done. */
+#endif
+
+ if (!make_parent_path(path))
+ return FALSE;
+
+#if defined __MINGW32__
+ if (mkdir(path) < 0) { /* try again */
+ error("Cannot make directory \"%s\"", path);
+ return FALSE;
+ }
+#else
+ if (mkdir(path, 0777) < 0) { /* try again */
+ error("Cannot make directory \"%s\"", path);
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+/* ------------------------------------------------------------------------ */
+static FILE *
+open_with_make_path(name)
+ char *name;
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
+ if (!make_parent_path(name) ||
+ (fp = fopen(name, WRITE_BINARY)) == NULL)
+ error("Cannot extract a file \"%s\"", name);
+ }
+ return fp;
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+adjust_info(name, hdr)
+ char *name;
+ LzHeader *hdr;
+{
+ struct utimbuf utimebuf;
+
+ /* adjust file stamp */
+ utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
+
+ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
+ utime(name, &utimebuf);
+
+ if (hdr->extend_type == EXTEND_UNIX
+ || hdr->extend_type == EXTEND_OS68K
+ || hdr->extend_type == EXTEND_XOSK) {
+#ifdef NOT_COMPATIBLE_MODE
+ Please need your modification in this space.
+#else
+ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
+ chmod(name, hdr->unix_mode);
+#endif
+ if (!getuid()){
+ uid_t uid = hdr->unix_uid;
+ gid_t gid = hdr->unix_gid;
+
+#if HAVE_GETPWNAM && HAVE_GETGRNAM
+ if (hdr->user[0]) {
+ struct passwd *ent = getpwnam(hdr->user);
+ if (ent) uid = ent->pw_uid;
+ }
+ if (hdr->group[0]) {
+ struct group *ent = getgrnam(hdr->group);
+ if (ent) gid = ent->gr_gid;
+ }
+#endif
+
+#if HAVE_LCHOWN
+ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
+ lchown(name, uid, gid);
+ else
+#endif /* HAVE_LCHWON */
+ chown(name, uid, gid);
+ }
+ }
+#if __CYGWIN__
+ else {
+ /* On Cygwin, execute permission should be set for .exe or .dll. */
+ mode_t m;
+
+ umask(m = umask(0)); /* get current umask */
+ chmod(name, 0777 & ~m);
+ }
+#endif
+}
+
+/* ------------------------------------------------------------------------ */
+static size_t
+extract_one(FILE* afp, LzHeader* hdr, addfile_handler hnd)
+{
+ FILE *fp; /* output file */
+ struct stat stbuf;
+ char name[FILENAME_LENGTH];
+ unsigned int crc;
+ int method;
+ boolean up_flag;
+ char *q = hdr->name, c;
+ size_t read_size = 0;
+
+ if (ignore_directory && strrchr(hdr->name, '/')) {
+ q = (char *) strrchr(hdr->name, '/') + 1;
+ }
+ else {
+ if (is_directory_traversal(q)) {
+ fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q);
+ exit(111);
+ }
+
+ if (*q == '/') {
+ while (*q == '/') { q++; }
+
+ /*
+ * if OSK then strip device name
+ */
+ if (hdr->extend_type == EXTEND_OS68K
+ || hdr->extend_type == EXTEND_XOSK) {
+ do
+ c = (*q++);
+ while (c && c != '/');
+ if (!c || !*q)
+ q = "."; /* if device name only */
+ }
+ }
+ }
+
+ str_safe_copy(name, q, sizeof(name));
+
+ /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
+ /* 1999.4.30 t.okamoto */
+ for (method = 0;; method++) {
+ if (methods[method] == NULL) {
+ error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
+ 5, hdr->method, name);
+ return read_size;
+ }
+ if (memcmp(hdr->method, methods[method], 5) == 0)
+ break;
+ }
+
+ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
+ && method != LZHDIRS_METHOD_NUM) {
+ extract_regular:
+
+ if (skip_flg == FALSE) {
+ up_flag = inquire_extract(name);
+ if (up_flag == FALSE && force == FALSE) {
+ return read_size;
+ }
+ }
+
+ if (skip_flg == TRUE) { /* if skip_flg */
+ if (stat(name, &stbuf) == 0 && force != TRUE) {
+ if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
+ return read_size;
+ }
+ }
+ }
+
+ unlink(name);
+ if ((fp = open_with_make_path(name)) != NULL) {
+ crc = decode_lzhuf(afp, fp,
+ hdr->original_size, hdr->packed_size,
+ name, method, &read_size);
+ fclose(fp);
+ }
+ if (!fp)
+ return read_size;
+
+
+ if (hdr->has_crc && crc != hdr->crc)
+ error("CRC error: \"%s\"", name);
+ }
+ else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
+ || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
+ || method == LZHDIRS_METHOD_NUM) {
+ /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
+ if (!ignore_directory) {
+ /* NAME has trailing SLASH '/', (^_^) */
+ if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
+ int l_code;
+
+#ifdef S_IFLNK
+ if (skip_flg == FALSE) {
+ up_flag = inquire_extract(name);
+ if (up_flag == FALSE && force == FALSE) {
+ return read_size;
+ }
+ } else {
+ if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
+ if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
+ return read_size;
+ }
+ }
+ }
+
+ unlink(name);
+ make_parent_path(name);
+ l_code = symlink(hdr->realname, name);
+ if (l_code < 0) {
+ warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
+ name, hdr->realname);
+ }
+#else
+ warning("Can't make Symbolic Link %s -> %s",
+ name, hdr->realname);
+ return read_size;
+#endif
+ } else { /* make directory */
+ if (!make_parent_path(name))
+ return read_size;
+ /* save directory information */
+ add_dirinfo(name, hdr);
+ }
+ }
+ }
+ else {
+ if (force) /* force extract */
+ goto extract_regular;
+ else
+ error("Unknown file type: \"%s\". use `f' option to force extract.", name);
+ }
+
+ adjust_info(name, hdr);
+
+ return read_size;
+}
+
+/* ------------------------------------------------------------------------ */
+/* EXTRACT COMMAND MAIN */
+/* ------------------------------------------------------------------------ */
+void
+cmd_extract(const char* archive_name, addfile_handler hnd)
+{
+ LzHeader hdr;
+ off_t pos;
+ FILE *afp;
+ size_t read_size;
+
+ /* open archive file */
+ if ((afp = open_old_archive(archive_name)) == NULL)
+ fatal_error("Cannot open archive file \"%s\"", archive_name);
+
+ if (archive_is_msdos_sfx1(archive_name))
+ seek_lha_header(afp);
+
+ /* extract each files */
+ while (get_header(afp, &hdr)) {
+ if (need_file(hdr.name)) {
+ pos = ftello(afp);
+ read_size = extract_one(afp, &hdr, hnd);
+ if (read_size != hdr.packed_size) {
+ /* when error occurred in extract_one(), should adjust
+ point of file stream */
+ if (pos != -1 && afp != stdin)
+ fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
+ else {
+ size_t i = hdr.packed_size - read_size;
+ while (i--) fgetc(afp);
+ }
+ } else {
+ hnd(&hdr);
+ }
+ } else {
+ if (afp != stdin)
+ fseeko(afp, hdr.packed_size, SEEK_CUR);
+ else {
+ size_t i = hdr.packed_size;
+ while (i--) fgetc(afp);
+ }
+ }
+ }
+
+ /* close archive file */
+ fclose(afp);
+
+ /* adjust directory information */
+ adjust_dirinfo();
+
+ return;
+}
+
+int
+is_directory_traversal(char *path)
+{
+ int state = 0;
+
+ for (; *path; path++) {
+ switch (state) {
+ case 0:
+ if (*path == '.') state = 1;
+ else state = 3;
+ break;
+ case 1:
+ if (*path == '.') state = 2;
+ else if (*path == '/') state = 0;
+ else state = 3;
+ break;
+ case 2:
+ if (*path == '/') return 1;
+ else state = 3;
+ break;
+ case 3:
+ if (*path == '/') state = 0;
+ break;
+ }
+ }
+
+ return state == 2;
+}
+
+/*
+ * restore directory information (time stamp).
+ * added by A.Iriyama 2003.12.12
+ */
+
+typedef struct lhdDirectoryInfo_t {
+ struct lhdDirectoryInfo_t *next;
+ LzHeader hdr;
+} LzHeaderList;
+
+static LzHeaderList *dirinfo;
+
+static void add_dirinfo(char *name, LzHeader *hdr)
+{
+ LzHeaderList *p;
+
+ if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
+ return;
+
+ p = xmalloc(sizeof(LzHeaderList));
+
+ memcpy(&p->hdr, hdr, sizeof(LzHeader));
+ strncpy(p->hdr.name, name, sizeof(p->hdr.name));
+ p->hdr.name[sizeof(p->hdr.name)-1] = 0;
+
+ {
+ LzHeaderList *tmp = dirinfo;
+ dirinfo = p;
+ dirinfo->next = tmp;
+ }
+}
+
+static void adjust_dirinfo()
+{
+ while (dirinfo) {
+ adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
+
+ {
+ LzHeaderList *tmp = dirinfo;
+ dirinfo = dirinfo->next;
+ free(tmp);
+ }
+ }
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* maketbl.c -- makes decoding table */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+void
+make_table(nchar, bitlen, tablebits, table)
+ short nchar;
+ unsigned char bitlen[];
+ short tablebits;
+ unsigned short table[];
+{
+ unsigned short count[17]; /* count of bitlen */
+ unsigned short weight[17]; /* 0x10000ul >> bitlen */
+ unsigned short start[17]; /* first code of bitlen */
+ unsigned short total;
+ unsigned int i, l;
+ int j, k, m, n, avail;
+ unsigned short *p;
+
+ avail = nchar;
+
+ /* initialize */
+ for (i = 1; i <= 16; i++) {
+ count[i] = 0;
+ weight[i] = 1 << (16 - i);
+ }
+
+ /* count */
+ for (i = 0; i < nchar; i++)
+ count[bitlen[i]]++;
+
+ /* calculate first code */
+ total = 0;
+ for (i = 1; i <= 16; i++) {
+ start[i] = total;
+ total += weight[i] * count[i];
+ }
+ if ((total & 0xffff) != 0)
+ error("make_table(): Bad table (5)");
+
+ /* shift data for make table. */
+ m = 16 - tablebits;
+ for (i = 1; i <= tablebits; i++) {
+ start[i] >>= m;
+ weight[i] >>= m;
+ }
+
+ /* initialize */
+ j = start[tablebits + 1] >> m;
+ k = 1 << tablebits;
+ if (j != 0)
+ for (i = j; i < k; i++)
+ table[i] = 0;
+
+ /* create table and tree */
+ for (j = 0; j < nchar; j++) {
+ k = bitlen[j];
+ if (k == 0)
+ continue;
+ l = start[k] + weight[k];
+ if (k <= tablebits) {
+ /* code in table */
+ for (i = start[k]; i < l; i++)
+ table[i] = j;
+ }
+ else {
+ /* code not in table */
+ p = &table[(i = start[k]) >> m];
+ i <<= tablebits;
+ n = k - tablebits;
+ /* make tree (n length) */
+ while (--n >= 0) {
+ if (*p == 0) {
+ right[avail] = left[avail] = 0;
+ *p = avail++;
+ }
+ if (i & 0x8000)
+ p = &right[*p];
+ else
+ p = &left[*p];
+ i <<= 1;
+ }
+ *p = j;
+ }
+ start[k] = l;
+ }
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* maketree.c -- make Huffman tree */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+static void
+make_code(nchar, bitlen, code, leaf_num)
+ int nchar;
+ unsigned char *bitlen;
+ unsigned short *code; /* table */
+ unsigned short *leaf_num;
+{
+ unsigned short weight[17]; /* 0x10000ul >> bitlen */
+ unsigned short start[17]; /* start code */
+ unsigned short total;
+ int i;
+ int c;
+
+ total = 0;
+ for (i = 1; i <= 16; i++) {
+ start[i] = total;
+ weight[i] = 1 << (16 - i);
+ total += weight[i] * leaf_num[i];
+ }
+ for (c = 0; c < nchar; c++) {
+ i = bitlen[c];
+ code[c] = start[i];
+ start[i] += weight[i];
+ }
+}
+
+static void
+count_leaf(node, nchar, leaf_num, depth) /* call with node = root */
+ int node;
+ int nchar;
+ unsigned short leaf_num[];
+ int depth;
+{
+ if (node < nchar)
+ leaf_num[depth < 16 ? depth : 16]++;
+ else {
+ count_leaf(left[node], nchar, leaf_num, depth + 1);
+ count_leaf(right[node], nchar, leaf_num, depth + 1);
+ }
+}
+
+static void
+make_len(nchar, bitlen, sort, leaf_num)
+ int nchar;
+ unsigned char *bitlen;
+ unsigned short *sort; /* sorted characters */
+ unsigned short *leaf_num;
+{
+ int i, k;
+ unsigned int cum;
+
+ cum = 0;
+ for (i = 16; i > 0; i--) {
+ cum += leaf_num[i] << (16 - i);
+ }
+#if (UINT_MAX != 0xffff)
+ cum &= 0xffff;
+#endif
+ /* adjust len */
+ if (cum) {
+ leaf_num[16] -= cum; /* always leaf_num[16] > cum */
+ do {
+ for (i = 15; i > 0; i--) {
+ if (leaf_num[i]) {
+ leaf_num[i]--;
+ leaf_num[i + 1] += 2;
+ break;
+ }
+ }
+ } while (--cum);
+ }
+ /* make len */
+ for (i = 16; i > 0; i--) {
+ k = leaf_num[i];
+ while (k > 0) {
+ bitlen[*sort++] = i;
+ k--;
+ }
+ }
+}
+
+/* priority queue; send i-th entry down heap */
+static void
+downheap(i, heap, heapsize, freq)
+ int i;
+ short *heap;
+ size_t heapsize;
+ unsigned short *freq;
+{
+ short j, k;
+
+ k = heap[i];
+ while ((j = 2 * i) <= heapsize) {
+ if (j < heapsize && freq[heap[j]] > freq[heap[j + 1]])
+ j++;
+ if (freq[k] <= freq[heap[j]])
+ break;
+ heap[i] = heap[j];
+ i = j;
+ }
+ heap[i] = k;
+}
+
+/* make tree, calculate bitlen[], return root */
+short
+make_tree(nchar, freq, bitlen, code)
+ int nchar;
+ unsigned short *freq;
+ unsigned char *bitlen;
+ unsigned short *code;
+{
+ short i, j, avail, root;
+ unsigned short *sort;
+
+ short heap[NC + 1]; /* NC >= nchar */
+ size_t heapsize;
+
+ avail = nchar;
+ heapsize = 0;
+ heap[1] = 0;
+ for (i = 0; i < nchar; i++) {
+ bitlen[i] = 0;
+ if (freq[i])
+ heap[++heapsize] = i;
+ }
+ if (heapsize < 2) {
+ code[heap[1]] = 0;
+ return heap[1];
+ }
+
+ /* make priority queue */
+ for (i = heapsize / 2; i >= 1; i--)
+ downheap(i, heap, heapsize, freq);
+
+ /* make huffman tree */
+ sort = code;
+ do { /* while queue has at least two entries */
+ i = heap[1]; /* take out least-freq entry */
+ if (i < nchar)
+ *sort++ = i;
+ heap[1] = heap[heapsize--];
+ downheap(1, heap, heapsize, freq);
+ j = heap[1]; /* next least-freq entry */
+ if (j < nchar)
+ *sort++ = j;
+ root = avail++; /* generate new node */
+ freq[root] = freq[i] + freq[j];
+ heap[1] = root;
+ downheap(1, heap, heapsize, freq); /* put into queue */
+ left[root] = i;
+ right[root] = j;
+ } while (heapsize > 1);
+
+ {
+ unsigned short leaf_num[17];
+
+ /* make leaf_num */
+ memset(leaf_num, 0, sizeof(leaf_num));
+ count_leaf(root, nchar, leaf_num, 0);
+
+ /* make bitlen */
+ make_len(nchar, bitlen, code, leaf_num);
+
+ /* make code table */
+ make_code(nchar, bitlen, code, leaf_num);
+ }
+
+ return root;
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* shuf.c -- extract static Huffman coding */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+
+/* ------------------------------------------------------------------------ */
+#undef NP
+#undef NP2
+
+#define NP (8 * 1024 / 64)
+#define NP2 (NP * 2 - 1)
+/* ------------------------------------------------------------------------ */
+static unsigned int np;
+int fixed[2][16] = {
+ {3, 0x01, 0x04, 0x0c, 0x18, 0x30, 0}, /* old compatible */
+ {2, 0x01, 0x01, 0x03, 0x06, 0x0D, 0x1F, 0x4E, 0} /* 8K buf */
+};
+/* ------------------------------------------------------------------------ */
+/* lh3 */
+void
+decode_start_st0( /*void*/ )
+{
+ n_max = 286;
+ maxmatch = MAXMATCH;
+ init_getbits();
+ init_code_cache();
+ np = 1 << (LZHUFF3_DICBIT - 6);
+}
+
+/* ------------------------------------------------------------------------ */
+void
+encode_p_st0(unsigned short j)
+{
+ unsigned short i;
+
+ i = j >> 6;
+ putcode(pt_len[i], pt_code[i]);
+ putbits(6, j & 0x3f);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+ready_made(method)
+ int method;
+{
+ int i, j;
+ unsigned int code, weight;
+ int *tbl;
+
+ tbl = fixed[method];
+ j = *tbl++;
+ weight = 1 << (16 - j);
+ code = 0;
+ for (i = 0; i < np; i++) {
+ while (*tbl == i) {
+ j++;
+ tbl++;
+ weight >>= 1;
+ }
+ pt_len[i] = j;
+ pt_code[i] = code;
+ code += weight;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1 */
+void
+encode_start_fix( /*void*/ )
+{
+ n_max = 314;
+ maxmatch = 60;
+ np = 1 << (12 - 6);
+ init_putbits();
+ init_code_cache();
+ start_c_dyn();
+ ready_made(0);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+read_tree_c( /*void*/ )
+{ /* read tree from file */
+ int i, c;
+
+ i = 0;
+ while (i < N1) {
+ if (getbits(1))
+ c_len[i] = getbits(LENFIELD) + 1;
+ else
+ c_len[i] = 0;
+ if (++i == 3 && c_len[0] == 1 && c_len[1] == 1 && c_len[2] == 1) {
+ c = getbits(CBIT);
+ for (i = 0; i < N1; i++)
+ c_len[i] = 0;
+ for (i = 0; i < 4096; i++)
+ c_table[i] = c;
+ return;
+ }
+ }
+ make_table(N1, c_len, 12, c_table);
+}
+
+/* ------------------------------------------------------------------------ */
+static void
+read_tree_p(/*void*/)
+{ /* read tree from file */
+ int i, c;
+
+ i = 0;
+ while (i < NP) {
+ pt_len[i] = getbits(LENFIELD);
+ if (++i == 3 && pt_len[0] == 1 && pt_len[1] == 1 && pt_len[2] == 1) {
+ c = getbits(LZHUFF3_DICBIT - 6);
+ for (i = 0; i < NP; i++)
+ pt_len[i] = 0;
+ for (i = 0; i < 256; i++)
+ pt_table[i] = c;
+ return;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1 */
+void
+decode_start_fix(/*void*/)
+{
+ n_max = 314;
+ maxmatch = 60;
+ init_getbits();
+ init_code_cache();
+ np = 1 << (LZHUFF1_DICBIT - 6);
+ start_c_dyn();
+ ready_made(0);
+ make_table(np, pt_len, 8, pt_table);
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh3 */
+unsigned short
+decode_c_st0(/*void*/)
+{
+ int i, j;
+ static unsigned short blocksize = 0;
+
+ if (blocksize == 0) { /* read block head */
+ blocksize = getbits(BUFBITS); /* read block blocksize */
+ read_tree_c();
+ if (getbits(1)) {
+ read_tree_p();
+ }
+ else {
+ ready_made(1);
+ }
+ make_table(NP, pt_len, 8, pt_table);
+ }
+ blocksize--;
+ j = c_table[peekbits(12)];
+ if (j < N1)
+ fillbuf(c_len[j]);
+ else {
+ fillbuf(12);
+ i = bitbuf;
+ do {
+ if ((short) i < 0)
+ j = right[j];
+ else
+ j = left[j];
+ i <<= 1;
+ } while (j >= N1);
+ fillbuf(c_len[j] - 12);
+ }
+ if (j == N1 - 1)
+ j += getbits(EXTRABITS);
+ return j;
+}
+
+/* ------------------------------------------------------------------------ */
+/* lh1, 3 */
+unsigned short
+decode_p_st0(/*void*/)
+{
+ int i, j;
+
+ j = pt_table[peekbits(8)];
+ if (j < np) {
+ fillbuf(pt_len[j]);
+ }
+ else {
+ fillbuf(8);
+ i = bitbuf;
+ do {
+ if ((short) i < 0)
+ j = right[j];
+ else
+ j = left[j];
+ i <<= 1;
+ } while (j >= np);
+ fillbuf(pt_len[j] - 8);
+ }
+ return (j << 6) + getbits(6);
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* slide.c -- sliding dictionary with percolating update */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14d Exchanging a search algorithm 1997.01.11 T.Okamoto */
+/* ------------------------------------------------------------------------ */
+
+#if 0
+#define DEBUG 1
+#endif
+
+#include "lha.h"
+
+#ifdef DEBUG
+FILE *fout = NULL;
+static int noslide = 1;
+#endif
+
+/* variables for hash */
+struct hash {
+ unsigned int pos;
+ int too_flag; /* if 1, matching candidate is too many */
+} *hash;
+static unsigned int *prev; /* previous posiion associated with hash */
+
+/* hash function: it represents 3 letters from `pos' on `text' */
+#define INIT_HASH(pos) \
+ ((( (text[(pos)] << 5) \
+ ^ text[(pos) + 1] ) << 5) \
+ ^ text[(pos) + 2] ) & (unsigned)(HSHSIZ - 1);
+#define NEXT_HASH(hash,pos) \
+ (((hash) << 5) \
+ ^ text[(pos) + 2] ) & (unsigned)(HSHSIZ - 1);
+
+static struct encode_option encode_define[2] = {
+#if defined(__STDC__) || defined(AIX)
+ /* lh1 */
+ {(void (*) ()) output_dyn,
+ (void (*) ()) encode_start_fix,
+ (void (*) ()) encode_end_dyn},
+ /* lh4, 5, 6, 7 */
+ {(void (*) ()) output_st1,
+ (void (*) ()) encode_start_st1,
+ (void (*) ()) encode_end_st1}
+#else
+ /* lh1 */
+ {(int (*) ()) output_dyn,
+ (int (*) ()) encode_start_fix,
+ (int (*) ()) encode_end_dyn},
+ /* lh4, 5, 6, 7 */
+ {(int (*) ()) output_st1,
+ (int (*) ()) encode_start_st1,
+ (int (*) ()) encode_end_st1}
+#endif
+};
+
+static struct decode_option decode_define[] = {
+ /* lh1 */
+ {decode_c_dyn, decode_p_st0, decode_start_fix},
+ /* lh2 */
+ {decode_c_dyn, decode_p_dyn, decode_start_dyn},
+ /* lh3 */
+ {decode_c_st0, decode_p_st0, decode_start_st0},
+ /* lh4 */
+ {decode_c_st1, decode_p_st1, decode_start_st1},
+ /* lh5 */
+ {decode_c_st1, decode_p_st1, decode_start_st1},
+ /* lh6 */
+ {decode_c_st1, decode_p_st1, decode_start_st1},
+ /* lh7 */
+ {decode_c_st1, decode_p_st1, decode_start_st1},
+ /* lzs */
+ {decode_c_lzs, decode_p_lzs, decode_start_lzs},
+ /* lz5 */
+ {decode_c_lz5, decode_p_lz5, decode_start_lz5}
+};
+
+static struct encode_option encode_set;
+static struct decode_option decode_set;
+
+#define TXTSIZ (MAX_DICSIZ * 2L + MAXMATCH)
+#define HSHSIZ (((unsigned long)1) <<15)
+#define NIL 0
+#define LIMIT 0x100 /* limit of hash chain */
+
+static unsigned int txtsiz;
+static unsigned long dicsiz;
+static unsigned int remainder;
+
+struct matchdata {
+ int len;
+ unsigned int off;
+};
+
+int
+encode_alloc(method)
+ int method;
+{
+ switch (method) {
+ case LZHUFF1_METHOD_NUM:
+ encode_set = encode_define[0];
+ maxmatch = 60;
+ dicbit = LZHUFF1_DICBIT; /* 12 bits Changed N.Watazaki */
+ break;
+ case LZHUFF5_METHOD_NUM:
+ encode_set = encode_define[1];
+ maxmatch = MAXMATCH;
+ dicbit = LZHUFF5_DICBIT; /* 13 bits */
+ break;
+ case LZHUFF6_METHOD_NUM:
+ encode_set = encode_define[1];
+ maxmatch = MAXMATCH;
+ dicbit = LZHUFF6_DICBIT; /* 15 bits */
+ break;
+ case LZHUFF7_METHOD_NUM:
+ encode_set = encode_define[1];
+ maxmatch = MAXMATCH;
+ dicbit = LZHUFF7_DICBIT; /* 16 bits */
+ break;
+ default:
+ error("unknown method %d", method);
+ exit(1);
+ }
+
+ dicsiz = (((unsigned long)1) << dicbit);
+ txtsiz = dicsiz*2+maxmatch;
+
+ if (hash) return method;
+
+ alloc_buf();
+
+ hash = (struct hash*)xmalloc(HSHSIZ * sizeof(struct hash));
+ prev = (unsigned int*)xmalloc(MAX_DICSIZ * sizeof(unsigned int));
+ text = (unsigned char*)xmalloc(TXTSIZ);
+
+ return method;
+}
+
+static void
+init_slide()
+{
+ unsigned int i;
+
+ for (i = 0; i < HSHSIZ; i++) {
+ hash[i].pos = NIL;
+ hash[i].too_flag = 0;
+ }
+}
+
+/* update dictionary */
+static void
+update_dict(pos, crc)
+ unsigned int *pos;
+ unsigned int *crc;
+{
+ unsigned int i, j;
+ long n;
+
+ memmove(&text[0], &text[dicsiz], txtsiz - dicsiz);
+
+ n = fread_crc(crc, &text[txtsiz - dicsiz], dicsiz, infile);
+
+ remainder += n;
+
+ *pos -= dicsiz;
+ for (i = 0; i < HSHSIZ; i++) {
+ j = hash[i].pos;
+ hash[i].pos = (j > dicsiz) ? j - dicsiz : NIL;
+ hash[i].too_flag = 0;
+ }
+ for (i = 0; i < dicsiz; i++) {
+ j = prev[i];
+ prev[i] = (j > dicsiz) ? j - dicsiz : NIL;
+ }
+}
+
+/* associate position with token */
+static void
+insert_hash(token, pos)
+ unsigned int token;
+ unsigned int pos;
+{
+ prev[pos & (dicsiz - 1)] = hash[token].pos; /* chain the previous pos. */
+ hash[token].pos = pos;
+}
+
+static void
+search_dict_1(token, pos, off, max, m)
+ unsigned int token;
+ unsigned int pos;
+ unsigned int off;
+ unsigned int max; /* max. length of matching string */
+ struct matchdata *m;
+{
+ unsigned int chain = 0;
+ unsigned int scan_pos = hash[token].pos;
+ int scan_beg = scan_pos - off;
+ int scan_end = pos - dicsiz;
+ unsigned int len;
+
+ while (scan_beg > scan_end) {
+ chain++;
+
+ if (text[scan_beg + m->len] == text[pos + m->len]) {
+ {
+ /* collate token */
+ unsigned char *a = &text[scan_beg];
+ unsigned char *b = &text[pos];
+
+ for (len = 0; len < max && *a++ == *b++; len++);
+ }
+
+ if (len > m->len) {
+ m->off = pos - scan_beg;
+ m->len = len;
+ if (m->len == max)
+ break;
+
+#ifdef DEBUG
+ if (noslide) {
+ if (pos - m->off < dicsiz) {
+ printf("matchpos=%u scan_pos=%u dicsiz=%u\n",
+ pos - m->off, scan_pos, dicsiz);
+ }
+ }
+#endif
+ }
+ }
+ scan_pos = prev[scan_pos & (dicsiz - 1)];
+ scan_beg = scan_pos - off;
+ }
+
+ if (chain >= LIMIT)
+ hash[token].too_flag = 1;
+}
+
+/* search the longest token matching to current token */
+static void
+search_dict(token, pos, min, m)
+ unsigned int token; /* search token */
+ unsigned int pos; /* position of token */
+ int min; /* min. length of matching string */
+ struct matchdata *m;
+{
+ unsigned int off, tok, max;
+
+ if (min < THRESHOLD - 1) min = THRESHOLD - 1;
+
+ max = maxmatch;
+ m->off = 0;
+ m->len = min;
+
+ off = 0;
+ for (tok = token; hash[tok].too_flag && off < maxmatch - THRESHOLD; ) {
+ /* If matching position is too many, The search key is
+ changed into following token from `off' (for speed). */
+ ++off;
+ tok = NEXT_HASH(tok, pos+off);
+ }
+ if (off == maxmatch - THRESHOLD) {
+ off = 0;
+ tok = token;
+ }
+
+ search_dict_1(tok, pos, off, max, m);
+
+ if (off > 0 && m->len < off + 3)
+ /* re-search */
+ search_dict_1(token, pos, 0, off+2, m);
+
+ if (m->len > remainder) m->len = remainder;
+}
+
+/* slide dictionary */
+static void
+next_token(token, pos, crc)
+ unsigned int *token;
+ unsigned int *pos;
+ unsigned int *crc;
+{
+ remainder--;
+ if (++*pos >= txtsiz - maxmatch) {
+ update_dict(pos, crc);
+#ifdef DEBUG
+ noslide = 0;
+#endif
+ }
+ *token = NEXT_HASH(*token, *pos);
+}
+
+unsigned int
+encode(intf)
+ struct interfacing *intf;
+{
+ unsigned int token, pos, crc;
+ size_t count;
+ struct matchdata match, last;
+
+#ifdef DEBUG
+ if (!fout)
+ fout = xfopen("en", "wt");
+ fprintf(fout, "[filename: %s]\n", reading_filename);
+#endif
+ infile = intf->infile;
+ outfile = intf->outfile;
+ origsize = intf->original;
+ compsize = count = 0L;
+ unpackable = 0;
+
+ INITIALIZE_CRC(crc);
+
+ init_slide();
+
+ encode_set.encode_start();
+ memset(text, ' ', TXTSIZ);
+
+ remainder = fread_crc(&crc, &text[dicsiz], txtsiz-dicsiz, infile);
+
+ match.len = THRESHOLD - 1;
+ match.off = 0;
+ if (match.len > remainder) match.len = remainder;
+
+ pos = dicsiz;
+ token = INIT_HASH(pos);
+ insert_hash(token, pos); /* associate token and pos */
+
+ while (remainder > 0 && ! unpackable) {
+ last = match;
+
+ next_token(&token, &pos, &crc);
+ search_dict(token, pos, last.len-1, &match);
+ insert_hash(token, pos);
+
+ if (match.len > last.len || last.len < THRESHOLD) {
+ /* output a letter */
+ encode_set.output(text[pos - 1], 0);
+#ifdef DEBUG
+ fprintf(fout, "%u C %02X\n", count, text[pos-1]);
+#endif
+ count++;
+ } else {
+ /* output length and offset */
+ encode_set.output(last.len + (256 - THRESHOLD),
+ (last.off-1) & (dicsiz-1) );
+
+#ifdef DEBUG
+ {
+ int i;
+ unsigned char *ptr;
+ unsigned int offset = (last.off & (dicsiz-1));
+
+ fprintf(fout, "%u M <%u %u> ",
+ count, last.len, count - offset);
+
+ ptr = &text[pos-1 - offset];
+ for (i=0; i < last.len; i++)
+ fprintf(fout, "%02X ", ptr[i]);
+ fprintf(fout, "\n");
+ }
+#endif
+ count += last.len;
+
+ --last.len;
+ while (--last.len > 0) {
+ next_token(&token, &pos, &crc);
+ insert_hash(token, pos);
+ }
+ next_token(&token, &pos, &crc);
+ search_dict(token, pos, THRESHOLD - 1, &match);
+ insert_hash(token, pos);
+ }
+ }
+ encode_set.encode_end();
+
+ intf->packed = compsize;
+ intf->original = count;
+
+ return crc;
+}
+
+unsigned int
+decode(intf)
+ struct interfacing *intf;
+{
+ unsigned int i, c;
+ unsigned int dicsiz1, adjust;
+ unsigned char *dtext;
+ unsigned int crc;
+
+#ifdef DEBUG
+ if (!fout)
+ fout = xfopen("de", "wt");
+ fprintf(fout, "[filename: %s]\n", writing_filename);
+#endif
+
+ infile = intf->infile;
+ outfile = intf->outfile;
+ dicbit = intf->dicbit;
+ origsize = intf->original;
+ compsize = intf->packed;
+ decode_set = decode_define[intf->method - 1];
+
+ INITIALIZE_CRC(crc);
+ dicsiz = 1L << dicbit;
+ dtext = (unsigned char *)xmalloc(dicsiz);
+
+ if (extract_broken_archive)
+
+ /* LHa for UNIX (autoconf) had a fatal bug since version
+ 1.14i-ac20030713 (slide.c revision 1.20).
+
+ This bug is possible to make a broken archive, proper LHA
+ cannot extract it (probably it report CRC error).
+
+ If the option "--extract-broken-archive" specified, extract
+ the broken archive made by old LHa for UNIX. */
+ memset(dtext, 0, dicsiz);
+ else
+ memset(dtext, ' ', dicsiz);
+ decode_set.decode_start();
+ dicsiz1 = dicsiz - 1;
+ adjust = 256 - THRESHOLD;
+ if (intf->method == LARC_METHOD_NUM)
+ adjust = 256 - 2;
+
+ decode_count = 0;
+ loc = 0;
+ while (decode_count < origsize) {
+ c = decode_set.decode_c();
+ if (c < 256) {
+#ifdef DEBUG
+ fprintf(fout, "%u C %02X\n", decode_count, c);
+#endif
+ dtext[loc++] = c;
+ if (loc == dicsiz) {
+ fwrite_crc(&crc, dtext, dicsiz, outfile);
+ loc = 0;
+ }
+ decode_count++;
+ }
+ else {
+ struct matchdata match;
+ unsigned int matchpos;
+
+ match.len = c - adjust;
+ match.off = decode_set.decode_p() + 1;
+ matchpos = (loc - match.off) & dicsiz1;
+#ifdef DEBUG
+ fprintf(fout, "%u M <%u %u> ",
+ decode_count, match.len, decode_count-match.off);
+#endif
+ decode_count += match.len;
+ for (i = 0; i < match.len; i++) {
+ c = dtext[(matchpos + i) & dicsiz1];
+#ifdef DEBUG
+ fprintf(fout, "%02X ", c & 0xff);
+#endif
+ dtext[loc++] = c;
+ if (loc == dicsiz) {
+ fwrite_crc(&crc, dtext, dicsiz, outfile);
+ loc = 0;
+ }
+ }
+#ifdef DEBUG
+ fprintf(fout, "\n");
+#endif
+ }
+ }
+ if (loc != 0) {
+ fwrite_crc(&crc, dtext, loc, outfile);
+ }
+
+ free(dtext);
+
+ /* usually read size is intf->packed */
+ intf->read_size = intf->packed - compsize;
+
+ return crc;
+}
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX */
+/* util.c -- LHarc Util */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14e Support for sfx archives 1999.05.28 T.Okamoto */
+/* Modified arton for LhaLib */
+/* ------------------------------------------------------------------------ */
+#include "lha.h"
+/*
+ * util.c - part of LHa for UNIX Feb 26 1992 modified by Masaru Oki Mar 4
+ * 1992 modified by Masaru Oki #ifndef USESTRCASECMP added. Mar 31 1992
+ * modified by Masaru Oki #ifdef NOMEMSET added.
+ */
+#include <errno.h>
+
+size_t
+copyfile(FILE* f1, FILE* f2, size_t size, int text_flg, unsigned int* crcp) /* return: size of source file */
+ /* int text_flg*/ /* 0: binary, 1: read text, 2: write text */
+{
+ unsigned short xsize;
+ char *buf;
+ size_t rsize = 0;
+
+ if (!text_mode)
+ text_flg = 0;
+
+ buf = (char *)xmalloc(BUFFERSIZE);
+ if (crcp)
+ INITIALIZE_CRC(*crcp);
+ if (text_flg)
+ init_code_cache();
+ while (size > 0) {
+ /* read */
+ if (text_flg & 1) {
+ xsize = fread_txt(buf, BUFFERSIZE, f1);
+ if (xsize == 0)
+ break;
+ if (ferror(f1)) {
+ fatal_error("file read error");
+ }
+ }
+ else {
+ xsize = (size > BUFFERSIZE) ? BUFFERSIZE : size;
+ if (fread(buf, 1, xsize, f1) != xsize) {
+ fatal_error("file read error");
+ }
+ if (size < xsize)
+ size = 0;
+ else
+ size -= xsize;
+ }
+
+ /* write */
+ if (f2) {
+ if (text_flg & 2) {
+ if (fwrite_txt(buf, xsize, f2)) {
+ fatal_error("file write error");
+ }
+ }
+ else {
+ if (fwrite(buf, 1, xsize, f2) != xsize) {
+ fatal_error("file write error");
+ }
+ }
+ }
+
+ /* calculate crc */
+ if (crcp) {
+ *crcp = calccrc(*crcp, buf, xsize);
+#ifdef NEED_INCREMENTAL_INDICATOR
+ put_indicator(xsize);
+#endif
+ }
+ rsize += xsize;
+ }
+ free(buf);
+ return rsize;
+}
+
+int
+encode_stored_crc(ifp, ofp, size, original_size_var, write_size_var)
+ FILE *ifp, *ofp;
+ size_t size;
+ size_t *original_size_var;
+ size_t *write_size_var;
+{
+ unsigned int crc;
+
+ size = copyfile(ifp, ofp, size, 1, &crc);
+ *original_size_var = *write_size_var = size;
+ return crc;
+}
+
+/* If TRUE, archive file name is msdos SFX file name. */
+boolean
+archive_is_msdos_sfx1(const char* name)
+{
+ int len = strlen(name);
+
+ if (len >= 4) {
+ if (strcasecmp(".COM", name + len - 4) == 0 ||
+ strcasecmp(".EXE", name + len - 4) == 0)
+ return 1;
+ }
+
+ if (len >= 2 && strcasecmp(".x", name + len - 2) == 0)
+ return 1;
+
+ return 0;
+}
+
+int
+#if STDC_HEADERS
+xsnprintf(char *dest, size_t size, char *fmt, ...)
+#else
+xsnprintf(dest, size, fmt, va_alist)
+ char *dest, *fmt;
+ size_t size;
+ va_dcl
+#endif
+{
+ int len;
+ va_list v;
+
+ va_init(v, fmt);
+ len = vsnprintf(dest, size, fmt, v);
+ va_end(v);
+
+ if (len == -1)
+ return -1;
+
+ if (len >= size) {
+ dest[size-1] = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+#if !STRCHR_8BIT_CLEAN
+/* 8 bit clean strchr()/strrchr()/memchr()/memrchr() */
+char *
+xstrchr(const char *s, int c)
+{
+ if (c == 0)
+ return (char*)s + strlen(s);
+
+ while (*s) {
+ if ((unsigned char)*s == (unsigned char)c)
+ return (char*)s;
+ s++;
+ }
+
+ return 0;
+}
+
+char *
+xstrrchr(const char *s, int c)
+{
+ char *p = 0;
+
+ while (*s) {
+ if ((unsigned char)*s == (unsigned char)c)
+ p = (char*)s;
+ s++;
+ }
+
+ return p;
+}
+
+char *
+xmemchr(const char *s, int c, size_t n)
+{
+ char *end = (char*)s + n;
+
+ while (s != end) {
+ if ((unsigned char)*s == (unsigned char)c)
+ return (char*)s;
+ s++;
+ }
+
+ return 0;
+}
+
+char *
+xmemrchr(const char *s, int c, size_t n)
+{
+ char *end = (char*)s-1;
+ char *p = 0;
+
+ s += n-1;
+ while (s != end) {
+ if ((unsigned char)*s == (unsigned char)c)
+ p = (char*)s;
+ s--;
+ }
+
+ return p;
+}
+#endif
+
+/* This function is similar to strncpy() but `dst' is always
+ terminated by '\0'. Return the copied string length. */
+int
+str_safe_copy(char *dst, const char *src, int dstsz)
+{
+ int i;
+
+ if (dstsz < 1) return 0;
+
+ for (i = 0; i < dstsz; i++) {
+ if ((dst[i] = src[i]) == '\0')
+ return i;
+ }
+
+ /* here is i == dstsz */
+ dst[--i] = '\0'; /* if eliminated this line,
+ this function was same as strncpy(). */
+
+ return i;
+}
--- /dev/null
+LhaLib is a Ruby extension library that unpack LHarched file.
+
+The C sources were originally written by Y.Tagawa, M.Oki, N.Watazaki, T.Okamoto and other people, and original Lha was written by Y.Yoshizaki in early Japanese Personal Computer days.
+This library is a separate production from original LHa, and released under LGPL (the origianl LHa is distributed under the proprietary license). So the product's responsibility is dedicated by arton.
--- /dev/null
+#
+# setup.rb
+#
+# Copyright (c) 2000-2004 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map) # Ruby 1.4.6
+ module Enumerable
+ alias map collect
+ end
+end
+
+unless File.respond_to?(:read) # Ruby 1.6
+ def File.read(fname)
+ open(fname) {|f|
+ return f.read
+ }
+ end
+end
+
+def File.binread(fname)
+ open(fname, 'rb') {|f|
+ return f.read
+ }
+end
+
+# for corrupted windows stat(2)
+def File.dir?(path)
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+ raise SetupError, msg
+end
+
+#
+# Config
+#
+
+if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+ ARGV.delete(arg)
+ require arg.split(/=/, 2)[1]
+ $".push 'rbconfig.rb'
+else
+ require 'rbconfig'
+end
+
+def multipackage_install?
+ FileTest.directory?(File.dirname($0) + '/packages')
+end
+
+
+class ConfigItem
+ def initialize(name, template, default, desc)
+ @name = name.freeze
+ @template = template
+ @value = default
+ @default = default.dup.freeze
+ @description = desc
+ end
+
+ attr_reader :name
+ attr_reader :description
+
+ attr_accessor :default
+ alias help_default default
+
+ def help_opt
+ "--#{@name}=#{@template}"
+ end
+
+ def value
+ @value
+ end
+
+ def eval(table)
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
+ end
+
+ def set(val)
+ @value = check(val)
+ end
+
+ private
+
+ def check(val)
+ setup_rb_error "config: --#{name} requires argument" unless val
+ val
+ end
+end
+
+class BoolItem < ConfigItem
+ def config_type
+ 'bool'
+ end
+
+ def help_opt
+ "--#{@name}"
+ end
+
+ private
+
+ def check(val)
+ return 'yes' unless val
+ unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+ end
+ (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
+ end
+end
+
+class PathItem < ConfigItem
+ def config_type
+ 'path'
+ end
+
+ private
+
+ def check(path)
+ setup_rb_error "config: --#{@name} requires argument" unless path
+ path[0,1] == '$' ? path : File.expand_path(path)
+ end
+end
+
+class ProgramItem < ConfigItem
+ def config_type
+ 'program'
+ end
+end
+
+class SelectItem < ConfigItem
+ def initialize(name, template, default, desc)
+ super
+ @ok = template.split('/')
+ end
+
+ def config_type
+ 'select'
+ end
+
+ private
+
+ def check(val)
+ unless @ok.include?(val.strip)
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+ end
+ val.strip
+ end
+end
+
+class PackageSelectionItem < ConfigItem
+ def initialize(name, template, default, help_default, desc)
+ super name, template, default, desc
+ @help_default = help_default
+ end
+
+ attr_reader :help_default
+
+ def config_type
+ 'package'
+ end
+
+ private
+
+ def check(val)
+ unless File.dir?("packages/#{val}")
+ setup_rb_error "config: no such package: #{val}"
+ end
+ val
+ end
+end
+
+class ConfigTable_class
+
+ def initialize(items)
+ @items = items
+ @table = {}
+ items.each do |i|
+ @table[i.name] = i
+ end
+ ALIASES.each do |ali, name|
+ @table[ali] = @table[name]
+ end
+ end
+
+ include Enumerable
+
+ def each(&block)
+ @items.each(&block)
+ end
+
+ def key?(name)
+ @table.key?(name)
+ end
+
+ def lookup(name)
+ @table[name] or raise ArgumentError, "no such config item: #{name}"
+ end
+
+ def add(item)
+ @items.push item
+ @table[item.name] = item
+ end
+
+ def remove(name)
+ item = lookup(name)
+ @items.delete_if {|i| i.name == name }
+ @table.delete_if {|name, i| i.name == name }
+ item
+ end
+
+ def new
+ dup()
+ end
+
+ def savefile
+ '.config'
+ end
+
+ def load
+ begin
+ t = dup()
+ File.foreach(savefile()) do |line|
+ k, v = *line.split(/=/, 2)
+ t[k] = v.strip
+ end
+ t
+ rescue Errno::ENOENT
+ setup_rb_error $!.message + "#{File.basename($0)} config first"
+ end
+ end
+
+ def save
+ @items.each {|i| i.value }
+ File.open(savefile(), 'w') {|f|
+ @items.each do |i|
+ f.printf "%s=%s\n", i.name, i.value if i.value
+ end
+ }
+ end
+
+ def [](key)
+ lookup(key).eval(self)
+ end
+
+ def []=(key, val)
+ lookup(key).set val
+ end
+
+end
+
+c = ::Config::CONFIG
+
+rubypath = c['bindir'] + '/' + c['ruby_install_name']
+
+major = c['MAJOR'].to_i
+minor = c['MINOR'].to_i
+teeny = c['TEENY'].to_i
+version = "#{major}.#{minor}"
+
+# ruby ver. >= 1.4.4?
+newpath_p = ((major >= 2) or
+ ((major == 1) and
+ ((minor >= 5) or
+ ((minor == 4) and (teeny >= 4)))))
+
+if c['rubylibdir']
+ # V < 1.6.3
+ _stdruby = c['rubylibdir']
+ _siteruby = c['sitedir']
+ _siterubyver = c['sitelibdir']
+ _siterubyverarch = c['sitearchdir']
+elsif newpath_p
+ # 1.4.4 <= V <= 1.6.3
+ _stdruby = "$prefix/lib/ruby/#{version}"
+ _siteruby = c['sitedir']
+ _siterubyver = "$siteruby/#{version}"
+ _siterubyverarch = "$siterubyver/#{c['arch']}"
+else
+ # V < 1.4.4
+ _stdruby = "$prefix/lib/ruby/#{version}"
+ _siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
+ _siterubyver = _siteruby
+ _siterubyverarch = "$siterubyver/#{c['arch']}"
+end
+libdir = '-* dummy libdir *-'
+stdruby = '-* dummy rubylibdir *-'
+siteruby = '-* dummy site_ruby *-'
+siterubyver = '-* dummy site_ruby version *-'
+parameterize = lambda {|path|
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
+ .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\
+ .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\
+ .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\
+ .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
+}
+libdir = parameterize.call(c['libdir'])
+stdruby = parameterize.call(_stdruby)
+siteruby = parameterize.call(_siteruby)
+siterubyver = parameterize.call(_siterubyver)
+siterubyverarch = parameterize.call(_siterubyverarch)
+
+if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+else
+ makeprog = 'make'
+end
+
+common_conf = [
+ PathItem.new('prefix', 'path', c['prefix'],
+ 'path prefix of target environment'),
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+ 'the directory for commands'),
+ PathItem.new('libdir', 'path', libdir,
+ 'the directory for libraries'),
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+ 'the directory for shared data'),
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+ 'the directory for man pages'),
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+ 'the directory for man pages'),
+ PathItem.new('stdruby', 'path', stdruby,
+ 'the directory for standard ruby libraries'),
+ PathItem.new('siteruby', 'path', siteruby,
+ 'the directory for version-independent aux ruby libraries'),
+ PathItem.new('siterubyver', 'path', siterubyver,
+ 'the directory for aux ruby libraries'),
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
+ 'the directory for aux ruby binaries'),
+ PathItem.new('rbdir', 'path', '$siterubyver',
+ 'the directory for ruby scripts'),
+ PathItem.new('sodir', 'path', '$siterubyverarch',
+ 'the directory for ruby extentions'),
+ PathItem.new('rubypath', 'path', rubypath,
+ 'the path to set to #! line'),
+ ProgramItem.new('rubyprog', 'name', rubypath,
+ 'the ruby program using for installation'),
+ ProgramItem.new('makeprog', 'name', makeprog,
+ 'the make program to compile ruby extentions'),
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+ 'shebang line (#!) editing mode'),
+ BoolItem.new('without-ext', 'yes/no', 'no',
+ 'does not compile/install ruby extentions')
+]
+class ConfigTable_class # open again
+ ALIASES = {
+ 'std-ruby' => 'stdruby',
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
+ 'site-ruby' => 'siterubyver', # For backward compatibility
+ 'bin-dir' => 'bindir',
+ 'bin-dir' => 'bindir',
+ 'rb-dir' => 'rbdir',
+ 'so-dir' => 'sodir',
+ 'data-dir' => 'datadir',
+ 'ruby-path' => 'rubypath',
+ 'ruby-prog' => 'rubyprog',
+ 'ruby' => 'rubyprog',
+ 'make-prog' => 'makeprog',
+ 'make' => 'makeprog'
+ }
+end
+multipackage_conf = [
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+ 'package names that you want to install'),
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+ 'package names that you do not want to install')
+]
+if multipackage_install?
+ ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
+else
+ ConfigTable = ConfigTable_class.new(common_conf)
+end
+
+
+module MetaConfigAPI
+
+ def eval_file_ifexist(fname)
+ instance_eval File.read(fname), fname, 1 if File.file?(fname)
+ end
+
+ def config_names
+ ConfigTable.map {|i| i.name }
+ end
+
+ def config?(name)
+ ConfigTable.key?(name)
+ end
+
+ def bool_config?(name)
+ ConfigTable.lookup(name).config_type == 'bool'
+ end
+
+ def path_config?(name)
+ ConfigTable.lookup(name).config_type == 'path'
+ end
+
+ def value_config?(name)
+ case ConfigTable.lookup(name).config_type
+ when 'bool', 'path'
+ true
+ else
+ false
+ end
+ end
+
+ def add_config(item)
+ ConfigTable.add item
+ end
+
+ def add_bool_config(name, default, desc)
+ ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+ end
+
+ def add_path_config(name, default, desc)
+ ConfigTable.add PathItem.new(name, 'path', default, desc)
+ end
+
+ def set_config_default(name, default)
+ ConfigTable.lookup(name).default = default
+ end
+
+ def remove_config(name)
+ ConfigTable.remove(name)
+ end
+
+end
+
+
+#
+# File Operations
+#
+
+module FileOperations
+
+ def mkdir_p(dirname, prefix = nil)
+ dirname = prefix + File.expand_path(dirname) if prefix
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
+ return if no_harm?
+
+ # does not check '/'... it's too abnormal case
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
+ if /\A[a-z]:\z/i =~ dirs[0]
+ disk = dirs.shift
+ dirs[0] = disk + dirs[0]
+ end
+ dirs.each_index do |idx|
+ path = dirs[0..idx].join('')
+ Dir.mkdir path unless File.dir?(path)
+ end
+ end
+
+ def rm_f(fname)
+ $stderr.puts "rm -f #{fname}" if verbose?
+ return if no_harm?
+
+ if File.exist?(fname) or File.symlink?(fname)
+ File.chmod 0777, fname
+ File.unlink fname
+ end
+ end
+
+ def rm_rf(dn)
+ $stderr.puts "rm -rf #{dn}" if verbose?
+ return if no_harm?
+
+ Dir.chdir dn
+ Dir.foreach('.') do |fn|
+ next if fn == '.'
+ next if fn == '..'
+ if File.dir?(fn)
+ verbose_off {
+ rm_rf fn
+ }
+ else
+ verbose_off {
+ rm_f fn
+ }
+ end
+ end
+ Dir.chdir '..'
+ Dir.rmdir dn
+ end
+
+ def move_file(src, dest)
+ File.unlink dest if File.exist?(dest)
+ begin
+ File.rename src, dest
+ rescue
+ File.open(dest, 'wb') {|f| f.write File.binread(src) }
+ File.chmod File.stat(src).mode, dest
+ File.unlink src
+ end
+ end
+
+ def install(from, dest, mode, prefix = nil)
+ $stderr.puts "install #{from} #{dest}" if verbose?
+ return if no_harm?
+
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+ str = File.binread(from)
+ if diff?(str, realdest)
+ verbose_off {
+ rm_f realdest if File.exist?(realdest)
+ }
+ File.open(realdest, 'wb') {|f|
+ f.write str
+ }
+ File.chmod mode, realdest
+
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+ if prefix
+ f.puts realdest.sub(prefix, '')
+ else
+ f.puts realdest
+ end
+ }
+ end
+ end
+
+ def diff?(new_content, path)
+ return true unless File.exist?(path)
+ new_content != File.binread(path)
+ end
+
+ def command(str)
+ $stderr.puts str if verbose?
+ system str or raise RuntimeError, "'system #{str}' failed"
+ end
+
+ def ruby(str)
+ command config('rubyprog') + ' ' + str
+ end
+
+ def make(task = '')
+ command config('makeprog') + ' ' + task
+ end
+
+ def extdir?(dir)
+ File.exist?(dir + '/MANIFEST')
+ end
+
+ def all_files_in(dirname)
+ Dir.open(dirname) {|d|
+ return d.select {|ent| File.file?("#{dirname}/#{ent}") }
+ }
+ end
+
+ REJECT_DIRS = %w(
+ CVS SCCS RCS CVS.adm .svn
+ )
+
+ def all_dirs_in(dirname)
+ Dir.open(dirname) {|d|
+ return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
+ }
+ end
+
+end
+
+
+#
+# Main Installer
+#
+
+module HookUtils
+
+ def run_hook(name)
+ try_run_hook "#{curr_srcdir()}/#{name}" or
+ try_run_hook "#{curr_srcdir()}/#{name}.rb"
+ end
+
+ def try_run_hook(fname)
+ return false unless File.file?(fname)
+ begin
+ instance_eval File.read(fname), fname, 1
+ rescue
+ setup_rb_error "hook #{fname} failed:\n" + $!.message
+ end
+ true
+ end
+
+end
+
+
+module HookScriptAPI
+
+ def get_config(key)
+ @config[key]
+ end
+
+ alias config get_config
+
+ def set_config(key, val)
+ @config[key] = val
+ end
+
+ #
+ # srcdir/objdir (works only in the package directory)
+ #
+
+ #abstract srcdir_root
+ #abstract objdir_root
+ #abstract relpath
+
+ def curr_srcdir
+ "#{srcdir_root()}/#{relpath()}"
+ end
+
+ def curr_objdir
+ "#{objdir_root()}/#{relpath()}"
+ end
+
+ def srcfile(path)
+ "#{curr_srcdir()}/#{path}"
+ end
+
+ def srcexist?(path)
+ File.exist?(srcfile(path))
+ end
+
+ def srcdirectory?(path)
+ File.dir?(srcfile(path))
+ end
+
+ def srcfile?(path)
+ File.file? srcfile(path)
+ end
+
+ def srcentries(path = '.')
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
+ return d.to_a - %w(. ..)
+ }
+ end
+
+ def srcfiles(path = '.')
+ srcentries(path).select {|fname|
+ File.file?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+ def srcdirectories(path = '.')
+ srcentries(path).select {|fname|
+ File.dir?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+end
+
+
+class ToplevelInstaller
+
+ Version = '3.3.1'
+ Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
+
+ TASKS = [
+ [ 'all', 'do config, setup, then install' ],
+ [ 'config', 'saves your configurations' ],
+ [ 'show', 'shows current configuration' ],
+ [ 'setup', 'compiles ruby extentions and others' ],
+ [ 'install', 'installs files' ],
+ [ 'clean', "does `make clean' for each extention" ],
+ [ 'distclean',"does `make distclean' for each extention" ]
+ ]
+
+ def ToplevelInstaller.invoke
+ instance().invoke
+ end
+
+ @singleton = nil
+
+ def ToplevelInstaller.instance
+ @singleton ||= new(File.dirname($0))
+ @singleton
+ end
+
+ include MetaConfigAPI
+
+ def initialize(ardir_root)
+ @config = nil
+ @options = { 'verbose' => true }
+ @ardir = File.expand_path(ardir_root)
+ end
+
+ def inspect
+ "#<#{self.class} #{__id__()}>"
+ end
+
+ def invoke
+ run_metaconfigs
+ case task = parsearg_global()
+ when nil, 'all'
+ @config = load_config('config')
+ parsearg_config
+ init_installers
+ exec_config
+ exec_setup
+ exec_install
+ else
+ @config = load_config(task)
+ __send__ "parsearg_#{task}"
+ init_installers
+ __send__ "exec_#{task}"
+ end
+ end
+
+ def run_metaconfigs
+ eval_file_ifexist "#{@ardir}/metaconfig"
+ end
+
+ def load_config(task)
+ case task
+ when 'config'
+ ConfigTable.new
+ when 'clean', 'distclean'
+ if File.exist?(ConfigTable.savefile)
+ then ConfigTable.load
+ else ConfigTable.new
+ end
+ else
+ ConfigTable.load
+ end
+ end
+
+ def init_installers
+ @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
+ end
+
+ #
+ # Hook Script API bases
+ #
+
+ def srcdir_root
+ @ardir
+ end
+
+ def objdir_root
+ '.'
+ end
+
+ def relpath
+ '.'
+ end
+
+ #
+ # Option Parsing
+ #
+
+ def parsearg_global
+ valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
+
+ while arg = ARGV.shift
+ case arg
+ when /\A\w+\z/
+ setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
+ return arg
+
+ when '-q', '--quiet'
+ @options['verbose'] = false
+
+ when '--verbose'
+ @options['verbose'] = true
+
+ when '-h', '--help'
+ print_usage $stdout
+ exit 0
+
+ when '-v', '--version'
+ puts "#{File.basename($0)} version #{Version}"
+ exit 0
+
+ when '--copyright'
+ puts Copyright
+ exit 0
+
+ else
+ setup_rb_error "unknown global option '#{arg}'"
+ end
+ end
+
+ nil
+ end
+
+
+ def parsearg_no_options
+ unless ARGV.empty?
+ setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
+ end
+ end
+
+ alias parsearg_show parsearg_no_options
+ alias parsearg_setup parsearg_no_options
+ alias parsearg_clean parsearg_no_options
+ alias parsearg_distclean parsearg_no_options
+
+ def parsearg_config
+ re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
+ @options['config-opt'] = []
+
+ while i = ARGV.shift
+ if /\A--?\z/ =~ i
+ @options['config-opt'] = ARGV.dup
+ break
+ end
+ m = re.match(i) or setup_rb_error "config: unknown option #{i}"
+ name, value = *m.to_a[1,2]
+ @config[name] = value
+ end
+ end
+
+ def parsearg_install
+ @options['no-harm'] = false
+ @options['install-prefix'] = ''
+ while a = ARGV.shift
+ case a
+ when /\A--no-harm\z/
+ @options['no-harm'] = true
+ when /\A--prefix=(.*)\z/
+ path = $1
+ path = File.expand_path(path) unless path[0,1] == '/'
+ @options['install-prefix'] = path
+ else
+ setup_rb_error "install: unknown option #{a}"
+ end
+ end
+ end
+
+ def print_usage(out)
+ out.puts 'Typical Installation Procedure:'
+ out.puts " $ ruby #{File.basename $0} config"
+ out.puts " $ ruby #{File.basename $0} setup"
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
+ out.puts
+ out.puts 'Detailed Usage:'
+ out.puts " ruby #{File.basename $0} <global option>"
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+ fmt = " %-24s %s\n"
+ out.puts
+ out.puts 'Global options:'
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
+ out.printf fmt, ' --verbose', 'output messages verbosely'
+ out.printf fmt, '-h,--help', 'print this message'
+ out.printf fmt, '-v,--version', 'print version and quit'
+ out.printf fmt, ' --copyright', 'print copyright and quit'
+ out.puts
+ out.puts 'Tasks:'
+ TASKS.each do |name, desc|
+ out.printf fmt, name, desc
+ end
+
+ fmt = " %-24s %s [%s]\n"
+ out.puts
+ out.puts 'Options for CONFIG or ALL:'
+ ConfigTable.each do |item|
+ out.printf fmt, item.help_opt, item.description, item.help_default
+ end
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+ out.puts
+ out.puts 'Options for INSTALL:'
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+ out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
+ out.puts
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ @installer.exec_config
+ @config.save # must be final
+ end
+
+ def exec_setup
+ @installer.exec_setup
+ end
+
+ def exec_install
+ @installer.exec_install
+ end
+
+ def exec_show
+ ConfigTable.each do |i|
+ printf "%-20s %s\n", i.name, i.value
+ end
+ end
+
+ def exec_clean
+ @installer.exec_clean
+ end
+
+ def exec_distclean
+ @installer.exec_distclean
+ end
+
+end
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+ include HookUtils
+ include HookScriptAPI
+ include FileOperations
+
+ def initialize(ardir)
+ super
+ @packages = all_dirs_in("#{@ardir}/packages")
+ raise 'no package exists' if @packages.empty?
+ end
+
+ def run_metaconfigs
+ eval_file_ifexist "#{@ardir}/metaconfig"
+ @packages.each do |name|
+ eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
+ end
+ end
+
+ def init_installers
+ @installers = {}
+ @packages.each do |pack|
+ @installers[pack] = Installer.new(@config, @options,
+ "#{@ardir}/packages/#{pack}",
+ "packages/#{pack}")
+ end
+
+ with = extract_selection(config('with'))
+ without = extract_selection(config('without'))
+ @selected = @installers.keys.select {|name|
+ (with.empty? or with.include?(name)) \
+ and not without.include?(name)
+ }
+ end
+
+ def extract_selection(list)
+ a = list.split(/,/)
+ a.each do |name|
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
+ end
+ a
+ end
+
+ def print_usage(f)
+ super
+ f.puts 'Inluded packages:'
+ f.puts ' ' + @packages.sort.join(' ')
+ f.puts
+ end
+
+ #
+ # multi-package metaconfig API
+ #
+
+ attr_reader :packages
+
+ def declare_packages(list)
+ raise 'package list is empty' if list.empty?
+ list.each do |name|
+ raise "directory packages/#{name} does not exist"\
+ unless File.dir?("#{@ardir}/packages/#{name}")
+ end
+ @packages = list
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ run_hook 'pre-config'
+ each_selected_installers {|inst| inst.exec_config }
+ run_hook 'post-config'
+ @config.save # must be final
+ end
+
+ def exec_setup
+ run_hook 'pre-setup'
+ each_selected_installers {|inst| inst.exec_setup }
+ run_hook 'post-setup'
+ end
+
+ def exec_install
+ run_hook 'pre-install'
+ each_selected_installers {|inst| inst.exec_install }
+ run_hook 'post-install'
+ end
+
+ def exec_clean
+ rm_f ConfigTable.savefile
+ run_hook 'pre-clean'
+ each_selected_installers {|inst| inst.exec_clean }
+ run_hook 'post-clean'
+ end
+
+ def exec_distclean
+ rm_f ConfigTable.savefile
+ run_hook 'pre-distclean'
+ each_selected_installers {|inst| inst.exec_distclean }
+ run_hook 'post-distclean'
+ end
+
+ #
+ # lib
+ #
+
+ def each_selected_installers
+ Dir.mkdir 'packages' unless File.dir?('packages')
+ @selected.each do |pack|
+ $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+ Dir.chdir "packages/#{pack}"
+ yield @installers[pack]
+ Dir.chdir '../..'
+ end
+ end
+
+ def verbose?
+ @options['verbose']
+ end
+
+ def no_harm?
+ @options['no-harm']
+ end
+
+end
+
+
+class Installer
+
+ FILETYPES = %w( bin lib ext data )
+
+ include HookScriptAPI
+ include HookUtils
+ include FileOperations
+
+ def initialize(config, opt, srcroot, objroot)
+ @config = config
+ @options = opt
+ @srcdir = File.expand_path(srcroot)
+ @objdir = File.expand_path(objroot)
+ @currdir = '.'
+ end
+
+ def inspect
+ "#<#{self.class} #{File.basename(@srcdir)}>"
+ end
+
+ #
+ # Hook Script API base methods
+ #
+
+ def srcdir_root
+ @srcdir
+ end
+
+ def objdir_root
+ @objdir
+ end
+
+ def relpath
+ @currdir
+ end
+
+ #
+ # configs/options
+ #
+
+ def no_harm?
+ @options['no-harm']
+ end
+
+ def verbose?
+ @options['verbose']
+ end
+
+ def verbose_off
+ begin
+ save, @options['verbose'] = @options['verbose'], false
+ yield
+ ensure
+ @options['verbose'] = save
+ end
+ end
+
+ #
+ # TASK config
+ #
+
+ def exec_config
+ exec_task_traverse 'config'
+ end
+
+ def config_dir_bin(rel)
+ end
+
+ def config_dir_lib(rel)
+ end
+
+ def config_dir_ext(rel)
+ extconf if extdir?(curr_srcdir())
+ end
+
+ def extconf
+ opt = @options['config-opt'].join(' ')
+ command "#{config('rubyprog')} \"#{curr_srcdir()}/extconf.rb\" #{opt}"
+ end
+
+ def config_dir_data(rel)
+ end
+
+ #
+ # TASK setup
+ #
+
+ def exec_setup
+ exec_task_traverse 'setup'
+ end
+
+ def setup_dir_bin(rel)
+ all_files_in(curr_srcdir()).each do |fname|
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
+ end
+ end
+
+ def adjust_shebang(path)
+ return if no_harm?
+ tmpfile = File.basename(path) + '.tmp'
+ begin
+ File.open(path, 'rb') {|r|
+ first = r.gets
+ return unless File.basename(config('rubypath')) == 'ruby'
+ return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
+ $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
+ File.open(tmpfile, 'wb') {|w|
+ w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
+ w.write r.read
+ }
+ move_file tmpfile, File.basename(path)
+ }
+ ensure
+ File.unlink tmpfile if File.exist?(tmpfile)
+ end
+ end
+
+ def setup_dir_lib(rel)
+ end
+
+ def setup_dir_ext(rel)
+ make if extdir?(curr_srcdir())
+ end
+
+ def setup_dir_data(rel)
+ end
+
+ #
+ # TASK install
+ #
+
+ def exec_install
+ rm_f 'InstalledFiles'
+ exec_task_traverse 'install'
+ end
+
+ def install_dir_bin(rel)
+ install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
+ end
+
+ def install_dir_lib(rel)
+ install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
+ end
+
+ def install_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ install_files ruby_extentions('.'),
+ "#{config('sodir')}/#{File.dirname(rel)}",
+ 0555
+ end
+
+ def install_dir_data(rel)
+ install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
+ end
+
+ def install_files(list, dest, mode)
+ mkdir_p dest, @options['install-prefix']
+ list.each do |fname|
+ install fname, dest, mode, @options['install-prefix']
+ end
+ end
+
+ def ruby_scripts
+ collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
+ end
+
+ # picked up many entries from cvs-1.11.1/src/ignore.c
+ reject_patterns = %w(
+ core RCSLOG tags TAGS .make.state
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+ *.org *.in .*
+ )
+ mapping = {
+ '.' => '\.',
+ '$' => '\$',
+ '#' => '\#',
+ '*' => '.*'
+ }
+ REJECT_PATTERNS = Regexp.new('\A(?:' +
+ reject_patterns.map {|pat|
+ pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
+ }.join('|') +
+ ')\z')
+
+ def collect_filenames_auto
+ mapdir((existfiles() - hookfiles()).reject {|fname|
+ REJECT_PATTERNS =~ fname
+ })
+ end
+
+ def existfiles
+ all_files_in(curr_srcdir()) | all_files_in('.')
+ end
+
+ def hookfiles
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
+ }.flatten
+ end
+
+ def mapdir(filelist)
+ filelist.map {|fname|
+ if File.exist?(fname) # objdir
+ fname
+ else # srcdir
+ File.join(curr_srcdir(), fname)
+ end
+ }
+ end
+
+ def ruby_extentions(dir)
+ Dir.open(dir) {|d|
+ ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
+ if ents.empty?
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+ end
+ return ents
+ }
+ end
+
+ #
+ # TASK clean
+ #
+
+ def exec_clean
+ exec_task_traverse 'clean'
+ rm_f ConfigTable.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ def clean_dir_bin(rel)
+ end
+
+ def clean_dir_lib(rel)
+ end
+
+ def clean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'clean' if File.file?('Makefile')
+ end
+
+ def clean_dir_data(rel)
+ end
+
+ #
+ # TASK distclean
+ #
+
+ def exec_distclean
+ exec_task_traverse 'distclean'
+ rm_f ConfigTable.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ def distclean_dir_bin(rel)
+ end
+
+ def distclean_dir_lib(rel)
+ end
+
+ def distclean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'distclean' if File.file?('Makefile')
+ end
+
+ #
+ # lib
+ #
+
+ def exec_task_traverse(task)
+ run_hook "pre-#{task}"
+ FILETYPES.each do |type|
+ if config('without-ext') == 'yes' and type == 'ext'
+ $stderr.puts 'skipping ext/* by user option' if verbose?
+ next
+ end
+ traverse task, type, "#{task}_dir_#{type}"
+ end
+ run_hook "post-#{task}"
+ end
+
+ def traverse(task, rel, mid)
+ dive_into(rel) {
+ run_hook "pre-#{task}"
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+ all_dirs_in(curr_srcdir()).each do |d|
+ traverse task, "#{rel}/#{d}", mid
+ end
+ run_hook "post-#{task}"
+ }
+ end
+
+ def dive_into(rel)
+ return unless File.dir?("#{@srcdir}/#{rel}")
+
+ dir = File.basename(rel)
+ Dir.mkdir dir unless File.dir?(dir)
+ prevdir = Dir.pwd
+ Dir.chdir dir
+ $stderr.puts '---> ' + rel if verbose?
+ @currdir = rel
+ yield
+ Dir.chdir prevdir
+ $stderr.puts '<--- ' + rel if verbose?
+ @currdir = File.dirname(rel)
+ end
+
+end
+
+
+if $0 == __FILE__
+ begin
+ if multipackage_install?
+ ToplevelInstallerMulti.invoke
+ else
+ ToplevelInstaller.invoke
+ end
+ rescue SetupError
+ raise if $DEBUG
+ $stderr.puts $!.message
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+ exit 1
+ end
+end
--- /dev/null
+#!/usr/local/env ruby
+# $Id: $
+
+require 'test/unit'
+require 'fileutils'
+require 'lhalib'
+
+puts "start LhaLib(#{LhaLib::VERSION} test"
+class TestLhaLib < Test::Unit::TestCase
+ include LhaLib
+ def setup
+ FileUtils.rm_rf ['test.dat', 'testlib.c', 'testh.h', 'tmp']
+ end
+
+ def tearDown
+ end
+
+ def test_copy_only
+ assert_equal(1, x('test.lzh'))
+ assert(File.exist?('test.dat'))
+ obuff = ' ' * 256
+ (0..255).each do |x|
+ obuff[x] = x
+ end
+ nbuff = nil
+ File.open('test.dat', 'rb') do |f|
+ nbuff = f.read
+ end
+ assert_equal(obuff, nbuff)
+ end
+
+ def test_x
+ assert_equal(2, x('test2.lzh'))
+ assert(File.exist?('testlib.c'))
+ assert(File.exist?('testh.h'))
+ check_file('testlib.c')
+ check_file('testh.h')
+ end
+
+ def test_x_withdir
+ assert_equal(4, x('tmpdir.lzh'))
+ assert(File.exist?('tmp/testlib.c'))
+ assert(File.exist?('tmp/testh.h'))
+ check_file('testlib.c', 'tmp')
+ check_file('testh.h', 'tmp')
+ end
+
+ def test_proc
+ i = 0
+ name = ['tmp/', 'tmp/test.dat', 'tmp/testh.h', 'tmp/testlib.c']
+ size = [0, 256, 9310, 1997]
+ # directory = S_IFDIR | 0755, file = S_IFREG + 0644
+ perm = [040755, 0100644, 0100644, 0100644]
+ cnt = x('tmpdir.lzh') do |info|
+ assert_equal(name[i], info[:name])
+ assert_equal(size[i], info[:original_size])
+ assert_equal(perm[i], info[:permission])
+ i += 1
+ end
+ # normal extract process was done ?
+ assert_equal(4, cnt)
+ assert_equal(4, i)
+ assert(File.exist?('tmp/testlib.c'))
+ assert(File.exist?('tmp/testh.h'))
+ check_file('testlib.c', 'tmp')
+ check_file('testh.h', 'tmp')
+ end
+
+ def test_badarchive
+ assert_equal(0, x('test.rb'))
+ end
+
+ private
+ def check_file(name, dir = nil)
+ org = nil
+ File.open("#{name}.org", 'rb') do |f|
+ org = f.read
+ end
+ ex = nil
+ File.open(dir ? "#{dir}/#{name}" : name, 'rb') do |f|
+ ex = f.read
+ end
+ assert_equal(org, ex)
+ end
+end
+
--- /dev/null
+/* ------------------------------------------------------------------------ */
+/* LHa for UNIX Archiver Driver */
+/* */
+/* Modified Nobutaka Watazaki */
+/* */
+/* Ver. 1.14 Soruce All chagned 1995.01.14 N.Watazaki */
+/* Ver. 1.14i Modified and bug fixed 2000.10.06 t.okamoto */
+/* ------------------------------------------------------------------------ */
+/* Modified arton for LhaLib */
+/*
+ Included...
+ lharc.h interface.h slidehuf.h
+*/
+
+#include "ruby.h"
+
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcmp(s1, s2, n) bcmp ((s1), (s2), (n))
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+
+#ifndef NULL
+#define NULL ((char *)0)
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if STDC_HEADERS
+# include <stdarg.h>
+# define va_init(a,b) va_start(a,b)
+#else
+# include <varargs.h>
+# define va_init(a,b) va_start(a)
+#endif
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+#if HAVE_GRP_H
+# include <grp.h>
+#endif
+
+#if !HAVE_SETUID
+typedef int uid_t;
+#endif
+#if !HAVE_SETGID
+typedef int gid_t;
+#endif
+
+#if !HAVE_UINT64_T
+# define HAVE_UINT64_T 1
+# if SIZEOF_LONG == 8
+ typedef unsigned long uint64_t;
+# elif HAVE_LONG_LONG
+ typedef unsigned long long uint64_t;
+# else
+# undef HAVE_UINT64_T
+# endif
+#endif
+
+/*
+#if !HAVE_SSIZE_T
+typedef long ssize_t;
+#endif
+*/
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+#if HAVE_UTIME_H
+#include <utime.h>
+#else
+struct utimbuf {
+ time_t actime;
+ time_t modtime;
+};
+int utime(const char *, struct utimbuf *);
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# ifdef NONSYSTEM_DIR_LIBRARY /* no use ?? */
+# include "lhdir.h"
+# endif
+#endif
+
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#else
+int fnmatch(const char *pattern, const char *string, int flags);
+# define FNM_PATHNAME 1
+# define FNM_NOESCAPE 2
+# define FNM_PERIOD 4
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif /* SEEK_SET */
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#else
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef UCHAR_MAX
+#define UCHAR_MAX ((1<<(sizeof(unsigned char)*8))-1)
+#endif
+
+#ifndef USHRT_MAX
+#define USHRT_MAX ((1<<(sizeof(unsigned short)*8))-1)
+#endif
+
+#ifndef SHRT_MAX
+#define SHRT_MAX ((1<<(sizeof(short)*8-1))-1)
+#endif
+
+#ifndef SHRT_MIN
+#define SHRT_MIN (SHRT_MAX-USHRT_MAX)
+#endif
+
+#ifndef ULONG_MAX
+#define ULONG_MAX ((1<<(sizeof(unsigned long)*8))-1)
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX ((1<<(sizeof(long)*8-1))-1)
+#endif
+
+#ifndef LONG_MIN
+#define LONG_MIN (LONG_MAX-ULONG_MAX)
+#endif
+
+#endif /* HAVE_LIMITS_H */
+
+#if !HAVE_FSEEKO
+# define fseeko fseek
+#endif
+#if !HAVE_FTELLO
+# define ftello ftell
+#endif
+
+#include "lha/lha_macro.h"
+
+#define exit(n) lha_exit(n)
+
+struct encode_option {
+#if defined(__STDC__) || defined(AIX)
+ void (*output) ();
+ void (*encode_start) ();
+ void (*encode_end) ();
+#else
+ int (*output) ();
+ int (*encode_start) ();
+ int (*encode_end) ();
+#endif
+};
+
+struct decode_option {
+ unsigned short (*decode_c) ();
+ unsigned short (*decode_p) ();
+#if defined(__STDC__) || defined(AIX)
+ void (*decode_start) ();
+#else
+ int (*decode_start) ();
+#endif
+};
+
+/* ------------------------------------------------------------------------ */
+/* LHa File Type Definition */
+/* ------------------------------------------------------------------------ */
+typedef int boolean; /* TRUE or FALSE */
+
+struct string_pool {
+ int used;
+ int size;
+ int n;
+ char *buffer;
+};
+
+typedef struct LzHeader {
+ size_t header_size;
+ int size_field_length;
+ char method[METHOD_TYPE_STORAGE];
+ size_t packed_size;
+ size_t original_size;
+ unsigned char attribute;
+ unsigned char header_level;
+ char name[FILENAME_LENGTH];
+ char realname[FILENAME_LENGTH];/* real name for symbolic link */
+ unsigned int crc; /* file CRC */
+ boolean has_crc; /* file CRC */
+ unsigned int header_crc; /* header CRC */
+ unsigned char extend_type;
+ unsigned char minor_version;
+
+ /* extend_type == EXTEND_UNIX and convert from other type. */
+ time_t unix_last_modified_stamp;
+ unsigned short unix_mode;
+ unsigned short unix_uid;
+ unsigned short unix_gid;
+ char user[256];
+ char group[256];
+} LzHeader;
+
+struct interfacing {
+ FILE *infile;
+ FILE *outfile;
+ size_t original;
+ size_t packed;
+ size_t read_size;
+ int dicbit;
+ int method;
+};
+
+
+typedef void addfile_handler(const char*);
+
+RUBY_EXTERN void cmd_extract(const char* archive_name,
+ addfile_handler hnd);
+
+#define fatal_error rb_fatal
+#define warning rb_warning
+
+RUBY_EXTERN FILE * open_old_archive(const char* archive);
+RUBY_EXTERN boolean need_file(const char*);
+RUBY_EXTERN boolean archive_is_msdos_sfx1(const char*);
+RUBY_EXTERN void output_dyn();
+RUBY_EXTERN void encode_start_fix();
+RUBY_EXTERN void encode_end_dyn();
+RUBY_EXTERN void output_st1();
+RUBY_EXTERN void encode_start_st0();
+RUBY_EXTERN void encode_end_st0();
+RUBY_EXTERN void encode_start_st1();
+RUBY_EXTERN void encode_end_st1();
+RUBY_EXTERN unsigned short decode_c_dyn();
+RUBY_EXTERN unsigned short decode_p_dyn();
+RUBY_EXTERN void decode_start_fix();
+RUBY_EXTERN void decode_start_dyn();
+RUBY_EXTERN void decode_start_st0();
+RUBY_EXTERN void decode_start_st1();
+RUBY_EXTERN unsigned short decode_c_st0();
+RUBY_EXTERN unsigned short decode_c_st1();
+RUBY_EXTERN unsigned short decode_p_st0();
+RUBY_EXTERN unsigned short decode_p_st1();
+RUBY_EXTERN unsigned short decode_c_lzs();
+RUBY_EXTERN unsigned short decode_p_lzs();
+RUBY_EXTERN void decode_start_lzs();
+RUBY_EXTERN unsigned short decode_c_lz5();
+RUBY_EXTERN unsigned short decode_p_lz5();
+RUBY_EXTERN void decode_start_lz5();
+RUBY_EXTERN void make_crctable();
+
+#define start_indicator(name, size, ing, len) 0
+#define finish_indicator(name, ed) 0
+
+#if defined(LHALIB_VERSION)
+ #define LHA_EXTERN
+#else
+ #define LHA_EXTERN RUBY_EXTERN
+#endif
+
+RUBY_EXTERN boolean lha_force;
+RUBY_EXTERN boolean lha_verbose;
+RUBY_EXTERN boolean lha_ignore_directory;
+RUBY_EXTERN boolean extract_broken_archive;
+RUBY_EXTERN boolean lha_noconvertcase;
+RUBY_EXTERN boolean lha_generic_format;
+RUBY_EXTERN boolean lha_text_mode;
+RUBY_EXTERN int lha_n_max;
+RUBY_EXTERN unsigned short lha_maxmatch;
+RUBY_EXTERN int lha_overwrite;
+
+LHA_EXTERN unsigned short bitbuf;
+LHA_EXTERN unsigned short dicbit;
+LHA_EXTERN size_t lha_decode_count;
+LHA_EXTERN size_t lha_origsize;
+LHA_EXTERN size_t lha_compsize;
+LHA_EXTERN int lha_unpackable;
+LHA_EXTERN unsigned long lha_loc;
+LHA_EXTERN unsigned char* lha_text;
+LHA_EXTERN unsigned short lha_left[], lha_right[];
+LHA_EXTERN unsigned char lha_c_len[], lha_pt_len[];
+LHA_EXTERN unsigned short lha_c_freq[], lha_c_table[], lha_c_code[];
+LHA_EXTERN unsigned short lha_p_freq[], lha_pt_table[], lha_pt_code[], lha_t_freq[];
+LHA_EXTERN unsigned int crctable[UCHAR_MAX + 1];
+LHA_EXTERN FILE* infile;
+LHA_EXTERN FILE* outfile;
+LHA_EXTERN int archive_file_gid;
+LHA_EXTERN int archive_file_mode;
+LHA_EXTERN int header_level;
+
+#define force lha_force
+#define verbose lha_verbose
+#define verbose_listing lha_verbose
+#define ignore_directory lha_ignore_directory
+#define n_max lha_n_max
+#define maxmatch lha_maxmatch
+#define decode_count lha_decode_count
+#define unpackable lha_unpackable
+#define left lha_left
+#define right lha_right
+#define c_len lha_c_len
+#define pt_len lha_pt_len
+#define c_freq lha_c_freq
+#define c_table lha_c_table
+#define c_code lha_c_code
+#define p_freq lha_pt_freq
+#define pt_table lha_pt_table
+#define pt_code lha_pt_code
+#define t_freq lha_t_freq
+#define loc lha_loc
+#define text lha_text
+#define origsize lha_origsize
+#define compsize lha_compsize
+#define noconvertcase lha_noconvertcase
+#define generic_format lha_generic_format
+#define text_mode lha_text_mode
+#define overwrite lha_overwrite
--- /dev/null
+/*
+ * LhaLib for Ruby
+ * Copyright(c) 2006 arton
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * $Id:$
+ */
+
+#define LHALIB_VERSION "0.8.0"
+
+#include "lha.h"
+#include "st.h"
+
+static VALUE lhalib;
+
+boolean lha_force = FALSE;
+boolean lha_verbose = FALSE;
+boolean lha_ignore_directory = FALSE;
+boolean extract_broken_archive = FALSE;
+boolean lha_noconvertcase = FALSE;
+boolean lha_generic_format = FALSE;
+boolean lha_text_mode = FALSE;
+int overwrite = 1;
+int lha_n_max;
+unsigned short lha_maxmatch;
+unsigned short bitbuf;
+size_t lha_decode_count;
+int lha_unpackable;
+unsigned long lha_loc;
+unsigned char* lha_text;
+FILE* infile;
+FILE* outfile;
+int archive_file_gid;
+int archive_file_mode;
+
+void add_file(const char* file)
+{
+}
+
+static VALUE s_ex(VALUE self, VALUE file)
+{
+ rb_check_safe_str(file);
+ rb_secure(4);
+ make_crctable();
+ cmd_extract(StringValueCStr(file), add_file);
+}
+
+static VALUE s_open(int argc, VALUE* argv, VALUE self)
+{
+ VALUE file;
+ VALUE proc;
+ rb_scan_args(argc, argv, "1&", &file, &proc);
+ rb_check_safe_str(file);
+ rb_secure(4);
+
+ make_crctable();
+ cmd_extract(StringValueCStr(file), add_file);
+ if (!NIL_P(proc))
+ {
+ rb_yield(file);
+ }
+}
+
+
+/*
+ * Class initializer called by Ruby while requiring this library
+ */
+void Init_lhalib()
+{
+ header_level = 2;
+ lhalib = rb_define_module("LhaLib");
+ rb_define_module_function(lhalib, "x", s_open, -1);
+ rb_define_module_function(lhalib, "x", s_ex, 1);
+ rb_define_const(lhalib, "VERSION", rb_str_new2(LHALIB_VERSION));
+
+}
--- /dev/null
+* (16 Apr 2009)
+
+Allow :with_deleted and :only_deleted options to work with count and calculate.
+Fixes compatibility with will_paginate. [James Le Cuirot]
+
+* (4 Oct 2007)
+
+Update for Edge rails: remove support for legacy #count args
+
+* (2 Feb 2007)
+
+Add support for custom primary keys [Jeff Dean]
+
+* (2 July 2006)
+
+Add paranoid delete_all implementation [Marshall Roch]
+
+* (23 May 2006)
+
+Allow setting of future dates for content expiration.
+
+* (15 May 2006)
+
+Added support for dynamic finders
+
+* (28 Mar 2006)
+
+Updated for Rails 1.1. I love removing code.
+
+ Refactored #find method
+ Nested Scopes
+
+*0.3.1* (20 Dec 2005)
+
+* took out deleted association code for 'chainsaw butchery of base classes' [sorry Erik Terpstra]
+* verified tests pass on Rails 1.0
+
+*0.3* (27 Nov 2005)
+
+* Deleted models will find deleted associations by default now [Erik Terpstra]
+* Added :group as valid option for find [Michael Dabney]
+* Changed the module namespace to Caboose::Acts::Paranoid
+
+*0.2.0* (6 Nov 2005)
+
+* Upgrade to Rails 1.0 RC4. ActiveRecord::Base#constrain has been replaced with scope_with.
+
+*0.1.7* (22 Oct 2005)
+
+* Added :with_deleted as a valid option of ActiveRecord::Base#find
+
+*0.1.6* (25 Sep 2005)
+
+* Fixed bug where nested constrains would get clobbered after multiple queries
+
+*0.1.5* (22 Sep 2005)
+
+* Fixed bug where acts_as_paranoid would clobber other constrains
+* Simplified acts_as_paranoid mixin including.
+
+*0.1.4* (18 Sep 2005)
+
+* First RubyForge release
+
+*0.1.3* (18 Sep 2005)
+
+* ignore multiple calls to acts_as_paranoid on the same model
+
+*0.1.2* (18 Sep 2005)
+
+* fixed a bug that kept you from selecting the first deleted record
+
+*0.1.1* (18 Sep 2005)
+
+* Fixed bug that kept you from selecting deleted records by ID
+
+*0.1* (17 Sep 2005)
+
+* Initial gem
--- /dev/null
+Copyright (c) 2005 Rick Olson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
--- /dev/null
+= acts_as_paranoid
+
+Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the current timestamp. ActiveRecord is required.
+
+http://github.com/technoweenie/acts_as_paranoid
\ No newline at end of file
--- /dev/null
+1. Pick Rails version. Either dump this plugin in a Rails app and run it from there, or specify it as an ENV var:
+
+ RAILS=2.2.2 rake
+ RAILS=2.2.2 ruby test/paranoid_test.rb
+
+2. Setup your database. By default sqlite3 is used, and no further setup is necessary. You can pick any of the listed databases in test/database.yml. Be sure to create the database first.
+
+ DB=mysql rake
+
+3. Profit!!
\ No newline at end of file
--- /dev/null
+require 'rubygems'
+
+Gem::manage_gems
+
+require 'rake/rdoctask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+require 'rake/testtask'
+require 'rake/contrib/rubyforgepublisher'
+
+PKG_NAME = 'acts_as_paranoid'
+PKG_VERSION = '0.3.1'
+PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
+PROD_HOST = "technoweenie@bidwell.textdrive.com"
+RUBY_FORGE_PROJECT = 'ar-paranoid'
+RUBY_FORGE_USER = 'technoweenie'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the calculations plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the acts_as_paranoid plugin.'
+Rake::RDocTask.new do |rdoc|
+ rdoc.rdoc_dir = 'html'
+ rdoc.title = "#{PKG_NAME} -- protect your ActiveRecord objects from accidental deletion"
+ rdoc.options << '--line-numbers --inline-source --accessor cattr_accessor=object'
+ rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+ rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+spec = Gem::Specification.new do |s|
+ s.name = PKG_NAME
+ s.version = PKG_VERSION
+ s.platform = Gem::Platform::RUBY
+ s.summary = "acts_as_paranoid keeps models from actually being deleted by setting a deleted_at field."
+ s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS)
+ s.files.delete "acts_as_paranoid_plugin.sqlite.db"
+ s.files.delete "acts_as_paranoid_plugin.sqlite3.db"
+ s.require_path = 'lib'
+ s.autorequire = 'acts_as_paranoid'
+ s.has_rdoc = true
+ s.test_files = Dir['test/**/*_test.rb']
+ s.author = "Rick Olson"
+ s.email = "technoweenie@gmail.com"
+ s.homepage = "http://techno-weenie.net"
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.need_tar = true
+end
+
+desc "Publish the API documentation"
+task :pdoc => [:rdoc] do
+ Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
+end
+
+desc 'Publish the gem and API docs'
+task :publish => [:pdoc, :rubyforge_upload]
+
+desc "Publish the release files to RubyForge."
+task :rubyforge_upload => :package do
+ files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
+
+ if RUBY_FORGE_PROJECT then
+ require 'net/http'
+ require 'open-uri'
+
+ project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
+ project_data = open(project_uri) { |data| data.read }
+ group_id = project_data[/[?&]group_id=(\d+)/, 1]
+ raise "Couldn't get group id" unless group_id
+
+ # This echos password to shell which is a bit sucky
+ if ENV["RUBY_FORGE_PASSWORD"]
+ password = ENV["RUBY_FORGE_PASSWORD"]
+ else
+ print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
+ password = STDIN.gets.chomp
+ end
+
+ login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
+ data = [
+ "login=1",
+ "form_loginname=#{RUBY_FORGE_USER}",
+ "form_pw=#{password}"
+ ].join("&")
+ http.post("/account/login.php", data)
+ end
+
+ cookie = login_response["set-cookie"]
+ raise "Login failed" unless cookie
+ headers = { "Cookie" => cookie }
+
+ release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
+ release_data = open(release_uri, headers) { |data| data.read }
+ package_id = release_data[/[?&]package_id=(\d+)/, 1]
+ raise "Couldn't get package id" unless package_id
+
+ first_file = true
+ release_id = ""
+
+ files.each do |filename|
+ basename = File.basename(filename)
+ file_ext = File.extname(filename)
+ file_data = File.open(filename, "rb") { |file| file.read }
+
+ puts "Releasing #{basename}..."
+
+ release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
+ release_date = Time.now.strftime("%Y-%m-%d %H:%M")
+ type_map = {
+ ".zip" => "3000",
+ ".tgz" => "3110",
+ ".gz" => "3110",
+ ".gem" => "1400"
+ }; type_map.default = "9999"
+ type = type_map[file_ext]
+ boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
+
+ query_hash = if first_file then
+ {
+ "group_id" => group_id,
+ "package_id" => package_id,
+ "release_name" => PKG_FILE_NAME,
+ "release_date" => release_date,
+ "type_id" => type,
+ "processor_id" => "8000", # Any
+ "release_notes" => "",
+ "release_changes" => "",
+ "preformatted" => "1",
+ "submit" => "1"
+ }
+ else
+ {
+ "group_id" => group_id,
+ "release_id" => release_id,
+ "package_id" => package_id,
+ "step2" => "1",
+ "type_id" => type,
+ "processor_id" => "8000", # Any
+ "submit" => "Add This File"
+ }
+ end
+
+ query = "?" + query_hash.map do |(name, value)|
+ [name, URI.encode(value)].join("=")
+ end.join("&")
+
+ data = [
+ "--" + boundary,
+ "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
+ "Content-Type: application/octet-stream",
+ "Content-Transfer-Encoding: binary",
+ "", file_data, ""
+ ].join("\x0D\x0A")
+
+ release_headers = headers.merge(
+ "Content-Type" => "multipart/form-data; boundary=#{boundary}"
+ )
+
+ target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
+ http.post(target + query, data, release_headers)
+ end
+
+ if first_file then
+ release_id = release_response.body[/release_id=(\d+)/, 1]
+ raise("Couldn't get release id") unless release_id
+ end
+
+ first_file = false
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+class << ActiveRecord::Base
+ def belongs_to_with_deleted(association_id, options = {})
+ with_deleted = options.delete :with_deleted
+ belongs_to_without_deleted(association_id, options).tap do
+ if with_deleted
+ reflection = reflect_on_association(association_id)
+ association_accessor_methods(reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
+ association_constructor_method(:build, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
+ association_constructor_method(:create, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
+ end
+ end
+ end
+
+ def has_many_without_deleted(association_id, options = {}, &extension)
+ with_deleted = options.delete :with_deleted
+ has_many_with_deleted(association_id, options, &extension).tap do
+ if options[:through] && !with_deleted
+ reflection = reflect_on_association(association_id)
+ collection_reader_method(reflection, Caboose::Acts::HasManyThroughWithoutDeletedAssociation)
+ collection_accessor_methods(reflection, Caboose::Acts::HasManyThroughWithoutDeletedAssociation, false)
+ end
+ end
+ end
+
+ alias_method_chain :belongs_to, :deleted
+ alias_method :has_many_with_deleted, :has_many
+ alias_method :has_many, :has_many_without_deleted
+ alias_method :exists_with_deleted?, :exists?
+end
+ActiveRecord::Base.send :include, Caboose::Acts::Paranoid
+ActiveRecord::Base.send :include, Caboose::Acts::ParanoidFindWrapper
+class << ActiveRecord::Base
+ alias_method_chain :acts_as_paranoid, :find_wrapper
+end
--- /dev/null
+module Caboose # :nodoc:
+ module Acts # :nodoc:
+ class BelongsToWithDeletedAssociation < ActiveRecord::Associations::BelongsToAssociation
+ private
+ def find_target
+ @reflection.klass.find_with_deleted(
+ @owner[@reflection.primary_key_name],
+ :conditions => conditions,
+ :include => @reflection.options[:include]
+ )
+ end
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+module Caboose # :nodoc:
+ module Acts # :nodoc:
+ class HasManyThroughWithoutDeletedAssociation < ActiveRecord::Associations::HasManyThroughAssociation
+ protected
+ def current_time
+ ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
+ end
+
+ def construct_conditions
+ return super unless @reflection.through_reflection.klass.paranoid?
+ table_name = @reflection.through_reflection.table_name
+ conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
+ "#{table_name}.#{attr} = #{value}"
+ end
+
+ deleted_attribute = @reflection.through_reflection.klass.deleted_attribute
+ quoted_current_time = @reflection.through_reflection.klass.quote_value(
+ current_time,
+ @reflection.through_reflection.klass.columns_hash[deleted_attribute.to_s])
+ conditions << "#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > #{quoted_current_time}"
+
+ conditions << sql_conditions if sql_conditions
+ "(" + conditions.join(') AND (') + ")"
+ end
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+module Caboose #:nodoc:
+ module Acts #:nodoc:
+ # Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the current timestamp.
+ # This assumes the table has a deleted_at date/time field. Most normal model operations will work, but there will be some oddities.
+ #
+ # class Widget < ActiveRecord::Base
+ # acts_as_paranoid
+ # end
+ #
+ # Widget.find(:all)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
+ #
+ # Widget.find(:first, :conditions => ['title = ?', 'test'], :order => 'title')
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test' ORDER BY title LIMIT 1
+ #
+ # Widget.find_with_deleted(:all)
+ # # SELECT * FROM widgets
+ #
+ # Widget.find_only_deleted(:all)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
+ #
+ # Widget.find_with_deleted(1).deleted?
+ # # Returns true if the record was previously destroyed, false if not
+ #
+ # Widget.count
+ # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL
+ #
+ # Widget.count ['title = ?', 'test']
+ # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test'
+ #
+ # Widget.count_with_deleted
+ # # SELECT COUNT(*) FROM widgets
+ #
+ # Widget.count_only_deleted
+ # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NOT NULL
+ #
+ # Widget.delete_all
+ # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36'
+ #
+ # Widget.delete_all!
+ # # DELETE FROM widgets
+ #
+ # @widget.destroy
+ # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' WHERE id = 1
+ #
+ # @widget.destroy!
+ # # DELETE FROM widgets WHERE id = 1
+ #
+ module Paranoid
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_paranoid(options = {})
+ unless paranoid? # don't let AR call this twice
+ cattr_accessor :deleted_attribute
+ self.deleted_attribute = options[:with] || :deleted_at
+ alias_method :destroy_without_callbacks!, :destroy_without_callbacks
+ class << self
+ alias_method :find_every_with_deleted, :find_every
+ alias_method :calculate_with_deleted, :calculate
+ alias_method :delete_all!, :delete_all
+ end
+ end
+ include InstanceMethods
+ end
+
+ def paranoid?
+ self.included_modules.include?(InstanceMethods)
+ end
+ end
+
+ module InstanceMethods #:nodoc:
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ def find_with_deleted(*args)
+ options = args.extract_options!
+ validate_find_options(options)
+ set_readonly_option!(options)
+ options[:with_deleted] = true # yuck!
+
+ case args.first
+ when :first then find_initial(options)
+ when :all then find_every(options)
+ else find_from_ids(args, options)
+ end
+ end
+
+ def find_only_deleted(*args)
+ options = args.extract_options!
+ validate_find_options(options)
+ set_readonly_option!(options)
+ options[:only_deleted] = true # yuck!
+
+ case args.first
+ when :first then find_initial(options)
+ when :all then find_every(options)
+ else find_from_ids(args, options)
+ end
+ end
+
+ def exists?(*args)
+ with_deleted_scope { exists_with_deleted?(*args) }
+ end
+
+ def exists_only_deleted?(*args)
+ with_only_deleted_scope { exists_with_deleted?(*args) }
+ end
+
+ def count_with_deleted(*args)
+ calculate_with_deleted(:count, *construct_count_options_from_args(*args))
+ end
+
+ def count_only_deleted(*args)
+ with_only_deleted_scope { count_with_deleted(*args) }
+ end
+
+ def count(*args)
+ with, only = extract_deleted_options(args.last) if args.last.is_a?(Hash)
+
+ with ? count_with_deleted(*args) :
+ only ? count_only_deleted(*args) :
+ with_deleted_scope { count_with_deleted(*args) }
+ end
+
+ def calculate(*args)
+ with, only = extract_deleted_options(args.last) if args.last.is_a?(Hash)
+
+ with ? calculate_with_deleted(*args) :
+ only ? calculate_only_deleted(*args) :
+ with_deleted_scope { calculate_with_deleted(*args) }
+ end
+
+ def delete_all(conditions = nil)
+ self.update_all ["#{self.deleted_attribute} = ?", current_time], conditions
+ end
+
+ protected
+ def current_time
+ default_timezone == :utc ? Time.now.utc : Time.now
+ end
+
+ def with_deleted_scope(&block)
+ with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > ?", current_time] } }, :merge, &block)
+ end
+
+ def with_only_deleted_scope(&block)
+ with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NOT NULL AND #{table_name}.#{deleted_attribute} <= ?", current_time] } }, :merge, &block)
+ end
+
+ private
+ # all find calls lead here
+ def find_every(options)
+ with, only = extract_deleted_options(options)
+
+ with ? find_every_with_deleted(options) :
+ only ? with_only_deleted_scope { find_every_with_deleted(options) } :
+ with_deleted_scope { find_every_with_deleted(options) }
+ end
+
+ def extract_deleted_options(options)
+ return options.delete(:with_deleted), options.delete(:only_deleted)
+ end
+ end
+
+ def destroy_without_callbacks
+ unless new_record?
+ self.class.update_all self.class.send(:sanitize_sql, ["#{self.class.deleted_attribute} = ?", (self.deleted_at = self.class.send(:current_time))]), ["#{self.class.primary_key} = ?", id]
+ end
+ freeze
+ end
+
+ def destroy_with_callbacks!
+ return false if callback(:before_destroy) == false
+ result = destroy_without_callbacks!
+ callback(:after_destroy)
+ result
+ end
+
+ def destroy!
+ transaction { destroy_with_callbacks! }
+ end
+
+ def deleted?
+ !!read_attribute(:deleted_at)
+ end
+
+ def recover!
+ self.deleted_at = nil
+ save!
+ end
+
+ def recover_with_associations!(*associations)
+ self.recover!
+ associations.to_a.each do |assoc|
+ self.send(assoc).find_with_deleted(:all).each do |a|
+ a.recover! if a.class.paranoid?
+ end
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+module Caboose #:nodoc:
+ module Acts #:nodoc:
+ # Adds a wrapper find method which can identify :with_deleted or :only_deleted options
+ # and would call the corresponding acts_as_paranoid finders find_with_deleted or
+ # find_only_deleted methods.
+ #
+ # With this wrapper you can easily change from using this pattern:
+ #
+ # if some_condition_enabling_access_to_deleted_records?
+ # @post = Post.find_with_deleted(params[:id])
+ # else
+ # @post = Post.find(params[:id])
+ # end
+ #
+ # to this:
+ #
+ # @post = Post.find(params[:id], :with_deleted => some_condition_enabling_access_to_deleted_records?)
+ #
+ # Examples
+ #
+ # class Widget < ActiveRecord::Base
+ # acts_as_paranoid
+ # end
+ #
+ # Widget.find(:all)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
+ #
+ # Widget.find(:all, :with_deleted => false)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
+ #
+ # Widget.find_with_deleted(:all)
+ # # SELECT * FROM widgets
+ #
+ # Widget.find(:all, :with_deleted => true)
+ # # SELECT * FROM widgets
+ #
+ # Widget.find_only_deleted(:all)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
+ #
+ # Widget.find(:all, :only_deleted => true)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL
+ #
+ # Widget.find(:all, :only_deleted => false)
+ # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
+ #
+ module ParanoidFindWrapper
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_paranoid_with_find_wrapper(options = {})
+ unless paranoid? # don't let AR call this twice
+ acts_as_paranoid_without_find_wrapper(options)
+ class << self
+ alias_method :find_without_find_wrapper, :find
+ alias_method :validate_find_options_without_find_wrapper, :validate_find_options
+ end
+ end
+ include InstanceMethods
+ end
+ end
+
+ module InstanceMethods #:nodoc:
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # This is a wrapper for the regular "find" so you can pass acts_as_paranoid related
+ # options and determine which finder to call.
+ def find(*args)
+ options = args.extract_options!
+ # Determine who to call.
+ finder_option = VALID_PARANOID_FIND_OPTIONS.detect { |key| options.delete(key) } || :without_find_wrapper
+ finder_method = "find_#{finder_option}".to_sym
+ # Put back the options in the args now that they don't include the extended keys.
+ args << options
+ send(finder_method, *args)
+ end
+
+ protected
+
+ VALID_PARANOID_FIND_OPTIONS = [:with_deleted, :only_deleted]
+
+ def validate_find_options(options) #:nodoc:
+ cleaned_options = options.reject { |k, v| VALID_PARANOID_FIND_OPTIONS.include?(k) }
+ validate_find_options_without_find_wrapper(cleaned_options)
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+sqlite:
+ :adapter: sqlite
+ :dbfile: acts_as_paranoid_plugin.sqlite.db
+sqlite3:
+ :adapter: sqlite3
+ :dbfile: acts_as_paranoid_plugin.sqlite3.db
+postgresql:
+ :adapter: postgresql
+ :username: postgres
+ :password: postgres
+ :database: acts_as_paranoid_plugin_test
+ :min_messages: ERROR
+mysql:
+ :adapter: mysql
+ :host: localhost
+ :username: rails
+ :password:
+ :database: acts_as_paranoid_plugin_test
\ No newline at end of file
--- /dev/null
+category_1:
+ id: 1
+ widget_id: 1
+ title: 'category 1'
+category_2:
+ id: 2
+ widget_id: 1
+ title: 'category 2'
+ deleted_at: '2005-01-01 00:00:00'
+category_3:
+ id: 3
+ widget_id: 2
+ title: 'category 3'
+ deleted_at: '2005-01-01 00:00:00'
+category_4:
+ id: 4
+ widget_id: 2
+ title: 'category 4'
+ deleted_at: '2005-01-01 00:00:00'
\ No newline at end of file
--- /dev/null
+cw_1:
+ category_id: 1
+ widget_id: 1
+cw_2:
+ category_id: 2
+ widget_id: 1
+cw_3:
+ category_id: 3
+ widget_id: 2
+cw_4:
+ category_id: 4
+ widget_id: 2
\ No newline at end of file
--- /dev/null
+tagging_1:
+ id: 1
+ tag_id: 1
+ widget_id: 1
+ deleted_at: '2005-01-01 00:00:00'
+tagging_2:
+ id: 2
+ tag_id: 2
+ widget_id: 1
--- /dev/null
+tag_1:
+ id: 1
+ name: 'tag 1'
+tag_2:
+ id: 2
+ name: 'tag 1'
--- /dev/null
+widget_1:
+ id: 1
+ title: 'widget 1'
+widget_2:
+ id: 2
+ title: 'deleted widget 2'
+ deleted_at: '2005-01-01 00:00:00'
+ category_id: 3
--- /dev/null
+require File.join(File.dirname(__FILE__), 'test_helper')
+
+class Widget < ActiveRecord::Base
+ acts_as_paranoid
+ has_many :categories, :dependent => :destroy
+ has_and_belongs_to_many :habtm_categories, :class_name => 'Category'
+ has_one :category
+ belongs_to :parent_category, :class_name => 'Category'
+ has_many :taggings
+ has_many :tags, :through => :taggings
+ has_many :any_tags, :through => :taggings, :class_name => 'Tag', :source => :tag, :with_deleted => true
+end
+
+class Category < ActiveRecord::Base
+ belongs_to :widget
+ belongs_to :any_widget, :class_name => 'Widget', :foreign_key => 'widget_id', :with_deleted => true
+ acts_as_paranoid
+
+ def self.search(name, options = {})
+ find :all, options.merge(:conditions => ['LOWER(title) LIKE ?', "%#{name.to_s.downcase}%"])
+ end
+
+ def self.search_with_deleted(name, options = {})
+ find_with_deleted :all, options.merge(:conditions => ['LOWER(title) LIKE ?', "%#{name.to_s.downcase}%"])
+ end
+end
+
+class Tag < ActiveRecord::Base
+ has_many :taggings
+ has_many :widgets, :through => :taggings
+end
+
+class Tagging < ActiveRecord::Base
+ belongs_to :tag
+ belongs_to :widget
+ acts_as_paranoid
+end
+
+class NonParanoidAndroid < ActiveRecord::Base
+end
+
+class ParanoidTest < Test::Unit::TestCase
+ fixtures :widgets, :categories, :categories_widgets, :tags, :taggings
+
+ def test_should_recognize_with_deleted_option
+ assert_equal [1, 2], Widget.find(:all, :with_deleted => true).collect { |w| w.id }
+ assert_equal [1], Widget.find(:all, :with_deleted => false).collect { |w| w.id }
+ end
+
+ def test_should_recognize_only_deleted_option
+ assert_equal [2], Widget.find(:all, :only_deleted => true).collect { |w| w.id }
+ assert_equal [1], Widget.find(:all, :only_deleted => false).collect { |w| w.id }
+ end
+
+ def test_should_exists_with_deleted
+ assert Widget.exists_with_deleted?(2)
+ assert !Widget.exists?(2)
+ end
+
+ def test_should_exists_only_deleted
+ assert Widget.exists_only_deleted?(2)
+ assert !Widget.exists_only_deleted?(1)
+ end
+
+ def test_should_count_with_deleted
+ assert_equal 1, Widget.count
+ assert_equal 2, Widget.count_with_deleted
+ assert_equal 1, Widget.count_only_deleted
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_set_deleted_at
+ assert_equal 1, Widget.count
+ assert_equal 1, Category.count
+ widgets(:widget_1).destroy
+ assert_equal 0, Widget.count
+ assert_equal 0, Category.count
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ assert_equal 4, Category.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_destroy
+ assert_equal 1, Widget.count
+ assert_equal 1, Category.count
+ widgets(:widget_1).destroy!
+ assert_equal 0, Widget.count
+ assert_equal 0, Category.count
+ assert_equal 1, Widget.count_only_deleted
+ assert_equal 1, Widget.calculate_with_deleted(:count, :all)
+ # Category doesn't get destroyed because the dependent before_destroy callback uses #destroy
+ assert_equal 4, Category.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_delete_all
+ assert_equal 1, Widget.count
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ assert_equal 1, Category.count
+ Widget.delete_all
+ assert_equal 0, Widget.count
+ # delete_all doesn't call #destroy, so the dependent callback never fires
+ assert_equal 1, Category.count
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_delete_all_with_conditions
+ assert_equal 1, Widget.count
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ Widget.delete_all("id < 3")
+ assert_equal 0, Widget.count
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_delete_all2
+ assert_equal 1, Category.count
+ assert_equal 4, Category.calculate_with_deleted(:count, :all)
+ Category.delete_all!
+ assert_equal 0, Category.count
+ assert_equal 0, Category.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_delete_all_with_conditions2
+ assert_equal 1, Category.count
+ assert_equal 4, Category.calculate_with_deleted(:count, :all)
+ Category.delete_all!("id < 3")
+ assert_equal 0, Category.count
+ assert_equal 2, Category.calculate_with_deleted(:count, :all)
+ end
+
+ def test_should_not_count_deleted
+ assert_equal 1, Widget.count
+ assert_equal 1, Widget.count(:all, :conditions => ['title=?', 'widget 1'])
+ assert_equal 2, Widget.calculate_with_deleted(:count, :all)
+ assert_equal 1, Widget.count_only_deleted
+ end
+
+ def test_should_find_only_deleted
+ assert_equal [2], Widget.find_only_deleted(:all).collect { |w| w.id }
+ assert_equal [1, 2], Widget.find_with_deleted(:all, :order => 'id').collect { |w| w.id }
+ end
+
+ def test_should_not_find_deleted
+ assert_equal [widgets(:widget_1)], Widget.find(:all)
+ assert_equal [1, 2], Widget.find_with_deleted(:all, :order => 'id').collect { |w| w.id }
+ end
+
+ def test_should_not_find_deleted_has_many_associations
+ assert_equal 1, widgets(:widget_1).categories.size
+ assert_equal [categories(:category_1)], widgets(:widget_1).categories
+ end
+
+ def test_should_not_find_deleted_habtm_associations
+ assert_equal 1, widgets(:widget_1).habtm_categories.size
+ assert_equal [categories(:category_1)], widgets(:widget_1).habtm_categories
+ end
+
+ def test_should_not_find_deleted_has_many_through_associations
+ assert_equal 1, widgets(:widget_1).tags.size
+ assert_equal [tags(:tag_2)], widgets(:widget_1).tags
+ end
+
+ def test_should_find_has_many_through_associations_with_deleted
+ assert_equal 2, widgets(:widget_1).any_tags.size
+ assert_equal Tag.find(:all), widgets(:widget_1).any_tags
+ end
+
+ def test_should_not_find_deleted_belongs_to_associations
+ assert_nil Category.find_with_deleted(3).widget
+ end
+
+ def test_should_find_belongs_to_assocation_with_deleted
+ assert_equal Widget.find_with_deleted(2), Category.find_with_deleted(3).any_widget
+ end
+
+ def test_should_find_first_with_deleted
+ assert_equal widgets(:widget_1), Widget.find(:first)
+ assert_equal 2, Widget.find_with_deleted(:first, :order => 'id desc').id
+ end
+
+ def test_should_find_single_id
+ assert Widget.find(1)
+ assert Widget.find_with_deleted(2)
+ assert_raises(ActiveRecord::RecordNotFound) { Widget.find(2) }
+ end
+
+ def test_should_find_multiple_ids
+ assert_equal [1,2], Widget.find_with_deleted(1,2).sort_by { |w| w.id }.collect { |w| w.id }
+ assert_equal [1,2], Widget.find_with_deleted([1,2]).sort_by { |w| w.id }.collect { |w| w.id }
+ assert_raises(ActiveRecord::RecordNotFound) { Widget.find(1,2) }
+ end
+
+ def test_should_ignore_multiple_includes
+ Widget.class_eval { acts_as_paranoid }
+ assert Widget.find(1)
+ end
+
+ def test_should_not_override_scopes_when_counting
+ assert_equal 1, Widget.send(:with_scope, :find => { :conditions => "title = 'widget 1'" }) { Widget.count }
+ assert_equal 0, Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.count }
+ assert_equal 1, Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.calculate_with_deleted(:count, :all) }
+ end
+
+ def test_should_not_override_scopes_when_finding
+ assert_equal [1], Widget.send(:with_scope, :find => { :conditions => "title = 'widget 1'" }) { Widget.find(:all) }.ids
+ assert_equal [], Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.find(:all) }.ids
+ assert_equal [2], Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.find_with_deleted(:all) }.ids
+ end
+
+ def test_should_allow_multiple_scoped_calls_when_finding
+ Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) do
+ assert_equal [2], Widget.find_with_deleted(:all).ids
+ assert_equal [2], Widget.find_with_deleted(:all).ids, "clobbers the constrain on the unmodified find"
+ assert_equal [], Widget.find(:all).ids
+ assert_equal [], Widget.find(:all).ids, 'clobbers the constrain on a paranoid find'
+ end
+ end
+
+ def test_should_allow_multiple_scoped_calls_when_counting
+ Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) do
+ assert_equal 1, Widget.calculate_with_deleted(:count, :all)
+ assert_equal 1, Widget.calculate_with_deleted(:count, :all), "clobbers the constrain on the unmodified find"
+ assert_equal 0, Widget.count
+ assert_equal 0, Widget.count, 'clobbers the constrain on a paranoid find'
+ end
+ end
+
+ def test_should_give_paranoid_status
+ assert Widget.paranoid?
+ assert !NonParanoidAndroid.paranoid?
+ end
+
+ def test_should_give_record_status
+ assert_equal false, Widget.find(1).deleted?
+ Widget.find(1).destroy
+ assert Widget.find_with_deleted(1).deleted?
+ end
+
+ def test_should_find_deleted_has_many_assocations_on_deleted_records_by_default
+ w = Widget.find_with_deleted 2
+ assert_equal 2, w.categories.find_with_deleted(:all).length
+ assert_equal 2, w.categories.find_with_deleted(:all).size
+ end
+
+ def test_should_find_deleted_habtm_assocations_on_deleted_records_by_default
+ w = Widget.find_with_deleted 2
+ assert_equal 2, w.habtm_categories.find_with_deleted(:all).length
+ assert_equal 2, w.habtm_categories.find_with_deleted(:all).size
+ end
+
+ def test_dynamic_finders
+ assert Widget.find_by_id(1)
+ assert_nil Widget.find_by_id(2)
+ end
+
+ def test_custom_finder_methods
+ w = Widget.find_with_deleted(:all).inject({}) { |all, w| all.merge(w.id => w) }
+ assert_equal [1], Category.search('c').ids
+ assert_equal [1,2,3,4], Category.search_with_deleted('c', :order => 'id').ids
+ assert_equal [1], widgets(:widget_1).categories.search('c').collect(&:id)
+ assert_equal [1,2], widgets(:widget_1).categories.search_with_deleted('c').ids
+ assert_equal [], w[2].categories.search('c').ids
+ assert_equal [3,4], w[2].categories.search_with_deleted('c').ids
+ end
+
+ def test_should_recover_record
+ Widget.find(1).destroy
+ assert_equal true, Widget.find_with_deleted(1).deleted?
+
+ Widget.find_with_deleted(1).recover!
+ assert_equal false, Widget.find(1).deleted?
+ end
+
+ def test_should_recover_record_and_has_many_associations
+ Widget.find(1).destroy
+ assert_equal true, Widget.find_with_deleted(1).deleted?
+ assert_equal true, Category.find_with_deleted(1).deleted?
+
+ Widget.find_with_deleted(1).recover_with_associations!(:categories)
+ assert_equal false, Widget.find(1).deleted?
+ assert_equal false, Category.find(1).deleted?
+ end
+end
+
+class Array
+ def ids
+ collect &:id
+ end
+end
--- /dev/null
+ActiveRecord::Schema.define(:version => 1) do
+
+ create_table :widgets, :force => true do |t|
+ t.column :title, :string, :limit => 50
+ t.column :category_id, :integer
+ t.column :deleted_at, :timestamp
+ end
+
+ create_table :categories, :force => true do |t|
+ t.column :widget_id, :integer
+ t.column :title, :string, :limit => 50
+ t.column :deleted_at, :timestamp
+ end
+
+ create_table :categories_widgets, :force => true, :id => false do |t|
+ t.column :category_id, :integer
+ t.column :widget_id, :integer
+ end
+
+ create_table :tags, :force => true do |t|
+ t.column :name, :string, :limit => 50
+ end
+
+ create_table :taggings, :force => true do |t|
+ t.column :tag_id, :integer
+ t.column :widget_id, :integer
+ t.column :deleted_at, :timestamp
+ end
+
+end
\ No newline at end of file
--- /dev/null
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+
+require 'test/unit'
+require 'rubygems'
+if ENV['RAILS'].nil?
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
+else
+ # specific rails version targeted
+ # load activerecord and plugin manually
+ gem 'activerecord', "=#{ENV['RAILS']}"
+ require 'active_record'
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
+ Dir["#{$LOAD_PATH.last}/**/*.rb"].each do |path|
+ require path[$LOAD_PATH.last.size + 1..-1]
+ end
+ require File.join(File.dirname(__FILE__), '..', 'init.rb')
+end
+require 'active_record/fixtures'
+
+config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
+# do this so fixtures will load
+ActiveRecord::Base.configurations.update config
+ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
+ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
+
+load(File.dirname(__FILE__) + "/schema.rb")
+
+Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
+$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
+
+class Test::Unit::TestCase #:nodoc:
+ def create_fixtures(*table_names)
+ if block_given?
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
+ else
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
+ end
+ end
+
+ # Turn off transactional fixtures if you're working with MyISAM tables in MySQL
+ self.use_transactional_fixtures = true
+
+ # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
+ self.use_instantiated_fixtures = false
+
+ # Add more helper methods to be used by all tests here...
+end
--- /dev/null
+= Double Submit Protection Plugin =
+===================================
+
+This plugin implements a symetrical token approach to avoid duplicate posts of data. This is specially
+useful when you're dealing with data-sensitive forms (credit-card processing, for instance), where
+a duplicated posting may lead to a user getting charged twice. This control is done via a
+token placed on user's form and synchronized with the session - whenever the token is different, you
+can simply verify it on the controllers by using the 'double_submit?' method
+
+Version History
+===============
+
+v1.0
+- Initial release of the plugin
+
+v1.0.1
+- Use flash in place of session
+
+v1.0.2
+- Support multiple token
+
+Usage Example
+=============
+
+some_view.html.erb
+----------------------------------------------------------------------
+<%= form_for :blah, :url => { :action => "create" } do |f| %>
+ ... (some form content here)
+ <%=double_submit_token %>
+<% end %>
+
+<%= form_for :foo, :url => { :action => "create" } do |f| %>
+ ... (some form content here)
+ <%=double_submit_token 'foo_submit_token' %>
+<% end %>
+----------------------------------------------------------------------
+
+blah_controller.rb
+----------------------------------------------------------------------
+ def create
+ if double_submit?
+ flash[:message] = 'Whoa, hang in there dude...'
+ render :action => :register
+ return
+ end
+
+ # do something here
+ end
+end
+----------------------------------------------------------------------
+
+foo_controller.rb
+----------------------------------------------------------------------
+ def create
+ if double_submit? 'foo_submit_token'
+ flash[:message] = 'Whoa, hang in there dude...'
+ render :action => :register
+ return
+ end
+
+ # do something here
+ end
+end
+----------------------------------------------------------------------
+
+Inspired by https://github.com/herval/double_submit_protection
+==============================================================
+Copyright (C) 2008 Herval Freire (hervalfreire@gmail.com). No license intended - you are free to duplicate,
+immitate, print this code and shred it to pieces, blog about it, take pictures with it and even say it was
+ brilliantly developed by yourself and brag about it.
+
+===================================
+Dianthu Dia (dianthudia@y7mail.com)
--- /dev/null
+$:.unshift "#{File.dirname(__FILE__)}/lib"
+require 'double_submit_protection'
+
+
+ActionView::Base.class_eval do
+ include Hervalicious::DoubleSubmitProtection::View
+end
+
+
+ActionController::Base.class_eval do
+ include Hervalicious::DoubleSubmitProtection::Controller
+end
--- /dev/null
+module Hervalicious
+ module DoubleSubmitProtection
+
+ DEFAULT_TOKEN_NAME = 'submit_token'
+
+ module View
+ def double_submit_token(token_name=nil)
+ token_name ||= DEFAULT_TOKEN_NAME
+ flash[token_name] = Digest::MD5.hexdigest(rand.to_s)
+ hidden_field_tag(token_name, flash[token_name])
+ end
+ end
+
+ module Controller
+ def double_submit?(token_name=nil)
+ token_name ||= DEFAULT_TOKEN_NAME
+ token = flash[token_name]
+ token.nil? || ( (request.post? || request.put?) && (token != params[token_name]) )
+ end
+ end
+ end
+end
+
--- /dev/null
+Copyright (c) 2010 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+ImageSubmitTagExt
+=================
+
+Added :disable option to image_submit_tag helper, which functions like :disable_with option of submit_tag, prevents the submit button being pressed when the form is submitted.
+
+
+Example
+=======
+
+image_submit_tag("submit.png", :disable => true)
+# => <input onclick="this.disabled = true;result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());if (result == false) { this.disabled = false; }return result;" src="/images/submit.png" type="image" />
+
+Copyright (c) 2010 Junya Ishihara, released under the MIT license
--- /dev/null
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the image_submit_tag_ext plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.libs << 'test'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the image_submit_tag_ext plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'ImageSubmitTagExt'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
--- /dev/null
+# Include hook code here
+require 'image_submit_tag_ext'
--- /dev/null
+# Install hook code here
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# ImageSubmitTagExt
+#
+# Added :disable option to image_submit_tag helper, which functions like :disable_with option of submit_tag,
+# prevents the submit button being pressed when the form is submitted.
+#
+# Released under the MIT license
+#
+# Junya Ishihara <junya@champierre.com>
+# http://champierre.com
+#
+
+module ActionView
+ module Helpers
+ module FormTagHelper
+ def image_submit_tag(source, options = {})
+ options.stringify_keys!
+ if confirm = options.delete("confirm")
+ options["onclick"] ||= ''
+ options["onclick"] += "return #{confirm_javascript_function(confirm)};"
+ end
+
+ if disable = options.delete("disable")
+ onclick = "#{options.delete('onclick')};" if options['onclick']
+
+ options["onclick"] = "this.disabled = true;#{onclick}"
+ options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());"
+ options["onclick"] << "if (result == false) { this.disabled = false; }return result;"
+ end
+
+ tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
+ end
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+# desc "Explaining what the task does"
+# task :image_submit_tag_ext do
+# # Task goes here
+# end
--- /dev/null
+require 'test_helper'
+
+class ImageSubmitTagExtTest < ActiveSupport::TestCase
+ # Replace this with your real tests.
+ test "the truth" do
+ assert true
+ end
+end
--- /dev/null
+require 'rubygems'
+require 'active_support'
+require 'active_support/test_case'
\ No newline at end of file
--- /dev/null
+# Uninstall hook code here
--- /dev/null
+Copyright (c) 2008 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+MbMail
+======
+
+MbMail is a rails-plugin for handling mails for mobile-phone in Japan.
+
+* features
+ * DMail
+ * converting deco-mail(text/html mail for mobile-phone) between
+ each formats (NTT DoCoMo, Au, and SoftBank).
+ * MbMailer
+ * switching default charset to 'iso-2022-jp'
+ * accepting 'triple-dotted address (ex. test...@example.com)'
+
+Example
+=======
+
+See spec/mb_mail/*.rb files or http://github.com/tmtysk
+
+Thanks
+======
+
+* emoticon(emoji) conversion table is provided by Jpmobile.
+* mail-header parser and some utils are rewrited from tmail.
+* Sample GIF image is provided by ICHIGO-APORO.
+
+Copyright (c) 2008 tmtysk, released under the MIT license
--- /dev/null
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the mb_mail plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the mb_mail plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'MbMail'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
--- /dev/null
+# Include hook code here
--- /dev/null
+# Install hook code here
--- /dev/null
+require 'rubygems'
+require 'nkf'
+require 'scanf'
+
+#Dir[File.join(File.dirname(__FILE__), 'tmail/**/*.rb')].sort.each { |f| require f }
+Dir[File.join(File.dirname(__FILE__), 'jpmobile/**/*.rb')].sort.each { |f| require f }
+Dir[File.join(File.dirname(__FILE__), 'mb_mail/**/*.rb')].sort.each { |f| require f }
--- /dev/null
+# 携帯メール対応モジュール
+module MbMail
+
+ # HeaderField 操作用に TMail から移管
+ class HeaderField < TMail::HeaderField; end
+
+ # デコメールクラス
+ class DMail < TMail::Mail
+
+ def []=( key, val )
+ dkey = key.downcase
+
+ if val.nil?
+ @header.delete dkey
+ return nil
+ end
+
+ case val
+ when String
+ header = new_hf(key, val)
+ when HeaderField
+ # HeaderField が与えられた場合、そのままヘッダに代入する
+ header = val
+ when Array
+ ALLOW_MULTIPLE.include? dkey or
+ raise ArgumentError, "#{key}: Header must not be multiple"
+ @header[dkey] = val
+ return val
+ else
+ header = new_hf(key, val.to_s)
+ end
+ if ALLOW_MULTIPLE.include? dkey
+ (@header[dkey] ||= []).push header
+ else
+ @header[dkey] = header
+ end
+
+ val
+ end
+
+ # docomo のデコメールフォーマットに変換する
+ def to_docomo_format
+ converted_for_carrier(:docomo)
+ end
+
+ # au のデコレーションメールフォーマットに変換する
+ def to_au_format
+ converted_for_carrier(:au)
+ end
+
+ # softbank のデコレメールフォーマットに変換する
+ def to_softbank_format
+ converted_for_carrier(:softbank)
+ end
+
+ protected
+
+ # 指定された content-type のパーツを取得する
+ def get_specified_type_parts(mimetype)
+ specified_type_parts = []
+ specified_type_parts << self if Regexp.new("^#{mimetype}$", Regexp::IGNORECASE) =~ self.content_type
+ if /^multipart\/(.+)$/ =~ self.content_type then
+ self.parts.each do |p|
+ specified_type_parts += p.get_specified_type_parts(mimetype)
+ end
+ end
+ specified_type_parts
+ end
+
+ private
+
+ # 指定のキャリアのデコメールフォーマットに変換する
+ # 現時点では、:docomo, :au, :softbank のみ対応
+ def converted_for_carrier(carrier = :docomo)
+ organize_mail_parts
+
+ dm = MbMail::DMail.new
+ self.header.each do |key,value|
+ next if key == 'content-type' # content-type は引き継いだらダメ
+ dm[key] = value.to_s
+ end
+ dm.body = ""
+ dm.content_type = 'multipart/mixed'
+
+ # text/plain パートの作成
+ tp = MbMail::DMail.new
+ case carrier
+ when :docomo
+ tp.content_type = 'text/plain; charset="Shift_JIS"'
+ tp.transfer_encoding = 'Base64'
+ tp.body = Base64.encode64(Jpmobile::Emoticon::unicodecr_to_external(NKF.nkf('-m0 -x -Ws', @text_part.body), Jpmobile::Emoticon::CONVERSION_TABLE_TO_DOCOMO))
+ when :au
+ tp.content_type = 'text/plain; charset="Shift_JIS"'
+ tp.transfer_encoding = 'Base64'
+ tp.body = Base64.encode64(Jpmobile::Emoticon::unicodecr_to_external(NKF.nkf('-m0 -x -Ws', @text_part.body), Jpmobile::Emoticon::CONVERSION_TABLE_TO_AU))
+ when :softbank
+ tp.content_type = 'text/plain; charset="UTF-8"'
+ tp.transfer_encoding = 'Base64'
+ table = Jpmobile::Emoticon::CONVERSION_TABLE_TO_SOFTBANK
+ emoticon_converted = @text_part.body.gsub(/&#x([0-9a-f]{4});/i) do |match|
+ unicode = $1.scanf("%x").first
+ case table[unicode]
+ when Integer
+ [(table[unicode].to_i-0x1000)].pack('U')
+ when String
+ table[unicode]
+ else
+ match
+ end.force_encoding(Encoding::ASCII_8BIT)
+ end
+ tp.body = Base64.encode64(emoticon_converted)
+ else
+ tp.content_type = 'text/plain; charset="UTF-8"'
+ tp.transfer_encoding = 'Base64'
+ tp.body = Base64.encode64(@text_part.body)
+ end
+
+ # text/html パートの作成
+ hp = MbMail::DMail.new
+ case carrier
+ when :docomo
+ hp.content_type = 'text/html; charset="Shift_JIS"'
+ hp.transfer_encoding = 'Base64'
+ hp.body = Base64.encode64(Jpmobile::Emoticon::unicodecr_to_external(NKF.nkf('-m0 -x -Ws', @html_part.body), Jpmobile::Emoticon::CONVERSION_TABLE_TO_DOCOMO))
+ when :au
+ hp.content_type = 'text/html; charset="Shift_JIS"'
+ hp.transfer_encoding = 'Base64'
+ hp.body = Base64.encode64(Jpmobile::Emoticon::unicodecr_to_external(NKF.nkf('-m0 -x -Ws', @html_part.body), Jpmobile::Emoticon::CONVERSION_TABLE_TO_AU))
+ when :softbank
+ hp.content_type = 'text/html; charset="UTF-8"'
+ hp.transfer_encoding = 'Base64'
+ table = Jpmobile::Emoticon::CONVERSION_TABLE_TO_SOFTBANK
+ emoticon_converted = @html_part.body.gsub(/&#x([0-9a-f]{4});/i) do |match|
+ unicode = $1.scanf("%x").first
+ case table[unicode]
+ when Integer
+ [(table[unicode].to_i-0x1000)].pack('U')
+ when String
+ table[unicode]
+ else
+ match
+ end.force_encoding(Encoding::ASCII_8BIT)
+ end
+ hp.body = Base64.encode64(emoticon_converted)
+ else
+ hp.content_type = 'text/plain; charset="UTF-8"'
+ hp.transfer_encoding = 'Base64'
+ hp.body = Base64.encode64(@html_part.body)
+ end
+
+ # キャリアによって multipart 構成を分岐
+ alt_p = MbMail::DMail.new
+ alt_p.body = ""
+ alt_p.content_type = 'multipart/alternative'
+ alt_p.parts << tp
+ alt_p.parts << hp
+ case carrier
+ when :au
+ dm.parts << alt_p
+ @in_lined_image_parts.each do |ip| dm.parts << ip end
+ @attached_image_parts.each do |ap| dm.parts << ap end
+ else
+ rel_p = MbMail::DMail.new
+ rel_p.body = ""
+ rel_p.content_type = 'multipart/related'
+ rel_p.parts << alt_p
+ @in_lined_image_parts.each do |ip| rel_p.parts << ip end
+ dm.parts << rel_p
+ @attached_image_parts.each do |ap| dm.parts << ap end
+ end
+
+ dm
+ end
+
+ # オリジナルのメール構成を解析し、各パーツに分離する
+ def organize_mail_parts
+ @text_part = get_specified_type_parts('text/plain').first
+ @html_part = get_specified_type_parts('text/html').first
+ # いったん全ての画像をインライン扱いとし、
+ # その後本文から参照されていない画像を添付扱いとする
+ # au でいずれの添付タイプについても同等に扱われているため
+ @in_lined_image_parts = get_specified_type_parts('image/(gif|jpeg|jpg)')
+ @in_lined_image_parts.map do |ip| ip.content_disposition = 'inline' end
+ @attached_image_parts = []
+ @in_lined_image_parts.delete_if do |ip|
+ if /TMail\:\:MessageIdHeader\s\"<([\.@_0-9a-zA-Z]+)>/ =~ ip["content-id"].inspect then
+ unless Regexp.new("src=['\"]cid:#{$1}", Regexp::IGNORECASE) =~ @html_part.body then
+ ip.content_disposition = 'attachment'
+ @attached_image_parts << ip
+ true
+ end
+ else
+ false
+ end
+ end
+ end
+ end
+
+end
--- /dev/null
+module MbMail
+ # http://www.kbmj.com/~shinya/rails_seminar/slides/#(33)
+ # JapaneseMailer 実装より
+ class MbMailer < ActionMailer::Base
+
+ Dir[File.join(File.dirname(__FILE__), '../tmail/**/*.rb')].sort.each { |f| require f }
+
+ private
+
+ def initialize_defaults_with_charset(*args)
+ charset 'iso-2022-jp'
+ initialize_defaults_without_charset(*args)
+ end
+
+ alias_method_chain :initialize_defaults, :charset
+
+ def create_mail_with_encode_body
+ @body = NKF.nkf("-Wj", @body) if @parts.empty?
+ create_mail_without_encode_body
+ end
+
+ alias_method_chain :create_mail, :encode_body
+
+ public
+ # ヘッダに日本語を含める場合に用いる base64 カプセリング
+ # http://wiki.fdiary.net/rails/?ActionMailer
+ def base64(text, charset="iso-2022-jp", convert=true)
+ if convert
+ if charset == "iso-2022-jp"
+ text = NKF.nkf('-j -m0', text)
+ end
+ end
+ text = [text].pack('m').delete("\r\n")
+ "=?#{charset}?B?#{text}?="
+ end
+ end
+end
--- /dev/null
+module TMail
+
+ # Mail 構築時に MessageIdHeader が正しく作成できない処理を修正
+ class Mail
+ def []=( key, val )
+ dkey = key.downcase
+
+ if val.nil?
+ @header.delete dkey
+ return nil
+ end
+
+ case val
+ when String
+ header = new_hf(key, val)
+ when HeaderField
+ # HeaderField が与えられた場合、そのままヘッダに代入する
+ header = val
+ when Array
+ ALLOW_MULTIPLE.include? dkey or
+ raise ArgumentError, "#{key}: Header must not be multiple"
+ @header[dkey] = val
+ return val
+ else
+ header = new_hf(key, val.to_s)
+ end
+ if ALLOW_MULTIPLE.include? dkey
+ (@header[dkey] ||= []).push header
+ else
+ @header[dkey] = header
+ end
+
+ val
+ end
+ end
+end
--- /dev/null
+module TMail
+
+ class Parser < Racc::Parser
+
+module_eval(<<'...end parser.y/module_eval...', 'parser.y', 333)
+
+ include TextUtils
+
+ def Parser.parse(ident, str, comment = nil)
+ new().parse(ident, str, comment)
+ end
+
+ MAILP_DEBUG = false unless
+
+ def initialize
+ self.debug = MAILP_DEBUG
+ end
+
+ def debug=(flag)
+ @yydebug = flag && Racc_debug_parser
+ @scanner_debug = flag
+ end
+
+ def debug
+ @yydebug
+ end
+
+ alias debug? debug
+
+ def parse(ident, str, comments = nil)
+ @scanner = Scanner.new(str, ident, comments)
+ @scanner.debug = @scanner_debug
+ @first = [ident, ident]
+ result = yyparse(self, :parse0)
+ comments.map! {|c| to_kcode(c) } if comments
+ result
+ end
+
+ private
+
+ def parse0(&block)
+ yield @first
+ @scanner.scan(&block)
+ end
+
+ def on_error(t, val, vstack)
+ raise SyntaxError, "parse error on token #{racc_token2str(t)}"
+ end
+...end parser.y/module_eval...
+##### State transition tables begin ###
+
+racc_action_table = [
+ -69, 23, 25, 147, 148, 29, 31, 106, 107, 16,
+ 17, 19, 22, 137, 23, 25, -69, 32, 29, 31,
+ -69, 155, 16, 17, 19, 22, -69, 27, 76, 76,
+ 32, 23, 25, 156, 100, 29, 31, 143, 144, 16,
+ 17, 19, 22, 108, 27, 23, 25, 32, 99, 29,
+ 31, 115, 116, 16, 17, 19, 22, 110, 27, 97,
+ 76, 32, 23, 25, 121, 95, 29, 31, 113, 79,
+ 16, 17, 19, 22, 92, 27, 23, 25, 32, 118,
+ 29, 31, 89, 82, 16, 17, 19, 22, 124, 23,
+ 25, 100, 32, 29, 31, 126, 127, 16, 17, 19,
+ 22, 81, 27, 23, 25, 32, 92, 29, 31, 129,
+ 130, 16, 17, 19, 22, 76, 52, 23, 25, 32,
+ 76, 29, 31, 131, 80, 16, 17, 19, 22, 79,
+ 23, 25, 134, 32, 29, 31, 79, 78, 16, 17,
+ 19, 22, 76, 23, 25, 65, 32, 29, 31, 62,
+ 140, 16, 17, 19, 22, 76, 23, 25, 60, 32,
+ 29, 31, 100, 44, 16, 17, 19, 22, 76, 23,
+ 25, 121, 32, 29, 31, 149, 150, 16, 17, 19,
+ 22, 153, 27, 23, 25, 32, 68, 29, 31, 154,
+ 42, 16, 17, 19, 22, 158, 27, 23, 25, 32,
+ 160, 29, 31, 74, 40, 16, 17, 19, 22, 15,
+ 23, 25, 165, 32, 29, 31, 40, 167, 16, 17,
+ 19, 22, 168, 27, 23, 25, 32, nil, 29, 31,
+ nil, nil, 16, 17, 19, 22, nil, 23, 25, nil,
+ 32, 29, 31, nil, nil, 16, 17, 19, 22, nil,
+ 23, 25, nil, 32, 29, 31, nil, nil, 16, 17,
+ 19, 22, nil, 27, 23, 25, 32, nil, 29, 31,
+ 74, nil, 16, 17, 19, 22, nil, nil, 93, nil,
+ 32, 23, 25, nil, nil, 29, 31, nil, nil, 16,
+ 17, 19, 22, nil, 23, 25, nil, 32, 29, 31,
+ nil, nil, 16, 17, 19, 22, nil, 85, 25, nil,
+ 32, 29, 31, nil, 88, 16, 17, 19, 22, 85,
+ 25, nil, nil, 29, 31, nil, 88, 16, 17, 19,
+ 22, 4, 6, 7, 8, 9, 10, 11, 12, 13,
+ 1, 2, 3, 85, 25, nil, nil, 29, 31, nil,
+ 88, 16, 17, 19, 22, 85, 25, nil, nil, 29,
+ 31, nil, 88, 16, 17, 19, 22, 85, 25, nil,
+ nil, 29, 31, nil, 88, 16, 17, 19, 22, 85,
+ 25, nil, nil, 29, 31, nil, 88, 16, 17, 19,
+ 22, 85, 25, nil, nil, 29, 31, nil, 88, 16,
+ 17, 19, 22 ]
+
+racc_action_check = [
+ 28, 77, 77, 137, 137, 77, 77, 72, 72, 77,
+ 77, 77, 77, 127, 2, 2, 28, 77, 2, 2,
+ 28, 144, 2, 2, 2, 2, 28, 2, 67, 28,
+ 2, 3, 3, 144, 66, 3, 3, 135, 135, 3,
+ 3, 3, 3, 73, 3, 153, 153, 3, 62, 153,
+ 153, 87, 87, 153, 153, 153, 153, 75, 153, 60,
+ 87, 153, 91, 91, 91, 56, 91, 91, 81, 51,
+ 91, 91, 91, 91, 50, 91, 71, 71, 91, 90,
+ 71, 71, 45, 42, 71, 71, 71, 71, 97, 7,
+ 7, 98, 71, 7, 7, 99, 100, 7, 7, 7,
+ 7, 41, 7, 8, 8, 7, 103, 8, 8, 105,
+ 106, 8, 8, 8, 8, 109, 8, 9, 9, 8,
+ 112, 9, 9, 113, 40, 9, 9, 9, 9, 36,
+ 10, 10, 118, 9, 10, 10, 122, 33, 10, 10,
+ 10, 10, 70, 156, 156, 13, 10, 156, 156, 12,
+ 131, 156, 156, 156, 156, 133, 95, 95, 11, 156,
+ 95, 95, 136, 6, 95, 95, 95, 95, 139, 128,
+ 128, 128, 95, 128, 128, 140, 141, 128, 128, 128,
+ 128, 142, 128, 26, 26, 128, 26, 26, 26, 143,
+ 5, 26, 26, 26, 26, 149, 26, 27, 27, 26,
+ 152, 27, 27, 27, 4, 27, 27, 27, 27, 1,
+ 68, 68, 158, 27, 68, 68, 160, 164, 68, 68,
+ 68, 68, 165, 68, 123, 123, 68, nil, 123, 123,
+ nil, nil, 123, 123, 123, 123, nil, 55, 55, nil,
+ 123, 55, 55, nil, nil, 55, 55, 55, 55, nil,
+ 38, 38, nil, 55, 38, 38, nil, nil, 38, 38,
+ 38, 38, nil, 38, 52, 52, 38, nil, 52, 52,
+ 52, nil, 52, 52, 52, 52, nil, nil, 52, nil,
+ 52, 102, 102, nil, nil, 102, 102, nil, nil, 102,
+ 102, 102, 102, nil, 59, 59, nil, 102, 59, 59,
+ nil, nil, 59, 59, 59, 59, nil, 115, 115, nil,
+ 59, 115, 115, nil, 115, 115, 115, 115, 115, 89,
+ 89, nil, nil, 89, 89, nil, 89, 89, 89, 89,
+ 89, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 114, 114, nil, nil, 114, 114, nil,
+ 114, 114, 114, 114, 114, 44, 44, nil, nil, 44,
+ 44, nil, 44, 44, 44, 44, 44, 78, 78, nil,
+ nil, 78, 78, nil, 78, 78, 78, 78, 78, 74,
+ 74, nil, nil, 74, 74, nil, 74, 74, 74, 74,
+ 74, 130, 130, nil, nil, 130, 130, nil, 130, 130,
+ 130, 130, 130 ]
+
+racc_action_pointer = [
+ 329, 178, 0, 17, 189, 190, 145, 75, 89, 103,
+ 116, 144, 118, 114, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, 169, 183, 0, nil,
+ nil, nil, nil, 117, nil, nil, 103, nil, 236, nil,
+ 108, 87, 83, nil, 341, 63, nil, nil, nil, nil,
+ 58, 43, 250, nil, nil, 223, 49, nil, nil, 280,
+ 30, nil, 16, nil, nil, nil, 8, -1, 196, nil,
+ 113, 62, -9, 15, 365, 28, nil, -13, 353, nil,
+ nil, 53, nil, nil, nil, nil, nil, 31, nil, 305,
+ 57, 48, nil, nil, nil, 142, nil, 74, 65, 64,
+ 65, nil, 267, 90, nil, 81, 90, nil, nil, 86,
+ nil, nil, 91, 109, 329, 293, nil, nil, 117, nil,
+ nil, nil, 110, 210, nil, nil, nil, -20, 155, nil,
+ 377, 136, nil, 126, nil, 14, 136, -27, nil, 139,
+ 158, 161, 156, 174, 6, nil, nil, nil, nil, 181,
+ nil, nil, 174, 31, nil, nil, 129, nil, 195, nil,
+ 201, nil, nil, nil, 189, 208, nil, nil, nil ]
+
+racc_action_default = [
+ -111, -111, -111, -111, -14, -111, -20, -111, -111, -111,
+ -111, -111, -111, -111, -10, -96, -107, -108, -44, -109,
+ -78, -11, -110, -80, -43, -104, -111, -111, -60, -105,
+ -55, -106, -79, -68, -54, -71, -45, -12, -111, -1,
+ -111, -111, -111, -2, -111, -22, -51, -48, -50, -3,
+ -40, -41, -111, -46, -4, -87, -5, -89, -6, -91,
+ -111, -7, -96, -8, -9, -100, -102, -61, -59, -56,
+ -69, -111, -111, -111, -111, -77, -75, -70, -111, -57,
+ -15, -111, 169, -73, -81, -83, -21, -24, -82, -111,
+ -27, -111, -84, -47, -90, -111, -92, -111, -102, -111,
+ -103, -101, -111, -58, -52, -111, -111, -64, -63, -65,
+ -76, -72, -67, -111, -111, -111, -26, -23, -111, -29,
+ -49, -85, -42, -88, -93, -95, -96, -111, -111, -62,
+ -111, -111, -74, -25, -28, -31, -102, -111, -53, -66,
+ -111, -111, -34, -111, -111, -94, -97, -99, -98, -111,
+ -18, -13, -38, -111, -30, -33, -111, -32, -16, -19,
+ -14, -35, -36, -37, -111, -111, -39, -86, -17 ]
+
+racc_goto_table = [
+ 39, 67, 70, 73, 24, 37, 69, 57, 59, 38,
+ 36, 66, 55, 67, 91, 84, 159, 86, 69, 101,
+ 109, 49, 53, 135, 112, 142, 77, 70, 73, 152,
+ 119, 90, 45, 157, 161, 151, 141, 21, 14, 18,
+ 120, 103, 64, 63, 61, 84, 70, 105, 58, 84,
+ 132, 125, 56, 94, 54, 43, 5, 96, 98, 146,
+ 84, 133, 117, nil, nil, 102, nil, 128, 77, nil,
+ 104, nil, nil, nil, 38, 111, 139, nil, nil, nil,
+ nil, nil, nil, nil, nil, 84, 84, nil, nil, 145,
+ nil, nil, nil, 57, nil, nil, nil, nil, 123, 122,
+ 111, 84, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 94, 136, nil, nil, nil, nil, nil, 70, 163,
+ 138, 70, 164, 162, 38, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, 166 ]
+
+racc_goto_check = [
+ 2, 37, 37, 29, 13, 13, 28, 42, 42, 36,
+ 31, 47, 46, 37, 32, 45, 23, 24, 28, 48,
+ 25, 4, 4, 20, 25, 21, 41, 37, 29, 22,
+ 19, 18, 17, 26, 27, 16, 15, 12, 11, 33,
+ 34, 35, 10, 9, 8, 45, 37, 29, 7, 45,
+ 43, 48, 6, 42, 5, 3, 1, 42, 47, 49,
+ 45, 25, 24, nil, nil, 41, nil, 32, 41, nil,
+ 13, nil, nil, nil, 36, 42, 25, nil, nil, nil,
+ nil, nil, nil, nil, nil, 45, 45, nil, nil, 48,
+ nil, nil, nil, 42, nil, nil, nil, nil, 46, 31,
+ 42, 45, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 42, 47, nil, nil, nil, nil, nil, 37, 29,
+ 13, 37, 29, 28, 36, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, 2 ]
+
+racc_goto_pointer = [
+ nil, 56, -4, 49, 14, 46, 43, 38, 33, 31,
+ 29, 37, 35, 2, nil, -95, -106, 26, -14, -60,
+ -96, -110, -113, -136, -27, -54, -111, -119, -20, -24,
+ nil, 8, -36, 37, -51, -27, 6, -25, nil, nil,
+ nil, -2, -2, -64, nil, -29, 3, -4, -47, -78 ]
+
+racc_goto_default = [
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, 48, 41, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, 87, nil, nil, 30, 34,
+ 50, 51, nil, 46, 47, nil, 26, 28, 71, 72,
+ 33, 114, 35, 83, 75, 20, nil, nil, nil, nil ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 2, 35, :_reduce_1,
+ 2, 35, :_reduce_2,
+ 2, 35, :_reduce_3,
+ 2, 35, :_reduce_4,
+ 2, 35, :_reduce_5,
+ 2, 35, :_reduce_6,
+ 2, 35, :_reduce_7,
+ 2, 35, :_reduce_8,
+ 2, 35, :_reduce_9,
+ 2, 35, :_reduce_10,
+ 2, 35, :_reduce_11,
+ 2, 35, :_reduce_12,
+ 6, 36, :_reduce_13,
+ 0, 48, :_reduce_none,
+ 2, 48, :_reduce_none,
+ 3, 49, :_reduce_16,
+ 5, 49, :_reduce_17,
+ 1, 50, :_reduce_18,
+ 7, 37, :_reduce_19,
+ 0, 51, :_reduce_none,
+ 2, 51, :_reduce_21,
+ 0, 52, :_reduce_none,
+ 2, 52, :_reduce_23,
+ 1, 58, :_reduce_24,
+ 3, 58, :_reduce_25,
+ 2, 58, :_reduce_26,
+ 0, 53, :_reduce_none,
+ 2, 53, :_reduce_28,
+ 0, 54, :_reduce_29,
+ 3, 54, :_reduce_30,
+ 0, 55, :_reduce_none,
+ 2, 55, :_reduce_32,
+ 2, 55, :_reduce_33,
+ 0, 56, :_reduce_none,
+ 2, 56, :_reduce_35,
+ 1, 61, :_reduce_36,
+ 1, 61, :_reduce_37,
+ 0, 57, :_reduce_none,
+ 2, 57, :_reduce_39,
+ 1, 38, :_reduce_none,
+ 1, 38, :_reduce_none,
+ 3, 38, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 46, :_reduce_none,
+ 1, 39, :_reduce_none,
+ 2, 39, :_reduce_47,
+ 1, 64, :_reduce_48,
+ 3, 64, :_reduce_49,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 69, :_reduce_52,
+ 3, 69, :_reduce_53,
+ 1, 47, :_reduce_none,
+ 1, 47, :_reduce_none,
+ 2, 47, :_reduce_56,
+ 2, 67, :_reduce_none,
+ 3, 65, :_reduce_58,
+ 2, 65, :_reduce_59,
+ 1, 70, :_reduce_60,
+ 2, 70, :_reduce_61,
+ 4, 62, :_reduce_62,
+ 3, 62, :_reduce_63,
+ 2, 72, :_reduce_none,
+ 2, 73, :_reduce_65,
+ 4, 73, :_reduce_66,
+ 3, 63, :_reduce_67,
+ 1, 63, :_reduce_68,
+ 1, 74, :_reduce_none,
+ 2, 74, :_reduce_70,
+ 1, 71, :_reduce_71,
+ 3, 71, :_reduce_72,
+ 1, 59, :_reduce_73,
+ 3, 59, :_reduce_74,
+ 1, 78, :_reduce_none,
+ 2, 78, :_reduce_76,
+ 1, 75, :_reduce_77,
+ 1, 76, :_reduce_none,
+ 1, 76, :_reduce_none,
+ 1, 76, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 77, :_reduce_none,
+ 1, 66, :_reduce_none,
+ 2, 66, :_reduce_none,
+ 3, 60, :_reduce_86,
+ 1, 40, :_reduce_87,
+ 3, 40, :_reduce_88,
+ 1, 80, :_reduce_none,
+ 2, 80, :_reduce_90,
+ 1, 41, :_reduce_91,
+ 2, 41, :_reduce_92,
+ 3, 42, :_reduce_93,
+ 5, 43, :_reduce_94,
+ 3, 43, :_reduce_95,
+ 0, 81, :_reduce_96,
+ 5, 81, :_reduce_97,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 44, :_reduce_100,
+ 3, 45, :_reduce_101,
+ 0, 82, :_reduce_none,
+ 1, 82, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none,
+ 1, 79, :_reduce_none ]
+
+racc_reduce_n = 111
+
+racc_shift_n = 169
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ :DATETIME => 2,
+ :RECEIVED => 3,
+ :MADDRESS => 4,
+ :RETPATH => 5,
+ :KEYWORDS => 6,
+ :ENCRYPTED => 7,
+ :MIMEVERSION => 8,
+ :CTYPE => 9,
+ :CENCODING => 10,
+ :CDISPOSITION => 11,
+ :ADDRESS => 12,
+ :MAILBOX => 13,
+ :DIGIT => 14,
+ :ATOM => 15,
+ "," => 16,
+ ":" => 17,
+ :FROM => 18,
+ :BY => 19,
+ "@" => 20,
+ :DOMLIT => 21,
+ :VIA => 22,
+ :WITH => 23,
+ :ID => 24,
+ :FOR => 25,
+ ";" => 26,
+ "<" => 27,
+ ">" => 28,
+ "." => 29,
+ :QUOTED => 30,
+ :TOKEN => 31,
+ "/" => 32,
+ "=" => 33 }
+
+racc_nt_base = 34
+
+racc_use_result_var = false
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "DATETIME",
+ "RECEIVED",
+ "MADDRESS",
+ "RETPATH",
+ "KEYWORDS",
+ "ENCRYPTED",
+ "MIMEVERSION",
+ "CTYPE",
+ "CENCODING",
+ "CDISPOSITION",
+ "ADDRESS",
+ "MAILBOX",
+ "DIGIT",
+ "ATOM",
+ "\",\"",
+ "\":\"",
+ "FROM",
+ "BY",
+ "\"@\"",
+ "DOMLIT",
+ "VIA",
+ "WITH",
+ "ID",
+ "FOR",
+ "\";\"",
+ "\"<\"",
+ "\">\"",
+ "\".\"",
+ "QUOTED",
+ "TOKEN",
+ "\"/\"",
+ "\"=\"",
+ "$start",
+ "content",
+ "datetime",
+ "received",
+ "addrs_TOP",
+ "retpath",
+ "keys",
+ "enc",
+ "version",
+ "ctype",
+ "cencode",
+ "cdisp",
+ "addr_TOP",
+ "mbox",
+ "day",
+ "hour",
+ "zone",
+ "from",
+ "by",
+ "via",
+ "with",
+ "id",
+ "for",
+ "received_datetime",
+ "received_domain",
+ "domain",
+ "msgid",
+ "received_addrspec",
+ "routeaddr",
+ "spec",
+ "addrs",
+ "group_bare",
+ "commas",
+ "group",
+ "addr",
+ "mboxes",
+ "addr_phrase",
+ "local_head",
+ "routes",
+ "at_domains",
+ "local",
+ "dots",
+ "word",
+ "domword",
+ "dot_repeat",
+ "atom",
+ "phrase",
+ "params",
+ "opt_semicolon",
+ "value" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+module_eval(<<'.,.,', 'parser.y', 16)
+ def _reduce_1(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 17)
+ def _reduce_2(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 18)
+ def _reduce_3(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 19)
+ def _reduce_4(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 20)
+ def _reduce_5(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 21)
+ def _reduce_6(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 22)
+ def _reduce_7(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 23)
+ def _reduce_8(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 24)
+ def _reduce_9(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 25)
+ def _reduce_10(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 26)
+ def _reduce_11(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 27)
+ def _reduce_12(val, _values)
+ val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 33)
+ def _reduce_13(val, _values)
+ t = Time.gm(val[3].to_i, val[2][0,3], val[1].to_i, 0, 0, 0)
+ (t + val[4] - val[5]).localtime
+
+ end
+.,.,
+
+# reduce 14 omitted
+
+# reduce 15 omitted
+
+module_eval(<<'.,.,', 'parser.y', 42)
+ def _reduce_16(val, _values)
+ (val[0].to_i * 60 * 60) +
+ (val[2].to_i * 60)
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 47)
+ def _reduce_17(val, _values)
+ (val[0].to_i * 60 * 60) +
+ (val[2].to_i * 60) +
+ (val[4].to_i)
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 54)
+ def _reduce_18(val, _values)
+ timezone_string_to_unixtime(val[0])
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 59)
+ def _reduce_19(val, _values)
+ val
+
+ end
+.,.,
+
+# reduce 20 omitted
+
+module_eval(<<'.,.,', 'parser.y', 65)
+ def _reduce_21(val, _values)
+ val[1]
+
+ end
+.,.,
+
+# reduce 22 omitted
+
+module_eval(<<'.,.,', 'parser.y', 71)
+ def _reduce_23(val, _values)
+ val[1]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 77)
+ def _reduce_24(val, _values)
+ join_domain(val[0])
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 81)
+ def _reduce_25(val, _values)
+ join_domain(val[2])
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 85)
+ def _reduce_26(val, _values)
+ join_domain(val[0])
+
+ end
+.,.,
+
+# reduce 27 omitted
+
+module_eval(<<'.,.,', 'parser.y', 91)
+ def _reduce_28(val, _values)
+ val[1]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 96)
+ def _reduce_29(val, _values)
+ []
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 100)
+ def _reduce_30(val, _values)
+ val[0].push val[2]
+ val[0]
+
+ end
+.,.,
+
+# reduce 31 omitted
+
+module_eval(<<'.,.,', 'parser.y', 107)
+ def _reduce_32(val, _values)
+ val[1]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 111)
+ def _reduce_33(val, _values)
+ val[1]
+
+ end
+.,.,
+
+# reduce 34 omitted
+
+module_eval(<<'.,.,', 'parser.y', 117)
+ def _reduce_35(val, _values)
+ val[1]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 123)
+ def _reduce_36(val, _values)
+ val[0].spec
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 127)
+ def _reduce_37(val, _values)
+ val[0].spec
+
+ end
+.,.,
+
+# reduce 38 omitted
+
+module_eval(<<'.,.,', 'parser.y', 134)
+ def _reduce_39(val, _values)
+ val[1]
+
+ end
+.,.,
+
+# reduce 40 omitted
+
+# reduce 41 omitted
+
+# reduce 42 omitted
+
+# reduce 43 omitted
+
+# reduce 44 omitted
+
+# reduce 45 omitted
+
+# reduce 46 omitted
+
+module_eval(<<'.,.,', 'parser.y', 146)
+ def _reduce_47(val, _values)
+ [ Address.new(nil, nil) ]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 148)
+ def _reduce_48(val, _values)
+ val
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 149)
+ def _reduce_49(val, _values)
+ val[0].push val[2]; val[0]
+ end
+.,.,
+
+# reduce 50 omitted
+
+# reduce 51 omitted
+
+module_eval(<<'.,.,', 'parser.y', 156)
+ def _reduce_52(val, _values)
+ val
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 160)
+ def _reduce_53(val, _values)
+ val[0].push val[2]
+ val[0]
+
+ end
+.,.,
+
+# reduce 54 omitted
+
+# reduce 55 omitted
+
+module_eval(<<'.,.,', 'parser.y', 168)
+ def _reduce_56(val, _values)
+ val[1].phrase = Decoder.decode(val[0])
+ val[1]
+
+ end
+.,.,
+
+# reduce 57 omitted
+
+module_eval(<<'.,.,', 'parser.y', 176)
+ def _reduce_58(val, _values)
+ AddressGroup.new(val[0], val[2])
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 178)
+ def _reduce_59(val, _values)
+ AddressGroup.new(val[0], [])
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 181)
+ def _reduce_60(val, _values)
+ val[0].join('.')
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 182)
+ def _reduce_61(val, _values)
+ val[0] << ' ' << val[1].join('.')
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 186)
+ def _reduce_62(val, _values)
+ val[2].routes.replace val[1]
+ val[2]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 191)
+ def _reduce_63(val, _values)
+ val[1]
+
+ end
+.,.,
+
+# reduce 64 omitted
+
+module_eval(<<'.,.,', 'parser.y', 196)
+ def _reduce_65(val, _values)
+ [ val[1].join('.') ]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 197)
+ def _reduce_66(val, _values)
+ val[0].push val[3].join('.'); val[0]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 199)
+ def _reduce_67(val, _values)
+ Address.new( val[0], val[2] )
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 200)
+ def _reduce_68(val, _values)
+ Address.new( val[0], nil )
+ end
+.,.,
+
+# reduce 69 omitted
+
+module_eval(<<'.,.,', 'parser.y', 203)
+ def _reduce_70(val, _values)
+ (val[1] + 1).times { val[0].push '' }; val[0]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 206)
+ def _reduce_71(val, _values)
+ val
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 209)
+ def _reduce_72(val, _values)
+ val[1].times do
+ val[0].push ''
+ end
+ val[0].push val[2]
+ val[0]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 217)
+ def _reduce_73(val, _values)
+ val
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 220)
+ def _reduce_74(val, _values)
+ val[1].times do
+ val[0].push ''
+ end
+ val[0].push val[2]
+ val[0]
+
+ end
+.,.,
+
+# reduce 75 omitted
+
+module_eval(<<'.,.,', 'parser.y', 228)
+ def _reduce_76(val, _values)
+ val[0] + val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 230)
+ def _reduce_77(val, _values)
+ val[0].size - 1
+ end
+.,.,
+
+# reduce 78 omitted
+
+# reduce 79 omitted
+
+# reduce 80 omitted
+
+# reduce 81 omitted
+
+# reduce 82 omitted
+
+# reduce 83 omitted
+
+# reduce 84 omitted
+
+# reduce 85 omitted
+
+module_eval(<<'.,.,', 'parser.y', 245)
+ def _reduce_86(val, _values)
+ val[1] = val[1].spec
+ val.join('')
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 249)
+ def _reduce_87(val, _values)
+ val
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 250)
+ def _reduce_88(val, _values)
+ val[0].push val[2]; val[0]
+ end
+.,.,
+
+# reduce 89 omitted
+
+module_eval(<<'.,.,', 'parser.y', 253)
+ def _reduce_90(val, _values)
+ val[0] << ' ' << val[1]
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 257)
+ def _reduce_91(val, _values)
+ val.push nil
+ val
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 262)
+ def _reduce_92(val, _values)
+ val
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 267)
+ def _reduce_93(val, _values)
+ [ val[0].to_i, val[2].to_i ]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 272)
+ def _reduce_94(val, _values)
+ [ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 276)
+ def _reduce_95(val, _values)
+ [ val[0].downcase, nil, decode_params(val[1]) ]
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 281)
+ def _reduce_96(val, _values)
+ {}
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 285)
+ def _reduce_97(val, _values)
+ val[0][ val[2].downcase ] = val[4]
+ val[0]
+
+ end
+.,.,
+
+# reduce 98 omitted
+
+# reduce 99 omitted
+
+module_eval(<<'.,.,', 'parser.y', 294)
+ def _reduce_100(val, _values)
+ val[0].downcase
+
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 299)
+ def _reduce_101(val, _values)
+ [ val[0].downcase, decode_params(val[1]) ]
+
+ end
+.,.,
+
+# reduce 102 omitted
+
+# reduce 103 omitted
+
+# reduce 104 omitted
+
+# reduce 105 omitted
+
+# reduce 106 omitted
+
+# reduce 107 omitted
+
+# reduce 108 omitted
+
+# reduce 109 omitted
+
+# reduce 110 omitted
+
+def _reduce_none(val, _values)
+ val[0]
+end
+
+ end # class Parser
+end # module TMail
--- /dev/null
+require 'nkf'
+# convert_to で機種依存文字や絵文字に対応するために
+# Unquoter 内で NKF を使用するようにしたもの
+module TMail
+ class Unquoter
+ class << self
+ # http://www.kbmj.com/~shinya/rails_seminar/slides/#(30)
+ def convert_to_with_nkf(text, to, from)
+ if text && to =~ /^utf-8$/i && from =~ /^iso-2022-jp$/i
+ NKF.nkf("-Jw", text)
+ elsif text && from =~ /^utf-8$/i && to =~ /^iso-2022-jp$/i
+ NKF.nkf("-Wj", text)
+ else
+ convert_to_without_nkf(text, to, from)
+ end
+ end
+
+ alias_method_chain :convert_to, :nkf
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe MbMail::DMail, "は" do
+ it "引数無しで正しく初期化できる" do
+ @d = MbMail::DMail.new
+ @d.should satisfy { |d| d.instance_of? MbMail::DMail }
+ end
+ it "既存の HTML メールを元に正しく初期化できる" do
+ @d = MbMail::DMail.load("#{SAMPLE_DIR}/decomail.eml")
+ @d.should satisfy { |d| d.instance_of? MbMail::DMail }
+ end
+ describe "構築済みの HTML メールを" do
+ before do
+ @dm = MbMail::DMail.new
+
+ # ヘッダの作成
+ @dm.from = "=?iso-2022-jp?B?#{[NKF.nkf('-j -m0', '日本語from')].pack('m').delete("\r\n")}?= <test@example.com>"
+ @dm.subject = "=?iso-2022-jp?B?#{[NKF.nkf('-j -m0', '日本語subject')].pack('m').delete("\r\n")}?="
+ @dm.body = ''
+ @dm.content_type = 'multipart/mixed'
+
+ # 各パートを作成
+ ## インライン画像パート
+ ### インライン画像の content_id
+ ### au 向けは '@' を一つだけ含むようにすること
+ @sample_cid = '<sample_cid@example.com>'
+ @inlined_image_parts = []
+ inlined_image_part = MbMail::DMail.new
+ inlined_image_part.content_type = 'image/gif; name="inlined.gif"'
+ cid_header = MbMail::HeaderField.new('content-id', @sample_cid)
+ cid_header.id = @sample_cid
+ inlined_image_part['content-id'] = cid_header
+ inlined_image_part.transfer_encoding = 'Base64'
+ inlined_image_part.content_disposition = 'inline'
+ inlined_image_part.body = Base64.encode64(File.open("#{SAMPLE_DIR}/inlined.gif").read)
+ @inlined_image_parts << inlined_image_part
+ ## 添付画像パート
+ @attached_image_parts = []
+ attached_image_part = MbMail::DMail.new
+ attached_image_part.content_type = 'image/gif; name="attached.gif"'
+ attached_image_part.transfer_encoding = 'Base64'
+ attached_image_part.content_disposition = 'attachment'
+ attached_image_part.body = Base64.encode64(File.open("#{SAMPLE_DIR}/attached.gif").read)
+ @attached_image_parts << attached_image_part
+ ## 本文 text/plain パート
+ @text_part = MbMail::DMail.new
+ @text_part.transfer_encoding = 'Base64'
+ ### 絵文字変換を使うのであれば Unicode 実体参照に変換しておく
+ @text_part.content_type = 'text/plain; charset="UTF-8"'
+ @text_part.body = Base64.encode64(Jpmobile::Emoticon.utf8_to_unicodecr("日本語メールです。\n絵文字を使用する場合はdocomoのUnicode表記を使用します。\nこれは「晴れ」"))
+ ## 本文 text/html パート
+ @html_part = MbMail::DMail.new
+ @html_part.transfer_encoding = 'Base64'
+ @html_part.content_type = 'text/html; charset="UTF-8"'
+ @html_part.body = Base64.encode64(Jpmobile::Emoticon.utf8_to_unicodecr("<HTML><BODY><DIV>日本語メールです。</DIV><DIV><FONT color='#FF0000;'>絵文字を使用する場合はdocomoのUnicode表記を使用します。</FONT></DIV> <DIV>これは「晴れ」</DIV> <DIV>インライン画像はcidを指定して<IMG src='cid:#{@sample_cid}'>このように表記します。</DIV></BODY></HTML>"))
+
+ # 各パートを組み立てる
+ # サンプルは docomo と同形式
+ @alt_part = MbMail::DMail.new
+ @alt_part.body = ''
+ @alt_part.content_type = 'multipart/alternative'
+ @alt_part.parts << @text_part
+ @alt_part.parts << @html_part
+ @rel_part = MbMail::DMail.new
+ @rel_part.body = ''
+ @rel_part.content_type = 'multipart/related'
+ @rel_part.parts << @alt_part
+ @inlined_image_parts.each { |ip| @rel_part.parts << ip }
+ @dm.parts << @rel_part
+ @attached_image_parts.each { |ap| @dm.parts << ap }
+ end
+ it "docomo 向けに変換できる" do
+ lambda { @dm.to_docomo_format }.should_not raise_error
+ end
+ it "au 向けに変換できる" do
+ lambda { @dm.to_au_format }.should_not raise_error
+ end
+ it "softbank 向けに変換できる" do
+ lambda { @dm.to_softbank_format }.should_not raise_error
+ end
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe MbMail::MbMailer, "は" do
+ before do
+ class TestMbMailer < MbMail::MbMailer
+ def testmail(sent_at = Time.now)
+ @subject = base64('日本語subject')
+ @recipients = [ INVALID_ADDRESS ]
+ @from = "#{base64('日本語from')} <#{INVALID_ADDRESS}>"
+ @sent_at = sent_at
+ end
+ end
+ TestMbMailer.template_root = SAMPLE_DIR
+ TestMbMailer.delivery_method = :test
+ end
+ it "日本語ヘッダを作成するためのbase64メソッドが使用できる" do
+ MbMail::MbMailer.instance_methods.should include('base64')
+ end
+ it "送信先および送信元に3つ以上の連続ドットを含むメールを正しく作成できる" do
+ TestMbMailer.deliver_testmail
+ TestMbMailer.deliveries.first.to.first.should == INVALID_ADDRESS
+ TestMbMailer.deliveries.first.from.first.should == INVALID_ADDRESS
+ end
+ it "本文に含まれる機種依存文字を iso-2022-jp エンコードにて正しく送信できる" do
+ TestMbMailer.deliver_testmail
+ TestMbMailer.deliveries.first.charset.should == "iso-2022-jp"
+ TestMbMailer.deliveries.first.body.match(Regexp.new("㌧")).should_not be_nil
+ end
+end
--- /dev/null
+From: test@example.com
+To: test@example.com
+Subject: test
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="-----=_NextPart_32760_65996_33696"
+
+
+-------=_NextPart_32760_65996_33696
+Content-Type: multipart/alternative; boundary="-----=_NextPart_35544_68780_36480"
+
+
+-------=_NextPart_35544_68780_36480
+Content-Type: text/plain; charset="iso-2022-jp"
+Content-Transfer-Encoding: 7bit
+
+This is a body of text/plain part.
+
+-------=_NextPart_35544_68780_36480
+Content-Type: text/html; charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable
+
+<HTML><HEAD><META http-equiv=3D=22Content-Type=22 content=3D=22text/html;=
+ charset=3Diso-2022-jp=22></HEAD><BODY><DIV>This is a body of text/html =
+part.<IMG src=3D=22cid:sample_cid=40example.com=22></DIV></BODY></HTML>
+-------=_NextPart_35544_68780_36480--
+
+-------=_NextPart_32760_65996_33696
+Content-Type: image/gif; name="inlined.gif"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline; filename="inlined.gif"
+Content-ID: <sample_cid@example.com>
+
+0lGODlhHgAeAJECAP///wCZ/////wAAACH5BAEAAAIALAAAAAAeAB4AAAJZ
+lI8Sye22WljxoVnZtLz7D4biqGGheQUAtnVsFanA3DosHSs4KmHrC1nNfpmL
+Tsab0IgWlTOJZDWJSSGz+aopgK5or8jRksYlMDlmHqXHa7L7DY/L5/T6uAAA
+Ow==
+
+-------=_NextPart_32760_65996_33696
+Content-Type: image/gif; name="attached.gif"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="attached.gif"
+
+R0lGODlhHgAeAJECAP///wBm/////wAAACH5BAEAAAIALAAAAAAeAB4AAAJq
+lI+py+0P4woBVinonRs36oWf5pEIBWigpR5XAMRpp6jxWsGzyaEqnpLxErng
+zqWDDWvBlgG1WzGUP5dxWftJM1XWjfbcskTkMkaLdo5eQuGp1L6ZdcZZmU5P
+3eNyMt59l0RlxtVFeHhYAAA7
+
+-------=_NextPart_32760_65996_33696--
+
--- /dev/null
+this is a template of sample mail.
+本文に機種依存文字を含む事もできます。
+㌧
+㌦
+などなど。
--- /dev/null
+require 'spec'
+require 'action_controller'
+require 'action_mailer'
+require File.dirname(__FILE__) + "/../lib/mb_mail.rb"
+SAMPLE_DIR = "#{File.dirname(__FILE__)}/sample"
+INVALID_ADDRESS = 'test...test...@example.com'
--- /dev/null
+# desc "Explaining what the task does"
+# task :mb_mail do
+# # Task goes here
+# end
--- /dev/null
+require 'test/unit'
+
+class MbMailTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ flunk
+ end
+end
--- /dev/null
+# Uninstall hook code here
--- /dev/null
+Copyright (c) 2005-2007 Peter Donald
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
--- /dev/null
+= active-form plugin for Rails
+
+active-form is a plugin that makes it easy to have model objects that support the
+ActiveRecord Validations but are not backed by database tables. The plugin is designed
+to make it possible to use the ActiveForm derived classes in a very similar manner to
+the ActiveRecord::Base derived objects.
+
+Originally described at;
+
+http://www.realityforge.org/articles/2005/12/02/validations-for-non-activerecord-model-objects
+
+== How To Define An ActiveForm Object
+
+class Search < ActiveForm
+ attr_accessor :text
+ validates_length_of :text, :maximum=>30
+end
+
+== How To Use ActiveForm Object In Controller
+
+class NavigatorController < ApplicationController
+ def search
+ @search = Search.new(params[:search])
+ if @search.valid?
+ ...do search here...
+ end
+ end
+end
+
+== How To Use ActiveForm Object In View
+
+<%= start_form_tag(:action => 'search') %>
+<%= error_messages_for('search') %>
+<%= text_field('search', 'text', {"maxlength" => 30}) %>
+<button type="submit">Save</button>
+<%= end_form_tag %>
+
+== Details
+
+License: Released under the MIT license.
+
+== Credits
+
+Peter Donald <peter at realityforge dot org>.
+Dae San Hwang for fix to work with Rails 1.1.
+Trevor Squires for suggestion to use Reloadable::Subclasses rather than dispatcher hack for Rails 1.1.
+Tim Lucas for patch to allow bulk addition of attributes.
+Geoff Schmidt for adding support for Callbacks.
+Sean Christman for attributes getter.
+Jack Christensen for attributes setter.
+Troy Anderson for fixing human_attribute_name for localizations.
--- /dev/null
+require 'active_form'
--- /dev/null
+# Note ".valid?" method must occur on object for validates_associated
+class ActiveForm
+ def initialize(attributes = nil)
+ self.attributes = attributes
+ yield self if block_given?
+ end
+
+ def attributes=(attributes)
+ attributes.each do |key,value|
+ send(key.to_s + '=', value)
+ end if attributes
+ end
+
+ def attributes
+ attributes = instance_variables
+ attributes.delete("@errors")
+ Hash[*attributes.collect { |attribute| [attribute[1..-1], instance_variable_get(attribute)] }.flatten]
+ end
+
+ def [](key)
+ instance_variable_get("@#{key}")
+ end
+
+ def []=(key, value)
+ instance_variable_set("@#{key}", value)
+ end
+
+ def method_missing( method_id, *args )
+ if md = /_before_type_cast$/.match(method_id.to_s)
+ attr_name = md.pre_match
+ return self[attr_name] if self.respond_to?(attr_name)
+ end
+ super
+ end
+
+ def to_xml(options = {})
+ options[:root] ||= self.class.to_s.underscore
+ attributes.to_xml(options)
+ end
+
+ alias_method :respond_to_without_attributes?, :respond_to?
+
+ def new_record?
+ true
+ end
+
+ def self.self_and_descendants_from_active_record
+ [self]
+ end
+
+protected
+ def raise_not_implemented_error(*params)
+ ValidatingModel.raise_not_implemented_error(*params)
+ end
+
+ def self.human_attribute_name(attribute_key_name, options = {})
+ defaults = self_and_descendants_from_active_record.map do |klass|
+ "#{klass.name.underscore}.#{attribute_key_name}""#{klass.name.underscore}.#{attribute_key_name}"
+ end
+ defaults << options[:default] if options[:default]
+ defaults.flatten!
+ defaults << attribute_key_name.humanize
+ options[:count] ||= 1
+ I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
+ end
+
+ def self.human_name(options = {})
+ defaults = self_and_descendants_from_active_record.map do |klass|
+ "#{klass.name.underscore}""#{klass.name.underscore}"
+ end
+ defaults << self.name.humanize
+ I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
+ end
+
+ # these methods must be defined before Validations include
+ alias save raise_not_implemented_error
+ alias update_attribute raise_not_implemented_error
+ alias save! raise_not_implemented_error
+
+ # The following must be defined prior to Callbacks include
+ alias create_or_update raise_not_implemented_error
+ alias create raise_not_implemented_error
+ alias update raise_not_implemented_error
+ alias destroy raise_not_implemented_error
+
+ def self.instantiate(record)
+ object = allocate
+ object.attributes = record
+ object
+ end
+
+public
+ include ActiveRecord::Validations
+ include ActiveRecord::Callbacks
+
+protected
+
+ # the following methods must be defined after include so that they overide
+ # methods previously included
+ class << self
+ def raise_not_implemented_error(*params)
+ raise NotImplementedError
+ end
+
+ alias validates_uniqueness_of raise_not_implemented_error
+ alias create! raise_not_implemented_error
+ alias validate_on_create raise_not_implemented_error
+ alias validate_on_update raise_not_implemented_error
+ alias save_with_validation raise_not_implemented_error
+ end
+end
--- /dev/null
+SSL Requirement
+===============
+
+SSL requirement adds a declarative way of specifying that certain actions
+should only be allowed to run under SSL, and if they're accessed without it,
+they should be redirected.
+
+Example:
+
+ class ApplicationController < ActiveRecord::Base
+ include SslRequirement
+ end
+
+ class AccountController < ApplicationController
+ ssl_required :signup, :payment
+ ssl_allowed :index
+
+ def signup
+ # Non-SSL access will be redirected to SSL
+ end
+
+ def payment
+ # Non-SSL access will be redirected to SSL
+ end
+
+ def index
+ # This action will work either with or without SSL
+ end
+
+ def other
+ # SSL access will be redirected to non-SSL
+ end
+ end
+
+You can overwrite the protected method ssl_required? to rely on other things
+than just the declarative specification. Say, only premium accounts get SSL.
+
+P.S.: Beware when you include the SslRequirement module. At the time of
+inclusion, it'll add the before_filter that validates the declarations. Some
+times you'll want to run other before_filters before that. They should then be
+declared ahead of including this module.
+
+Copyright (c) 2005 David Heinemeier Hansson, released under the MIT license
\ No newline at end of file
--- /dev/null
+# Copyright (c) 2005 David Heinemeier Hansson
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+module SslRequirement
+ def self.included(controller)
+ controller.extend(ClassMethods)
+ controller.before_filter(:ensure_proper_protocol)
+ end
+
+ module ClassMethods
+ # Specifies that the named actions requires an SSL connection to be performed (which is enforced by ensure_proper_protocol).
+ def ssl_required(*actions)
+ write_inheritable_array(:ssl_required_actions, actions)
+ end
+
+ def ssl_allowed(*actions)
+ write_inheritable_array(:ssl_allowed_actions, actions)
+ end
+ end
+
+ protected
+ # Returns true if the current action is supposed to run as SSL
+ def ssl_required?
+ (self.class.read_inheritable_attribute(:ssl_required_actions) || []).include?(action_name.to_sym)
+ end
+
+ def ssl_allowed?
+ (self.class.read_inheritable_attribute(:ssl_allowed_actions) || []).include?(action_name.to_sym)
+ end
+
+ private
+ def ensure_proper_protocol
+ return true if ssl_allowed?
+
+ if ssl_required? && !request.ssl?
+ redirect_to "https://" + request.host + request.request_uri
+ flash.keep
+ return false
+ elsif request.ssl? && !ssl_required?
+ redirect_to "http://" + request.host + request.request_uri
+ flash.keep
+ return false
+ end
+ end
+end
\ No newline at end of file
--- /dev/null
+begin
+ require 'action_controller'
+rescue LoadError
+ if ENV['ACTIONCONTROLLER_PATH'].nil?
+ abort <<MSG
+Please set the ACTIONCONTROLLER_PATH environment variable to the directory
+containing the action_controller.rb file.
+MSG
+ else
+ $LOAD_PATH.unshift << ENV['ACTIONCONTROLLER_PATH']
+ begin
+ require 'action_controller'
+ rescue LoadError
+ abort "ActionController could not be found."
+ end
+ end
+end
+
+require 'action_controller/test_process'
+require 'test/unit'
+require "#{File.dirname(__FILE__)}/../lib/ssl_requirement"
+
+ActionController::Base.logger = nil
+ActionController::Routing::Routes.reload rescue nil
+
+class SslRequirementController < ActionController::Base
+ include SslRequirement
+
+ ssl_required :a, :b
+ ssl_allowed :c
+
+ def a
+ render :nothing => true
+ end
+
+ def b
+ render :nothing => true
+ end
+
+ def c
+ render :nothing => true
+ end
+
+ def d
+ render :nothing => true
+ end
+
+ def set_flash
+ flash[:foo] = "bar"
+ end
+end
+
+class SslRequirementTest < Test::Unit::TestCase
+ def setup
+ @controller = SslRequirementController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_redirect_to_https_preserves_flash
+ get :set_flash
+ get :b
+ assert_response :redirect
+ assert_equal "bar", flash[:foo]
+ end
+
+ def test_not_redirecting_to_https_does_not_preserve_the_flash
+ get :set_flash
+ get :d
+ assert_response :success
+ assert_nil flash[:foo]
+ end
+
+ def test_redirect_to_http_preserves_flash
+ get :set_flash
+ @request.env['HTTPS'] = "on"
+ get :d
+ assert_response :redirect
+ assert_equal "bar", flash[:foo]
+ end
+
+ def test_not_redirecting_to_http_does_not_preserve_the_flash
+ get :set_flash
+ @request.env['HTTPS'] = "on"
+ get :a
+ assert_response :success
+ assert_nil flash[:foo]
+ end
+
+ def test_required_without_ssl
+ assert_not_equal "on", @request.env["HTTPS"]
+ get :a
+ assert_response :redirect
+ assert_match %r{^https://}, @response.headers['Location']
+ get :b
+ assert_response :redirect
+ assert_match %r{^https://}, @response.headers['Location']
+ end
+
+ def test_required_with_ssl
+ @request.env['HTTPS'] = "on"
+ get :a
+ assert_response :success
+ get :b
+ assert_response :success
+ end
+
+ def test_disallowed_without_ssl
+ assert_not_equal "on", @request.env["HTTPS"]
+ get :d
+ assert_response :success
+ end
+
+ def test_disallowed_with_ssl
+ @request.env['HTTPS'] = "on"
+ get :d
+ assert_response :redirect
+ assert_match %r{^http://}, @response.headers['Location']
+ end
+
+ def test_allowed_without_ssl
+ assert_not_equal "on", @request.env["HTTPS"]
+ get :c
+ assert_response :success
+ end
+
+ def test_allowed_with_ssl
+ @request.env['HTTPS'] = "on"
+ get :c
+ assert_response :success
+ end
+end
\ No newline at end of file