Project import
diff --git a/logrotate/.travis.yml b/logrotate/.travis.yml
new file mode 100644
index 0000000..c1696d9
--- /dev/null
+++ b/logrotate/.travis.yml
@@ -0,0 +1,128 @@
+sudo: required
+dist: precise
+language: c
+
+matrix:
+  include:
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.4
+      env: COMPILER=gcc-4.4
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.5
+      env: COMPILER=gcc-4.5
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.6
+      env: COMPILER=gcc-4.6
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.7
+      env: COMPILER=gcc-4.7
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.8
+      env: COMPILER=gcc-4.8
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.9
+      env: COMPILER=gcc-4.9
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-5
+      env: COMPILER=gcc-5
+    - compiler: gcc
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-6
+      env: COMPILER=gcc-6
+    - compiler: clang
+      addons:
+        apt:
+          packages:
+            - clang-3.4
+      env: COMPILER=clang
+    - compiler: clang
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+            - llvm-toolchain-precise-3.5
+          packages:
+            - clang-3.5
+      env: COMPILER=clang-3.5
+    - compiler: clang
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+            - llvm-toolchain-precise-3.6
+          packages:
+            - clang-3.6
+      env: COMPILER=clang-3.6
+    - compiler: clang
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+            - llvm-toolchain-precise-3.7
+          packages:
+            - clang-3.7
+      env: COMPILER=clang-3.7
+
+install:
+  - sudo add-apt-repository ppa:dns/gnu -y
+  - sudo apt-get -qq update
+  - sudo apt-get -qq install libpopt-dev libselinux1-dev libacl1-dev automake dash rpm
+
+script:
+  - $COMPILER --version
+  - ./autogen.sh
+  - ./configure CC=$COMPILER
+  - make
+  - make test
+  - cd test && LOGROTATE=../logrotate /bin/dash test && cd ..
+  - make distcheck
+  # nodeps because rpm build deps can not be installed on debian system
+  - make rpm RPM_FLAGS="--nodeps"
+
+notifications:
+  email:
+    recipients:
+      - logrotate-owner@fedoraproject.org
+    on_success: always
+    on_failure: always
+
+# vim:et:ts=2:sw=2
diff --git a/logrotate/Android.mk b/logrotate/Android.mk
new file mode 100644
index 0000000..2bad090
--- /dev/null
+++ b/logrotate/Android.mk
@@ -0,0 +1,58 @@
+LOCAL_PATH :=$(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := logrotate
+LOCAL_INIT_RC := logrotate.rc
+LOCAL_SRC_FILES := basenames.c config.c log.c logrotate.c glob.c
+LOCAL_CFLAGS := \
+	-D_SIZE_T_DECLARED \
+	-DPACKAGE_NAME=\"logrotate\" \
+	-DPACKAGE_TARNAME=\"logrotate\" \
+	-DPACKAGE_VERSION=\"3.10.0\" \
+	-DPACKAGE_STRING=\"logrotate\ 3.10.0\" \
+	-DPACKAGE_BUGREPORT=\"\" \
+	-DPACKAGE_URL=\"\" \
+	-DPACKAGE=\"logrotate\" \
+	-DVERSION=\"3.10.0\" \
+	-D_GNU_SOURCE=1 \
+	-DSTDC_HEADERS=1 \
+	-DHAVE_SYS_TYPES_H=1 \
+	-DHAVE_SYS_STAT_H=1 \
+	-DHAVE_STDLIB_H=1 \
+	-DHAVE_STRING_H=1 \
+	-DHAVE_MEMORY_H=1 \
+	-DHAVE_STRINGS_H=1 \
+	-DHAVE_INTTYPES_H=1 \
+	-DHAVE_STDINT_H=1 \
+	-DHAVE_UNISTD_H=1 \
+	-DHAVE_STRUCT_STAT_ST_BLKSIZE=1 \
+	-DHAVE_ST_BLKSIZE=1 \
+	-DHAVE_STRUCT_STAT_ST_BLOCKS=1 \
+	-DHAVE_ST_BLOCKS=1 \
+	-DHAVE_LIBPOPT=1 \
+	-DHAVE_ASPRINTF=1 \
+	-DHAVE_FORK=1 \
+	-DHAVE_MADVISE=1 \
+	-DHAVE_QSORT=1 \
+	-DHAVE_STRNDUP=1 \
+	-DHAVE_STRPTIME=1 \
+	-DHAVE_VFORK=1 \
+	-DHAVE_VSYSLOG=1 \
+
+LOCAL_SHARED_LIBRARIES := \
+	libpopt \
+	liblog \
+	libcutils \
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := logrotate.conf
+LOCAL_SRC_FILES := logrotate.conf
+LOCAL_MODULE_CLASS := ETC
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := log_sender
+LOCAL_SRC_FILES := log_sender
+LOCAL_MODULE_CLASS := EXECUTABLES
+include $(BUILD_PREBUILT)
diff --git a/logrotate/CONTRIBUTING.md b/logrotate/CONTRIBUTING.md
new file mode 100644
index 0000000..84d3d52
--- /dev/null
+++ b/logrotate/CONTRIBUTING.md
@@ -0,0 +1,16 @@
+# logrotate contributions
+
+## Pull requests
+
+  - Fork it.
+  - Create your feature branch (`git checkout -b fixing-blah`), please avoid working directly on the `master` branch.
+  - Check for unnecessary whitespaces with `git diff --check` before committing.
+  - [optional] Add user visible changes to `ChangeLog.md` under the `UNRELEASED` section
+  - Commit your changes, try to follow this format: 
+```
+scope: short summary of the change
+[empty line]
+Long description of the change, explanation of why the change is useful, etc.
+```
+  - Push to the branch (`git push -u origin fixing-blah`).
+  - Create a new pull request.
diff --git a/logrotate/COPYING b/logrotate/COPYING
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/logrotate/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/logrotate/ChangeLog.md b/logrotate/ChangeLog.md
new file mode 100644
index 0000000..ad84459
--- /dev/null
+++ b/logrotate/ChangeLog.md
@@ -0,0 +1,426 @@
+# logrotate change log
+
+All notable changes to this project will be documented in this file.
+
+## [UNRELEASED]
+
+## [3.10.0] - 2016-08-03
+
+  - Legacy Makefile renamed to Makefile.legacy, will be removed eventually.
+  - Fix 'make dist' and 'make distcheck' to produce a usable release tarball.
+  - Fix 'olddir' usage with wildcard in the middle of path in the pattern
+    definition when the pattern did not match any log file.
+  - Remove half-rotated files when rotation of particular log file is skipped
+    because of an error during copy or compression.
+
+## [3.9.2] - 2016-01-20
+  - Upstream moved to GitHub: <https://github.com/logrotate/logrotate>.
+  - Add support for %M, %S and %V in "dateext" directive.
+  - Fix bad filename in subject of email when "compress" and "maillast" is
+    used.
+  - Allow rotating files created before 1996.
+  - Fix compilation errors on NetBSD caused by "array subscript has
+    type 'char' in config.c"
+  - Fix matching subdirectories on BSD systems for patterns like
+    "*/log" in situation where logrotate tried to match "foo/log" even when
+    "foo" has not been a directory.
+  - Fix logging dates in debug messages.
+  - Remove state file entries for logs which do not exist and have not been
+    rotated for more than a year.
+  - Fix poor performance with big state file.
+  - Support logging to syslog by using '-l syslog'.
+  - Allow running test-suite using dash.
+
+## [3.9.1] - 2015-04-03
+  - Fix off-by-one error which can lead to crash when copytruncate is used.
+
+## [3.9.0] - 2015-04-03
+  - Fix crash when using long dateformat. [nmerdan]
+  - Add support for %H dateformat. [czchen]
+  - Fix regression introduced in 3.8.9 when when rotating multiple
+    logs when one of them is missing.
+  - In the debug mode, do not skip the code-path which handles the case when
+    the last rotation does not exist. [Sergey Vidishev]
+  - Show more precise description when "log does not need rotating".
+  - Add new -l option to log verbose output to file. The file is overwritten
+    on every logrotate execution.
+  - Allow rotation of sparse files with copytruncate.
+
+## [3.8.9] - 2015-02-13
+  - Add new directive "createolddir" and "nocreateolddir". These directives
+    can be used to create the directory specified by olddir with particular
+    "mode", "owner" and "group".
+  - Continue with rotation even when first log from logset is removed
+    during the rotation.
+  - Fix crash on BSD systems introduced in 3.8.8 caused by different qsort_r
+    function. Function qsort is now used instead.
+  - Fix potential buffer overflow in usage of strncat function.
+  - Fix compilation with musl-libc.
+  - Add experimental 'renamecopy' directive to allow 'olddir' on different
+    physical device. See the "man logrotate" for more information.
+
+## [3.8.8] - 2014-10-16
+  - Add support for building using autotools/automake. Using "./autogen.sh",
+    "./configure" and "make" is now preferred way how to build logrotate.
+    Old Makefile remains available, but it is deprecated and will be removed
+    in the future. Please report any problem related to new build system.
+  - Add support for systems which do not support fork (use vfork instead)
+    and madvise.
+  - Fix bug when wrong log file has been removed in case of dateext and
+    dateformat %d-%m-%Y.
+  - Do not expect that the name of root account is 'root'.
+  - Do not stop rotation with an error when olddir and log file
+    are on different devices and copy or copytruncate is used.
+  - Return an error code when parent directory of log does not exist,
+    "su" directive is not used, logrotate is running as root and missingok
+    is not specified. [vcizek]
+  - Prepend error printed by compression program with the log name even when
+    the compression program exits with zero exit code.
+
+## [3.8.7] - 2013-10-10
+  - Fixed --force/-f option handling together with "size" directive
+    (3.8.5 regression).
+  - Use "logrotate_tmp_t" context for SELinux tests and if this context does
+    not exist, skip SELinux related tests.
+
+## [3.8.6] - 2013-07-31
+  - Fixed memory corruption caused by rotation directory which does not
+    exist with "sharedscripts" together with "prerotate" script.
+
+## [3.8.5] - 2013-06-10
+  - Improved rotation during daylight saving time and between timezone
+    changes.
+  - Fixed ACL setting problem caused by ext3 erroneously reporting ENOSYS
+    instead of ENOSUP.
+  - Do not continue with rotation if state file is corrupted.
+  - Make logrotate.status creation atomic.
+  - Allow "hourly" rotation. See manpage for more information.
+  - Use "/bin/echo" in tests. Fixes tests execution in Dash.
+  - Do no try to parse config files bigger than 16MB.
+  - Improved manpage consistency and formatting.
+  - Fix race condition between acl_set_fd() and fchmod().
+
+## [3.8.4] - 2013-04-30
+  - Added --version command line option
+  - Disable ACL tests if logrotate is not compiled WITH_ACL support or if
+    ACLs are not supported by the system running tests
+  - Disable SELinux tests if logrotate is not compiled WITH_SELINUX support
+    or if SELinux is not supported by the system running tests
+  - Fixed bug which prevented skipping particular log file config
+    if the config contained errors.
+  - Fixed skipping of configs containing firstaction/lastaction scripts
+    with '}' character in case of error before these scripts.
+  - Support also 'K' unit for *size directives.
+  - Added preremove option to let admin to do something with the old logs
+    before they are removed by logrotate.
+  - Fixed possible loop in tabooext parsing.
+  - Move code to set SELinux context before compressLogFile calls to create
+    compressed log files with the proper context.
+  - Call prerotate/postrotate script only for really rotated files in
+    nosharedscripts mode (as stated in man page).
+
+## [3.8.3] - 2012-10-04
+  - Fixed setting "size" bigger than 4GB on 32bit architectures
+  - Do not overwrite mode set by "create" option when using ACL. "create"
+    directive is now not mixed up with ACLs. If you use "create" in config
+    file and log file has some ACLs set, ACLs are not kept and are
+    overwritten by the mode set in "create" directive.
+  - Mode argument in "create" directive can be omitted. Only owner and group
+    is set in this case. Check man page for more info.
+
+## [3.8.2] - 2012-08-01
+  - show error and ignore config if '{' is not present after log files
+    declaration
+  - support whitespaces in compressoptions directive
+  - support for tilde expansion in config files
+  - 'su' directive does not affect script execution - scripts
+    are executed as a root if 'su' directive is present
+  - fixed mail sending for 'mailfirst', 'dateext' and 'delaycompress'
+    combination
+  - do not use gzip/gunzip from /usr/local on Solaris
+  - add O_NOFOLLOW when opening files as safeguard against symlink tricks.
+    Symlinks rotation is now officially unsupported. It didn't work
+    as expected in the past anyway.
+  - do not run external programs with uid != euid
+  - fixed potential bad-free when ACL is used
+  - Do not include alloca.h on NetBSD, since alloca() is declared in
+    stdlib.h there
+  - 13 new tests added
+
+## [3.8.1] - 2011-08-31
+  - fixed 1 memory leak in prerotateSingleLog
+  - another fixes for Solaris
+  - fixed HP-UX compilation and default config
+  - do not redirect logrotate errors to /dev/null in cron script
+  - fixed "size" directive parsing
+  - handle situation when acl_get_fd is supported, but acl_set_fd is not
+  - added "maxsize" directive (see man page)
+
+## [3.8.0] - 2011-06-21
+  - added "dateyesterday" option (see man page)
+  - fixed crash when config file had exactly 4096*N bytes
+  - added WITH_ACL make option to link against -lacl and preserve ACLs
+    during rotation
+  - added "su" option to define user/group for rotation. Logrotate now
+    skips directories which are world writable or writable by group
+    which is not "root" unless "su" directive is used.
+  - fixed CVE-2011-1098: race condition by creation of new files
+  - fixed possible shell injection when using "shred" directive (CVE-2011-1154)
+  - fixed escaping of file names within 'write state' action (CVE-2011-1155)
+  - better 'size' directive description
+  - fixed possible buffer-overflow when reading config files
+  - NetBSD/FreeBSD compilation fixes
+  - Solaris compilation fixes
+
+## [3.7.9] - 2010-06-28
+  - fix building on Solaris (patch by András Szilárd)
+  - don't copy config files on the stack -- mmap them instead
+    (fixes segfaults with too large/invalid config files)
+  - symlinked conf file man page as requested by Fedora guidelines
+    (thanks to Ivana Hutarova Varekova)
+  - cron script logrotate.cron redirects output to /dev/null
+  - added rotating (copying) non-writable, readable files
+    (patch by Henrique Martins)
+  - fixed missingok problem with globs
+    (taken from the Debian patches by Ted Percival
+     <ted@midg3t.net>)
+  - fixed bug when log files could be removed even there was
+    some error in rotation process.
+  - allow setting size greater than 4.2GB in configuration file
+  - pass currently rotated file to postrotate/prerotate script
+    in nosharedscripts mode
+  - added new TabooExts: ".disabled", ".dpkg-old", ".dpkg-dist",
+    ".dpkg-new", ".cfsaved", ".ucf-old", ".ucf-dist", ".ucf-new"
+    (taken from the Debian patches by Paul Martin <pm@debian.org>)
+  - Don't change utime atime/mtime when compressing files
+    (taken from the Debian patches by Paul Martin <pm@debian.org>)
+  - Better *rotate scripts parser. (taken from the Debian patches)
+  - Allow 'include' directive in log file definitions
+
+## [3.7.8] - 2009-01-28
+  - do not exit on status file errors
+  - limit config file inclusion nesting
+  - use hashes for status file handling (patch by Petr Tesarik
+    <ptesarik@suse.cz> and Leonardo Chiquitto)
+  - dateformat to allow unixtime (patch by Sami Kerola
+    <kerolasa@iki.fi>)
+  - manual page corrections (taken from the Debian patches by
+    Paul Martin <pm@debian.org>)
+
+## [3.7.7] - 2008-05-19
+  - dateformat
+  - fix possible buffer overflows in strings handling
+  - various minor bugfixes
+  - change logInfo handling (patches by Leonardo Chiquitto)
+
+## [3.7.6] - 2008-05-14
+  - patches from Leonardo Chiquitto that fix compile warnings
+  - examples/logrotate-default: add btmp rotation, dateext
+  - update man page
+  - tabooext honor wildcards
+  - fix selinux support with dateext
+
+## [3.7.5] - 2007-03-01
+  - import Fedora patches
+  - add option to use shred for deleting files, patch by
+    Peter Eckersley <pde@eff.org>
+  - ignore .cfsaved files
+  - bugfixes
+
+## [3.7.1] - 2004-10-20
+  - Fix sending mails and running scripts after the
+    system() -> execve() changes
+  - Preserve file attributes when compressing files (original patch
+    by Daniel Himler)
+
+## [3.7] - 2004-01-26
+  - always use compressext for the extension for compressed
+    files; before compresscmd and compressext had to agree
+  - moved all compression to one code block
+  - compression, scripts don't use system() anymore
+  - compress and maillast didn't work together properly
+  - delaycompress and mailfirst didn't work properly
+  - don't use system() for mailing (or uncompressing) logs anymore
+  - use "-s" for speciying the subjected of mailed logs
+
+## [3.6] - 2001-11-28
+  - See .spec file for changes
+
+## [3.5.4] - 2001-01-05
+  - %defattr(-,root,root) in specfile
+
+## [3.5.3] - 2001-01-03
+  - patch /tmp file race condition problem, use mkstemp;
+    Thanks go to Solar Designer <solar@openwall.com>
+
+## [3.5.2] - 2000-09-29
+  - added .swp and .rpmnew to default taboo list
+
+## [3.5.1] - 2000-08-11
+  - handle state dates in the future a bit more sanely
+
+## [3.5] - 2000-07-23
+  - multiple file names/patterns may be given for a single entry
+  - fixed mistake in when logs were uncompressed before mailing
+
+## [3.4] - 2000-07-13
+  - added sharedscripts/nosharedscripts
+  - added simple testbed
+  - quote filenames in state file to allow proper rotation of files
+    with spaces in the name -- this changes the version number of
+    the state file!
+  - ignore white space at end of line
+
+## [3.3.2] - 2000-06-19
+  - don't rotate lastlog
+
+## [3.3.1] - 2000-02-03
+  - support gzipped man pages
+
+## [3.3] - 1999-06-16
+  - added "mailfirst" and "maillast" flags (based on Tim Wall's patch)
+  - documented "extension" flag
+  - "rotate 0" gives proper script and mail behavior
+
+## [3.2] - 1999-04-07
+  - create wtmp with correct perms
+
+## [3.1] - 1999-04-01
+  - fixed small alloca()
+  - added missingok flag
+  - use popt to display usage message
+  - handle /some/file { } in config file
+
+## [3.0] - 1999-03-18
+  - updates for glibc 2.1
+
+## [2.9] - 1999-03-05
+  - fixed a bug parsing lines where { immediately follows the filename
+  - allow log file patterns to be placed in double quotes, which	
+    allows spaces in names
+  - complain about missing log files (John Van Essen)
+
+## [2.8] - 1999-01-13
+  - changes for glibc 2.1 (Cristian Gafton)
+
+## [2.7] - 1998-12-29
+  - updated man page to include --force (Simon Mudd)
+  - invoke scripts via /bin/sh rather then relying on /tmp execute
+    semantics (Philip Guenther)
+  - added "extension" option for forcing a file extension after rotation
+    (Rob Hagopian)
+
+## [2.6] - 1998-05-05
+  - added nodelaycompress flag (from Jos Vos)
+  - added copytruncate, nocopytruncate flag (from Jos Vos)
+  - removed umask handling; explicitly use fchmod() insteadmoved umask
+  - added --force option (Simon Mudd)
+  - moved /bin/mail to MAIL_COMMAND define (Simon Mudd)
+  - fixed segv caused by overly long filenames
+  - switched from getopt_long to popt
+
+## [2.5] - 1997-09-01
+  - set the umask of the process to 0, letting open() create processes
+    with the proper permissions
+  - added delaycompress flag (from Jos Vos)
+  - fixed how old logs are finally removed when an olddir is specified
+    (Jos Vos)
+  - added nomail option
+  - added mail, nomail documentation to man page
+  - added the tabooext directive
+  - fixed problem in globbing
+
+## [2.4] - 1997-08-11
+  - glob log names in config file
+  - added ,v to taboo list
+  - fixed bug w/ create parsing
+  - use an int rather then a mode_t when parsing create entries as
+    sscanf requires it
+
+## [2.3] - 1997-03-18
+  - fill in all of last rotated structure (this probable isn't
+    really necessary but it's a bit cleaner and will avoid future
+    problems);
+  - fixed .spec file
+
+## [2.2] - 1997-02-27
+  - If a file is rotated and we have no state information for it,
+    right out the current time.
+  - Weekly rotation happens when the current weekday is less then
+    the weekday of the last rotation or more then a week has
+    elapsed between the last rotation and now
+  - Monthly rotation happens when the current month is different
+    from the last month or the current year is different from the
+    last year
+  - (these were contributed and suggested by Ronald Wahl)
+  - added olddir/noolddir options
+  - added ifempty/notifempty options
+  - ignore nonnormal files when reading config files from a directory
+  - (these were suggested and originally implemented by
+    Henning Schmiedehausen)
+  - updated the man page to reflect these changes
+  - made "make install" accept PREFIX argument
+  - added .spec file to tarball
+
+## [2.1] - 1997-01-13
+  - Don't output state information for logs that have never been
+    rotated (better then 1900-1-0)
+  - Accept 1900-1-0 as time 0
+
+## [2.0.2] - 1996-12-10
+  - I have no idea :-(
+
+## [2.0.1] - 1996-12-09
+  - ignore files in included directories which end with ~, .rpmorig, or
+    .rpmsave
+
+[UNRELEASED]: https://github.com/logrotate/logrotate/compare/3.10.0...master
+[3.10.0]: https://github.com/logrotate/logrotate/compare/3.9.2...3.10.0
+ [3.9.2]: https://github.com/logrotate/logrotate/compare/r3-9-1...3.9.2
+ [3.9.1]: https://github.com/logrotate/logrotate/compare/r3-9-0...r3-9-1
+ [3.9.0]: https://github.com/logrotate/logrotate/compare/r3-8-9...r3-9-0
+ [3.8.9]: https://github.com/logrotate/logrotate/compare/r3-8-8...r3-8-9
+ [3.8.8]: https://github.com/logrotate/logrotate/compare/r3-8-7...r3-8-8
+ [3.8.7]: https://github.com/logrotate/logrotate/compare/r3-8-6...r3-8-7
+ [3.8.6]: https://github.com/logrotate/logrotate/compare/r3-8-5...r3-8-6
+ [3.8.5]: https://github.com/logrotate/logrotate/compare/r3-8-4...r3-8-5
+ [3.8.4]: https://github.com/logrotate/logrotate/compare/r3-8-3...r3-8-4
+ [3.8.3]: https://github.com/logrotate/logrotate/compare/r3.8.2...r3-8-3
+ [3.8.2]: https://github.com/logrotate/logrotate/compare/r3-8-1...r3.8.2
+ [3.8.1]: https://github.com/logrotate/logrotate/compare/r3-8-0...r3-8-1
+ [3.8.0]: https://github.com/logrotate/logrotate/compare/r3-7-9...r3-8-0
+ [3.7.9]: https://github.com/logrotate/logrotate/compare/r3-7-8...r3-7-9
+ [3.7.8]: https://github.com/logrotate/logrotate/compare/r3-7-7...r3-7-8
+ [3.7.7]: https://github.com/logrotate/logrotate/compare/r3-7-6...r3-7-7
+ [3.7.6]: https://github.com/logrotate/logrotate/compare/r3-7-5...r3-7-6
+ [3.7.5]: https://github.com/logrotate/logrotate/compare/r3-7-1...r3-7-5
+ [3.7.1]: https://github.com/logrotate/logrotate/compare/r3-7...r3-7-1
+   [3.7]: https://github.com/logrotate/logrotate/compare/r3-6...r3-7
+   [3.6]: https://github.com/logrotate/logrotate/compare/r3-5-4...r3-6
+ [3.5.4]: https://github.com/logrotate/logrotate/compare/r3-5-3...r3-5-4
+ [3.5.3]: https://github.com/logrotate/logrotate/compare/r3-5-2...r3-5-3
+ [3.5.2]: https://github.com/logrotate/logrotate/compare/r3-5-1...r3-5-2
+ [3.5.1]: https://github.com/logrotate/logrotate/compare/r3-5...r3-5-1
+   [3.5]: https://github.com/logrotate/logrotate/compare/r3-4...r3-5
+   [3.4]: https://github.com/logrotate/logrotate/compare/r3-3-2...r3-4
+ [3.3.2]: https://github.com/logrotate/logrotate/compare/r3-3-1...r3-3-2
+ [3.3.1]: https://github.com/logrotate/logrotate/compare/r3-3...r3-3-1
+   [3.3]: https://github.com/logrotate/logrotate/compare/r3-2...r3-3
+   [3.2]: https://github.com/logrotate/logrotate/compare/r3-1...r3-2
+   [3.1]: https://github.com/logrotate/logrotate/compare/r3-0...r3-1
+   [3.0]: https://github.com/logrotate/logrotate/compare/r2-9...r3-0
+   [2.9]: https://github.com/logrotate/logrotate/compare/r2-8...r2-9
+   [2.8]: https://github.com/logrotate/logrotate/compare/r2-7...r2-8
+   [2.7]: https://github.com/logrotate/logrotate/compare/r2-6...r2-7
+   [2.6]: https://github.com/logrotate/logrotate/compare/r2-5...r2-6
+   [2.5]: https://github.com/logrotate/logrotate/compare/r2-4...r2-5
+   [2.4]: https://github.com/logrotate/logrotate/compare/2-3...r2-4
+   [2.3]: https://github.com/logrotate/logrotate/compare/2-2...2-3
+   [2.2]: https://github.com/logrotate/logrotate/compare/2-1...2-2
+   [2.1]: https://github.com/logrotate/logrotate/compare/2-0-2...2-1
+ [2.0.2]: https://github.com/logrotate/logrotate/compare/2-0-1...2-0-2
+ [2.0.1]: https://github.com/logrotate/logrotate/commits/2-0-1
+
+<!--
+vim:et:sw=2:ts=2
+-->
diff --git a/logrotate/INSTALL b/logrotate/INSTALL
new file mode 100644
index 0000000..79fc3b5
--- /dev/null
+++ b/logrotate/INSTALL
@@ -0,0 +1,23 @@
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code
+
+  2. Configure logrotate using `./configure'.
+
+  3. Type `make' to compile the package.
+
+  4. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  5. Type `make install' to install the programs and any data files and
+     documentation.
+
+  6. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.
+
+
+If you want to add the Access Control List (ACL) support to the program
+use `./configure --with-acl=yes' at the point 2.
+
+If you want to add the NSA Security-Enhanced Linux (SELinux) support to
+the program use `./configure --with-selinux=yes' at the point 2.
diff --git a/logrotate/LICENSE b/logrotate/LICENSE
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/logrotate/LICENSE
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/logrotate/MODULE_LICENSE_GPL b/logrotate/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/logrotate/MODULE_LICENSE_GPL
diff --git a/logrotate/Makefile.am b/logrotate/Makefile.am
new file mode 100644
index 0000000..da3f21b
--- /dev/null
+++ b/logrotate/Makefile.am
@@ -0,0 +1,36 @@
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+AM_CFLAGS = -Wall -Werror
+sbin_PROGRAMS = logrotate
+logrotate_SOURCES = basenames.c config.c log.c logrotate.c \
+		    basenames.h config.h log.h logrotate.h queue.h
+
+dist_man_MANS = logrotate.8 logrotate.conf.5
+
+dist_noinst_DATA = logrotate.spec
+
+EXTRA_DIST = ChangeLog.md README.md autogen.sh examples
+
+# deprecated hard-wired Makefile, will be removed eventually
+EXTRA_DIST += Makefile.legacy
+
+# the dot ensures that logrotate is built before it is tested
+SUBDIRS = . test
+
+# for compatibility with older releases of logrotate
+test: check
+
+.PHONY: srpm rpm
+
+rpm: srpm
+	rpmbuild $(RPM_FLAGS) -ta $(distdir).tar.gz
+srpm: dist
+	rpmbuild $(RPM_FLAGS) -ts $(distdir).tar.gz
diff --git a/logrotate/Makefile.legacy b/logrotate/Makefile.legacy
new file mode 100644
index 0000000..7aa5b20
--- /dev/null
+++ b/logrotate/Makefile.legacy
@@ -0,0 +1,189 @@
+VERSION = $(shell awk '/Version:/ { print $$2 }' logrotate.spec)
+OS_NAME = $(shell uname -s)
+LFS = $(shell echo `getconf LFS_CFLAGS 2>/dev/null`)
+CFLAGS = -Wall -D_GNU_SOURCE -D$(OS_NAME) -DVERSION=\"$(VERSION)\" -DHAVE_STRPTIME=1 -DHAVE_QSORT -DHAVE_STRUCT_STAT_ST_BLOCKS -DHAVE_STRUCT_STAT_ST_BLKSIZE -DHAVE_VSYSLOG $(RPM_OPT_FLAGS) $(LFS)
+PROG = logrotate
+MAN = logrotate.8
+MAN5 = logrotate.conf.5
+LOADLIBES = -lpopt
+SVNURL= svn+ssh://svn.fedorahosted.org/svn/logrotate
+SVNPUBURL = http://svn.fedorahosted.org/svn/logrotate
+SVNTAG = r$(subst .,-,$(VERSION))
+
+ifeq ($(WITH_SELINUX),yes)
+CFLAGS += -DWITH_SELINUX
+LOADLIBES += -lselinux
+# See pretest
+TEST_SELINUX=1
+else
+# See pretest
+TEST_SELINUX=0
+endif
+
+ifeq ($(WITH_ACL),yes)
+CFLAGS += -DWITH_ACL
+LOADLIBES += -lacl
+# See pretest
+TEST_ACL=1
+else
+# See pretest
+TEST_ACL=0
+endif
+
+# HP-UX using GCC
+ifeq ($(OS_NAME),HP-UX)
+    ifeq ($(RPM_OPT_FLAGS),)
+        RPM_OPT_FLAGS = -O2
+    endif
+    CC = gcc
+    INSTALL = cpset
+    ifeq ($(POPT_DIR),)
+        POPT_DIR = /usr/local
+    endif
+    ifeq ($(HPLX_DIR),)
+	HPLX_DIR = /usr/local/hplx
+    endif
+    LOADLIBES += -lhplx -L$(HPLX_DIR)/lib
+    ifeq ($(BASEDIR),)
+	BASEDIR = /usr/local
+    endif
+endif
+
+# Solaris using gcc
+ifeq ($(OS_NAME),SunOS)
+    CFLAGS = -D_GNU_SOURCE -D$(OS_NAME) -DVERSION=\"$(VERSION)\" $(RPM_OPT_FLAGS) $(LFS)
+    CC ?= gcc
+    CPP = $(CC) -E -M
+    INSTALL = /usr/ucb/install
+    ifeq ($(CC),cc)
+        CPP = cc -xM
+    endif
+    BASEDIR ?= /usr/local
+endif
+
+# Red Hat Linux
+ifeq ($(OS_NAME),Linux)
+    INSTALL = install
+    BASEDIR = /usr
+endif
+
+# FreeBSD
+ifeq ($(OS_NAME),FreeBSD)
+    LOADLIBES += -L${LOCALBASE}/lib
+    CFLAGS += -I${LOCALBASE}/include
+    PREFIX=
+endif
+
+ifeq ($(OS_NAME),NetBSD)
+    CFLAGS += -I/usr/include
+    CFLAGS += -I$(BASEDIR)/include
+    LOADLIBES += -L/usr/lib
+    LOADLIBES += -L$(BASEDIR)/lib -Wl,-R,$(BASEDIR)/lib
+endif
+
+ifneq ($(POPT_DIR),)
+    CFLAGS += -I$(POPT_DIR)
+    LOADLIBES += -L$(POPT_DIR)
+endif
+
+ifneq ($(STATEFILE),)
+    CFLAGS += -DSTATEFILE=\"$(STATEFILE)\"
+endif
+
+BINDIR = $(BASEDIR)/sbin
+MANDIR ?= $(BASEDIR)/man
+
+#--------------------------------------------------------------------------
+
+OBJS = logrotate.o log.o config.o basenames.o
+SOURCES = $(subst .o,.c,$(OBJS) $(LIBOBJS))
+
+ifeq ($(RPM_OPT_FLAGS),)
+CFLAGS += -g
+LDFLAGS = -g
+endif
+
+LDFLAGS += $(EXTRA_LDFLAGS) $(EXTRA_LIBS)
+CFLAGS  += $(EXTRA_CPPFLAGS) $(EXTRA_CFLAGS) 
+
+ifeq (.depend,$(wildcard .depend))
+TARGET=$(PROG)
+else
+TARGET=depend $(PROG)
+endif
+
+RCSVERSION = $(subst .,-,$(VERSION))
+
+all: show_warning $(TARGET) pretest
+
+show_warning:
+	@echo ""
+	@echo "Building using this Makefile is DEPRECATED."
+	@echo "Use './autogen.sh', './configure' and 'make' instead."
+	@echo "Some new features will not be enabled when building using this Makefile."
+	@echo ""
+
+$(PROG): $(OBJS)
+
+clean:
+	rm -f $(OBJS) $(PROG) core* .depend
+	rm -f ./test/test.ACL ./test/test.SELINUX ./test/error.log
+
+depend:
+	$(CPP) $(CFLAGS) -M $(SOURCES) > .depend
+
+# pretest create the file ./test/test.ACL with
+# 0 or 1 according to the WITH_ACL=yes presence.
+# The file will be used by ./test/test to decide
+# if to do the ACL tests or not.
+pretest:
+	echo "$(TEST_ACL)" > ./test/test.ACL ;
+	echo "$(TEST_SELINUX)" > ./test/test.SELINUX ;
+
+.PHONY : test
+test: $(TARGET)
+	(cd test; ./test)
+
+install:
+	[ -d $(PREFIX)$(BINDIR) ] || mkdir -p $(PREFIX)$(BINDIR)
+	[ -d $(PREFIX)$(MANDIR) ] || mkdir -p $(PREFIX)$(MANDIR)
+	[ -d $(PREFIX)$(MANDIR)/man8 ] || mkdir -p $(PREFIX)$(MANDIR)/man8
+	[ -d $(PREFIX)$(MANDIR)/man5 ] || mkdir -p $(PREFIX)$(MANDIR)/man5
+
+	if [ "$(OS_NAME)" = HP-UX ]; then \
+	$(INSTALL) $(PROG) $(PREFIX)$(BINDIR) 0755 bin bin; \
+	$(INSTALL) $(MAN) $(PREFIX)$(MANDIR)/man`echo $(MAN) | sed "s/.*\.//"` 0644 bin bin; \
+	$(INSTALL) $(MAN5) $(PREFIX)$(MANDIR)/man`echo $(MAN5) | sed "s/.*\.//"` 0644 bin bin; \
+	else if [ "$(OS_NAME)" = FreeBSD ]; then \
+	$(BSD_INSTALL_PROGRAM) $(PROG) $(BINDIR); \
+	$(BSD_INSTALL_MAN) $(MAN) $(MANDIR)/man`echo $(MAN) | sed "s/.*\.//"`/$(MAN); \
+	$(BSD_INSTALL_MAN) $(MAN5) $(MANDIR)/man`echo $(MAN5) | sed "s/.*\.//"`/$(MAN5); \
+	else \
+	$(INSTALL) -m 755 $(PROG) $(PREFIX)$(BINDIR); \
+	$(INSTALL) -m 644 $(MAN) $(PREFIX)$(MANDIR)/man`echo $(MAN) | sed "s/.*\.//"`/$(MAN); \
+	$(INSTALL) -m 644 $(MAN5) $(PREFIX)$(MANDIR)/man`echo $(MAN5) | sed "s/.*\.//"`/$(MAN5); \
+	fi; fi
+
+co:
+	co RCS/*,v
+	(cd examples; co RCS/*,v)
+
+svntag:
+	svn copy $(SVNURL)/trunk $(SVNURL)/tags/$(SVNTAG) -m "Release $(VERSION)"
+
+create-archive:
+	@rm -rf /tmp/logrotate-$(VERSION) /tmp/logrotate
+	@cd /tmp; svn export $(SVNPUBURL)/tags/$(SVNTAG) logrotate-$(VERSION)
+	@cd /tmp/logrotate-$(VERSION)
+	@cd /tmp; tar czSpf logrotate-$(VERSION).tar.gz logrotate-$(VERSION)
+	@rm -rf /tmp/logrotate-$(VERSION)
+	@cp /tmp/logrotate-$(VERSION).tar.gz .
+	@rm -f /tmp/logrotate-$(VERSION).tar.gz
+	@echo " "
+	@echo "The final archive is ./logrotate-$(VERSION).tar.gz."
+
+archive: clean svntag create-archive
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/logrotate/README.HPUX b/logrotate/README.HPUX
new file mode 100644
index 0000000..ad912a7
--- /dev/null
+++ b/logrotate/README.HPUX
@@ -0,0 +1,43 @@
+How to build and install logrotate on HP-UX 11.00 (these instructions should
+also work on HP-UX 10.20):
+ 
+1.  Obtain and install the following GNU packages for HP-UX:
+        binutils 2.9.1
+        gcc 2.95.2
+        make 3.78.1
+    I used the packages at the Software Porting and Archive Centre for HP-UX
+    at http://hpux.cs.utah.edu/.
+
+    Obtain and install the following GNU/Linux to HP-UX Porting package:
+	libhplx library
+    See http://devresource.hp.com/LPK/index.html for downloads.
+    This library is needed to provide the ??? function.
+ 
+2.  Obtain, build, and install popt 1.4 (there doesn't seem to be a build at
+    the Porting Centre.)
+    See ftp://ftp.rpm.org/pub/rpm/dist/rpm-4.0.x/popt-1.6.4.tar.gz
+    Install it into the directory of your choice (i.e. 
+    "./configure --prefix=/opt/popt").
+ 
+3.  Build logrotate, telling it where to find popt and hplx installation. The
+    POPT_DIR defaults to /usr/local and HPLX_DIR defaults to /usr/local/hplx:
+        gmake POPT_DIR=/usr/local HPLX_DIR=/usr/local/hplx
+ 
+4.  Install logrotate into your desired directory (BASEDIR defaults to
+    /usr/local):
+        gmake install BASEDIR=/usr/local
+ 
+5.  Copy the configuration files into your desired location.
+        cp examples/logrotate-default /etc/logrotate.conf
+	mkdir /etc/logrotate.d
+
+6.  Set up a cron job to run logrotate daily. See examples/logrotate.cron.
+ 
+7.  I also recommend setting CLEAN_ADM=0 in /etc/rc.config.d/clean, and
+    setting up an init script to use logrotate for this instead.  This way,
+    logrotate manages all logfile pruning.
+ 
+ 
+Questions, comments, abuse to:
+    Paul D. Gear <citecpdg@citec.qld.gov.au>, <paulgear@bigfoot.com>
+    Danial M. Howard <howadani@isu.edu>, <dmhoward@byu.edu>
diff --git a/logrotate/README.Solaris b/logrotate/README.Solaris
new file mode 100644
index 0000000..f2d0a3d
--- /dev/null
+++ b/logrotate/README.Solaris
@@ -0,0 +1,19 @@
+Steps to build and install logrotate on Solaris 2.6 
+ 
+1.  Obtain and install the following GNU packages from http://Sunfreeware.com:
+        gcc 2.95.2
+        make 3.80
+	popt-1.6.3
+
+2.  Build and install logrotate:
+	gmake
+	gmake install
+ 
+OBS.: If you want to use the test script on Solaris 2.6, you'll need to have
+      bash installed, adjust the path after the sha-bang (#!) properly and
+      substitute the sintax $(...) for backticks `...` in all
+      "test-config.?.in" files.
+
+-- 
+Fidelis Assis <fidelis@embratel.net.br>
+
diff --git a/logrotate/README.md b/logrotate/README.md
new file mode 100644
index 0000000..02ea87f
--- /dev/null
+++ b/logrotate/README.md
@@ -0,0 +1,22 @@
+# logrotate
+
+The logrotate utility is designed to simplify the administration of log files on a system which generates a lot of log files. Logrotate allows for the automatic rotation compression, removal and mailing of log files. Logrotate can be set to handle a log file daily, weekly, monthly or when the log file gets to a certain size.
+
+## Download
+
+The latest release is:
+
+* [logrotate-3.10.0](https://github.com/logrotate/logrotate/releases/download/3.10.0/logrotate-3.10.0.tar.gz) ([Changelog](https://github.com/logrotate/logrotate/commit/028f1cb4))
+
+Previous releases:
+
+* [logrotate-3.9.2](https://github.com/logrotate/logrotate/releases/download/3.9.2/logrotate-3.9.2.tar.gz) ([Changelog](https://github.com/logrotate/logrotate/releases/tag/3.9.2))
+* [logrotate-3.9.1](https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.9.1.tar.gz)
+* [logrotate-3.9.0](https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.9.0.tar.gz)
+* [logrotate-3.8.9](https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.8.9.tar.gz)
+* [logrotate-3.8.8](https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.8.8.tar.gz)
+* [logrotate-3.8.7](https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.8.7.tar.gz)
+
+# Patches and Questions
+
+Open issues or pull requests on GitHub.
diff --git a/logrotate/autogen.sh b/logrotate/autogen.sh
new file mode 100755
index 0000000..280b871
--- /dev/null
+++ b/logrotate/autogen.sh
@@ -0,0 +1,1481 @@
+#!/bin/sh
+#                        a u t o g e n . s h
+#
+# Copyright (c) 2005-2007 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myrid of necessary steps.  The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors.  Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+#   To obtain help on usage:
+#     ./autogen.sh --help
+#
+#   To obtain verbose output:
+#     ./autogen.sh --verbose
+#
+#   To skip autoreconf and prepare manually:
+#     AUTORECONF=false ./autogen.sh
+#
+#   To verbosely try running with an older (unsupported) autoconf:
+#     AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author: Christopher Sean Morrison <morrison@brlcad.org>
+#
+######################################################################
+
+# set to minimum acceptible version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+    AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptible version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+    AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptible version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+    LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+    # extract copyright from header
+    __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+    if [ "x$__copyright" = "x" ] ; then
+	__copyright="`date +%Y`"
+    fi
+
+    # extract version from CVS Id string
+    __id="$Id: autogen.sh,v 14.97 2007/06/18 22:25:02 brlcad Exp $"
+    __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+    if [ "x$__version" = "x" ] ; then
+	__version=""
+    fi
+
+    echo "autogen.sh build preparation script by Christopher Sean Morrison"
+    echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+    echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+    echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [--version]"
+    echo "    --help     Help on $NAME_OF_AUTOGEN usage"
+    echo "    --verbose  Verbose progress output"
+    echo "    --quiet    Quiet suppressed progress output"
+    echo "    --version  Only perform GNU Build System version checks"
+    echo
+    echo "Description: This script will validate that minimum versions of the"
+    echo "GNU Build System tools are installed and then run autoreconf for you."
+    echo "Should autoreconf fail, manual preparation steps will be run"
+    echo "potentially accounting for several common preparation issues.  The"
+
+    echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+    echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+    echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+    echo "default automatic detection behavior."
+    echo
+
+    ident
+
+    return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+    if [ "x$1" = "x" ] ; then
+	echo "INTERNAL ERROR: version_error was not provided a version"
+	exit 1
+    fi
+    if [ "x$2" = "x" ] ; then
+	echo "INTERNAL ERROR: version_error was not provided an application name"
+	exit 1
+    fi
+    $ECHO
+    $ECHO "ERROR:  To prepare the ${PROJECT} build system from scratch,"
+    $ECHO "        at least version $1 of $2 must be installed."
+    $ECHO
+    $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+    $ECHO "run configure or make.  Either the GNU Autotools will need to be installed"
+    $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+    $ECHO "code on another system and then transferred to here. -- Cheers!"
+    $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+    if [ "x$1" = "x" ] ; then
+	echo "INTERNAL ERROR: version_check was not provided a minimum version"
+	exit 1
+    fi
+    _min="$1"
+    if [ "x$2" = "x" ] ; then
+	echo "INTERNAL ERROR: version check was not provided a comparison version"
+	exit 1
+    fi
+    _cur="$2"
+
+    # needed to handle versions like 1.10 and 1.4-p6
+    _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+    _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+    _min_major="`echo $_min | cut -d. -f1`"
+    _min_minor="`echo $_min | cut -d. -f2`"
+    _min_patch="`echo $_min | cut -d. -f3`"
+
+    _cur_major="`echo $_cur | cut -d. -f1`"
+    _cur_minor="`echo $_cur | cut -d. -f2`"
+    _cur_patch="`echo $_cur | cut -d. -f3`"
+
+    if [ "x$_min_major" = "x" ] ; then
+	_min_major=0
+    fi
+    if [ "x$_min_minor" = "x" ] ; then
+	_min_minor=0
+    fi
+    if [ "x$_min_patch" = "x" ] ; then
+	_min_patch=0
+    fi
+    if [ "x$_cur_minor" = "x" ] ; then
+	_cur_major=0
+    fi
+    if [ "x$_cur_minor" = "x" ] ; then
+	_cur_minor=0
+    fi
+    if [ "x$_cur_patch" = "x" ] ; then
+	_cur_patch=0
+    fi
+
+    $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+    if [ $_min_major -lt $_cur_major ] ; then
+	return 0
+    elif [ $_min_major -eq $_cur_major ] ; then
+	if [ $_min_minor -lt $_cur_minor ] ; then
+	    return 0
+	elif [ $_min_minor -eq $_cur_minor ] ; then
+	    if [ $_min_patch -lt $_cur_patch ] ; then
+		return 0
+	    elif [ $_min_patch -eq $_cur_patch ] ; then
+		return 0
+	    fi
+	fi
+    fi
+    return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+    _pwd="`pwd`"
+    if test -f "./configure.ac" ; then
+	echo "./configure.ac"
+    elif test -f "./configure.in" ; then
+	echo "./configure.in"
+    elif test -f "$_pwd/configure.ac" ; then
+	echo "$_pwd/configure.ac"
+    elif test -f "$_pwd/configure.in" ; then
+	echo "$_pwd/configure.in"
+    elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+	echo "$PATH_TO_AUTOGEN/configure.ac"
+    elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+	echo "$PATH_TO_AUTOGEN/configure.in"
+    fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+    HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+    QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+    VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+    VERSION_ONLY=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+    AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+    AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+    AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+    LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+    ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+    AUTOHEADER_OPTIONS=""
+fi
+for arg in $ARGS ; do
+    case "x$arg" in
+	x--help) HELP=yes ;;
+	x-[hH]) HELP=yes ;;
+	x--quiet) QUIET=yes ;;
+	x-[qQ]) QUIET=yes ;;
+	x--verbose) VERBOSE=yes ;;
+	x-[vV]) VERBOSE=yes ;;
+	x--version) VERSION_ONLY=yes ;;
+	*)
+	    echo "Unknown option: $arg"
+	    echo
+	    usage
+	    exit 1
+	    ;;
+    esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+    echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+    if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+	echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+    fi
+    exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+    echo "test" | $__cmd > /dev/null 2>&1
+    if [ $? != 0 ] ; then
+	echo "INTERNAL ERROR: '${__cmd}' command is required"
+	exit 2
+    fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+    echo "INTERNAL ERROR: grep command is required"
+    exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+    echo "INTERNAL ERROR: sed command is required"
+    exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+    *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+    *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+    *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+    *xhead*) HEAD_N="n " ;;
+    *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+    *xtail*) TAIL_N="n " ;;
+    *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+    if [ "x$VERBOSE" = "xyes" ] ; then
+	echo "Verbose output quelled by quiet option.  Further output disabled."
+    fi
+else
+    ECHO=echo
+    if [ "x$VERBOSE" = "xyes" ] ; then
+	echo "Verbose output enabled"
+	VERBOSE_ECHO=echo
+    fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+    RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+    HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+    usage
+    $ECHO "---"
+    $ECHO "Help was requested.  No preparation or configuration will be performed."
+    exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+    for sig in 1 2 13 15; do
+	trap - $sig
+    done
+}
+
+# do this cleanup whenever we exit.
+trap '
+    # start from the root
+    if test -d "$START_PATH" ; then
+	cd "$START_PATH"
+    fi
+
+    # restore/delete backup files
+    if test "x$PFC_INIT" = "x1" ; then
+	recursive_restore
+    fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+    trap '
+	$ECHO ""
+	$ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+	# start from the root
+	if test -d "$START_PATH" ; then
+	    cd "$START_PATH"
+	fi
+
+	# clean up on abnormal exit
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+
+	if test -f "acinclude.m4.$$.backup" ; then
+	    $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+	    chmod u+w acinclude.m4
+	    cat acinclude.m4.$$.backup > acinclude.m4
+
+	    $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+	    rm -f acinclude.m4.$$.backup
+        fi
+
+	{ (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+    CONFIGURE="`locate_configure_template`"
+    if [ ! "x$CONFIGURE" = "x" ] ; then
+	$VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+    fi
+else
+    $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+    if [ "x$VERSION_ONLY" = "xyes" ] ; then
+	CONFIGURE=/dev/null
+    else
+	$ECHO
+	$ECHO "A configure.ac or configure.in file could not be located implying"
+	$ECHO "that the GNU Build System is at least not used in this directory.  In"
+	$ECHO "any case, there is nothing to do here without one of those files."
+	$ECHO
+	$ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+	exit 1
+    fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+    PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if [ "x$PROJECT" = "xAC_INIT" ] ; then
+	# projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+	PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ 	]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    fi
+    if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+	PROJECT="project"
+    fi
+    if [ "x$PROJECT" = "x" ] ; then
+	PROJECT="project"
+    fi
+else
+    $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+    for AUTORECONF in autoreconf ; do
+	$VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+	$AUTORECONF --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    HAVE_AUTORECONF=yes
+	    break
+	fi
+    done
+else
+    HAVE_AUTORECONF=yes
+    $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+    for AUTOCONF in autoconf ; do
+	$VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+	$AUTOCONF --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    _acfound=yes
+	    break
+	fi
+    done
+else
+    _acfound=yes
+    $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+    $ECHO "ERROR:  Unable to locate GNU Autoconf."
+    _report_error=yes
+else
+    _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Autoconf version $_version"
+    version_check "$AUTOCONF_VERSION" "$_version"
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+    exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+    for AUTOMAKE in automake ; do
+	$VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+	$AUTOMAKE --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    _amfound=yes
+	    break
+	fi
+    done
+else
+    _amfound=yes
+    $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+    $ECHO
+    $ECHO "ERROR: Unable to locate GNU Automake."
+    _report_error=yes
+else
+    _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Automake version $_version"
+    version_check "$AUTOMAKE_VERSION" "$_version" 
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$AUTOMAKE_VERSION" "GNU Automake"
+    exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+    LIBTOOLIZE=libtoolize
+    $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+    $LIBTOOLIZE --version > /dev/null 2>&1
+    if [ ! $? = 0 ] ; then
+	HAVE_LIBTOOLIZE=no
+	$ECHO
+	if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+	    $ECHO "Warning:  libtoolize does not appear to be available."
+	else
+	    $ECHO "Warning:  libtoolize does not appear to be available.  This means that"
+	    $ECHO "the automatic build preparation via autoreconf will probably not work."
+	    $ECHO "Preparing the build by running each step individually, however, should"
+	    $ECHO "work and will be done automatically for you if autoreconf fails."
+	fi
+
+	# look for some alternates
+	for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+	    $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+	    _glibtoolize="`$tool --version > /dev/null 2>&1`"
+	    if [ $? = 0 ] ; then
+		$VERBOSE_ECHO "Found $tool --version"
+		_glti="`which $tool`"
+		if [ "x$_glti" = "x" ] ; then
+		    $VERBOSE_ECHO "Cannot find $tool with which"
+		    continue;
+		fi
+		if test ! -f "$_glti" ; then
+		    $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+		    continue;
+		fi
+		_gltidir="`dirname $_glti`"
+		if [ "x$_gltidir" = "x" ] ; then
+		    $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+		    continue;
+		fi
+		if test ! -d "$_gltidir" ; then
+		    $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+		    continue;
+		fi
+		HAVE_ALT_LIBTOOLIZE=yes
+		LIBTOOLIZE="$tool"
+		$ECHO
+		$ECHO "Fortunately, $tool was found which means that your system may simply"
+		$ECHO "have a non-standard or incomplete GNU Autotools install.  If you have"
+		$ECHO "sufficient system access, it may be possible to quell this warning by"
+		$ECHO "running:"
+		$ECHO
+		sudo -V > /dev/null 2>&1
+		if [ $? = 0 ] ; then
+		    $ECHO "   sudo ln -s $_glti $_gltidir/libtoolize"
+		    $ECHO
+		else
+		    $ECHO "   ln -s $_glti $_gltidir/libtoolize"
+		    $ECHO
+		    $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+		    $ECHO
+		fi
+		_ltfound=yes
+		break
+	    fi
+	done
+    else
+	_ltfound=yes
+    fi
+else
+    _ltfound=yes
+    $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+    $ECHO
+    $ECHO "ERROR: Unable to locate GNU Libtool."
+    _report_error=yes
+else
+    _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Libtool version $_version"
+    version_check "$LIBTOOL_VERSION" "$_version" 
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$LIBTOOL_VERSION" "GNU Libtool"
+    exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+    for ACLOCAL in aclocal ; do
+	$VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+	$ACLOCAL --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    break
+	fi
+    done
+else
+    $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+    for AUTOHEADER in autoheader ; do
+	$VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+	$AUTOHEADER --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    break
+	fi
+    done
+else
+    $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+    $ECHO
+    ident
+    $ECHO "---"
+    $ECHO "Version requested.  No preparation or configuration will be performed."
+    exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+    PFC_INIT=1
+
+    # protect COPYING & INSTALL from overwrite by automake.  the
+    # automake force option will (inappropriately) ignore the existing
+    # contents of a COPYING and/or INSTALL files (depending on the
+    # version) instead of just forcing *missing* files like it does
+    # for AUTHORS, NEWS, and README. this is broken but extremely
+    # prevalent behavior, so we protect against it by keeping a backup
+    # of the file that can later be restored.
+
+    if test -f COPYING ; then
+	if test -f COPYING.$$.protect_from_automake.backup ; then
+	    $VERBOSE_ECHO "Already backed up COPYING in `pwd`"
+	else
+	    $VERBOSE_ECHO "Backing up COPYING in `pwd`"
+	    $VERBOSE_ECHO "cp -p COPYING COPYING.$$.protect_from_automake.backup"
+	    cp -p COPYING COPYING.$$.protect_from_automake.backup
+	fi
+    fi
+    if test -f INSTALL ; then
+	if test -f INSTALL.$$.protect_from_automake.backup ; then
+	    $VERBOSE_ECHO "Already backed up INSTALL in `pwd`"
+	else
+	    $VERBOSE_ECHO "Backing up INSTALL in `pwd`"
+	    $VERBOSE_ECHO "cp -p INSTALL INSTALL.$$.protect_from_automake.backup"
+	    cp -p INSTALL INSTALL.$$.protect_from_automake.backup
+	fi
+    fi
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+    # for projects using recursive configure, run the build
+    # preparation steps for the subdirectories.  this function assumes
+    # START_PATH was set to pwd before recursion begins so that
+    # relative paths work.
+
+    # git 'r done, protect COPYING and INSTALL from being clobbered
+    protect_from_clobber
+
+    if test -d autom4te.cache ; then
+	$VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+    fi
+
+    # find configure template
+    _configure="`locate_configure_template`"
+    if [ "x$_configure" = "x" ] ; then
+	return
+    fi
+    # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+    # look for subdirs
+    # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    CHECK_DIRS=""
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+	fi
+    done
+
+    # process subdirs
+    if [ ! "x$CHECK_DIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively scanning the following directories:"
+	$VERBOSE_ECHO "  $CHECK_DIRS"
+	for dir in $CHECK_DIRS ; do
+	    $VERBOSE_ECHO "Protecting files from automake in $dir"
+	    cd "$START_PATH"
+	    eval "cd $dir"
+
+	    # recursively git 'r done
+	    recursive_protect
+	done
+    fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+    # The automake (and autoreconf by extension) -f/--force-missing
+    # option may overwrite COPYING and INSTALL even if they do exist.
+    # Here we restore the files if necessary.
+
+    spacer=no
+
+    # COPYING
+    if test -f COPYING.$$.protect_from_automake.backup ; then
+	if test -f COPYING ; then
+	    # compare entire content, restore if needed
+	    if test "x`cat COPYING`" != "x`cat COPYING.$$.protect_from_automake.backup`" ; then
+		if test "x$spacer" = "xno" ; then
+		    $VERBOSE_ECHO
+		    spacer=yes
+		fi
+		# restore the backup
+		$VERBOSE_ECHO "Restoring COPYING from backup (automake -f likely clobbered it)"
+		$VERBOSE_ECHO "rm -f COPYING"
+		rm -f COPYING
+		$VERBOSE_ECHO "mv COPYING.$$.protect_from_automake.backup COPYING"
+		mv COPYING.$$.protect_from_automake.backup COPYING
+	    fi # check contents
+	elif test -f COPYING.$$.protect_from_automake.backup ; then
+	    $VERBOSE_ECHO "mv COPYING.$$.protect_from_automake.backup COPYING"
+	    mv COPYING.$$.protect_from_automake.backup COPYING
+	fi # -f COPYING
+
+	# just in case
+	$VERBOSE_ECHO "rm -f COPYING.$$.protect_from_automake.backup"
+	rm -f COPYING.$$.protect_from_automake.backup
+    fi # -f COPYING.$$.protect_from_automake.backup
+
+    # INSTALL
+    if test -f INSTALL.$$.protect_from_automake.backup ; then
+	if test -f INSTALL ; then
+	    # compare entire content, restore if needed
+	    if test "x`cat INSTALL`" != "x`cat INSTALL.$$.protect_from_automake.backup`" ; then
+		if test "x$spacer" = "xno" ; then
+		    $VERBOSE_ECHO
+		    spacer=yes
+		fi
+		# restore the backup
+		$VERBOSE_ECHO "Restoring INSTALL from backup (automake -f likely clobbered it)"
+		$VERBOSE_ECHO "rm -f INSTALL"
+		rm -f INSTALL
+		$VERBOSE_ECHO "mv INSTALL.$$.protect_from_automake.backup INSTALL"
+		mv INSTALL.$$.protect_from_automake.backup INSTALL
+	    fi # check contents
+	elif test -f INSTALL.$$.protect_from_automake.backup ; then
+	    $VERBOSE_ECHO "mv INSTALL.$$.protect_from_automake.backup INSTALL"
+	    mv INSTALL.$$.protect_from_automake.backup INSTALL
+	fi # -f INSTALL
+
+	# just in case
+	$VERBOSE_ECHO "rm -f INSTALL.$$.protect_from_automake.backup"
+	rm -f INSTALL.$$.protect_from_automake.backup
+    fi # -f INSTALL.$$.protect_from_automake.backup
+
+    CONFIGURE="`locate_configure_template`"
+    if [ "x$CONFIGURE" = "x" ] ; then
+	return
+    fi
+
+    _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if test ! -d "$_aux_dir" ; then
+	_aux_dir=.
+    fi
+
+    for file in config.guess config.sub ltmain.sh ; do
+	if test -f "${_aux_dir}/${file}" ; then
+	    $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+	    rm -f "${_aux_dir}/${file}.backup"
+	fi
+    done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+    # restore COPYING and INSTALL from backup if they were clobbered
+    # for each directory recursively.
+
+    # git 'r undone
+    restore_clobbered
+
+    # find configure template
+    _configure="`locate_configure_template`"
+    if [ "x$_configure" = "x" ] ; then
+	return
+    fi
+
+    # look for subdirs
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    CHECK_DIRS=""
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+	fi
+    done
+
+    # process subdirs
+    if [ ! "x$CHECK_DIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively scanning the following directories:"
+	$VERBOSE_ECHO "  $CHECK_DIRS"
+	for dir in $CHECK_DIRS ; do
+	    $VERBOSE_ECHO "Checking files for automake damage in $dir"
+	    cd "$START_PATH"
+	    eval "cd $dir"
+
+	    # recursively git 'r undone
+	    recursive_restore
+	done
+    fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+    # this routine performs a variety of directory-specific
+    # initializations.  some are sanity checks, some are preventive,
+    # and some are necessary setup detection.
+    #
+    # this function sets:
+    #   CONFIGURE
+    #   SEARCH_DIRS
+    #   CONFIG_SUBDIRS
+
+    ##################################
+    # check for a configure template #
+    ##################################
+    CONFIGURE="`locate_configure_template`"
+    if [ "x$CONFIGURE" = "x" ] ; then
+	$ECHO
+	$ECHO "A configure.ac or configure.in file could not be located implying"
+	$ECHO "that the GNU Build System is at least not used in this directory.  In"
+	$ECHO "any case, there is nothing to do here without one of those files."
+	$ECHO
+	$ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+	exit 1
+    fi
+
+    #####################
+    # detect an aux dir #
+    #####################
+    _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if test ! -d "$_aux_dir" ; then
+	_aux_dir=.
+    else
+	$VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+    fi
+
+    ################################
+    # detect a recursive configure #
+    ################################
+    CONFIG_SUBDIRS=""
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+	    CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+	fi
+    done
+
+    ##################################################
+    # make sure certain generated files do not exist #
+    ##################################################
+    for file in config.guess config.sub ltmain.sh ; do
+	if test -f "${_aux_dir}/${file}" ; then
+	    $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+	    mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+	fi
+    done
+
+    ############################
+    # search alternate m4 dirs #
+    ############################
+    SEARCH_DIRS=""
+    for dir in m4 ; do
+	if [ -d $dir ] ; then
+	    $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+	    SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+	fi
+    done
+
+    ######################################
+    # remove any previous build products #
+    ######################################
+    if test -d autom4te.cache ; then
+	$VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+    fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+#     if test -f aclocal.m4 ; then
+# 	$VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# 	$VERBOSE_ECHO "rm -f aclocal.m4"
+# 	rm -f aclocal.m4
+#     fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful.  Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+    $ECHO
+    $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+    $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+    autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$autoreconf_output"
+
+    if [ ! $ret = 0 ] ; then
+	if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+	    if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+		$ECHO
+		$ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+		$ECHO "misconfiguration issue.  This problem is encountered on systems that"
+		$ECHO "have installed libtoolize under a different name without providing a"
+		$ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+		$ECHO
+		$ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+		export LIBTOOLIZE
+		RUN_RECURSIVE=no
+		export RUN_RECURSIVE
+		untrap_abnormal
+
+		$VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+		sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+		exit $?
+	    fi
+	fi
+
+	$ECHO "Warning: $AUTORECONF failed"
+
+	if test -f ltmain.sh ; then
+	    $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+	fi
+
+	$ECHO "Attempting to run the preparation steps individually"
+	reconfigure_manually=yes
+    fi
+else
+    reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+    # libtool is rather error-prone in comparison to the other
+    # autotools and this routine attempts to compensate for some
+    # common failures.  the output after a libtoolize failure is
+    # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+    # attempt to inject a project-provided libtool.m4 file.
+
+    _autoconf_output="$1"
+
+    if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+	# we already tried the libtool.m4, don't try again
+	return 1
+    fi
+
+    if test -f "$LIBTOOL_M4" ; then
+	found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+	if test ! "x$found_libtool" = "x" ; then
+	    if test -f acinclude.m4 ; then
+		rm -f acinclude.m4.$$.backup
+		$VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+		cat acinclude.m4 > acinclude.m4.$$.backup
+	    fi
+	    $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+	    chmod u+w acinclude.m4
+	    cat "$LIBTOOL_M4" >> acinclude.m4
+
+	    # don't keep doing this
+	    RUN_RECURSIVE=no
+	    export RUN_RECURSIVE
+	    untrap_abnormal
+
+	    $ECHO
+	    $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+	    $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+	    sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+	    exit $?
+	fi
+    fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+    ##################################################
+    # Manual preparation steps taken are as follows: #
+    #   aclocal [-I m4]                              #
+    #   libtoolize --automake -c -f                  #
+    #   aclocal [-I m4]                              #
+    #   autoconf -f                                  #
+    #   autoheader                                   #
+    #   automake -a -c -f                            #
+    ##################################################
+
+    ###########
+    # aclocal #
+    ###########
+    $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+    aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$aclocal_output"
+    if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+    ##############
+    # libtoolize #
+    ##############
+    need_libtoolize=no
+    for feature in AC_PROG_LIBTOOL LT_INIT ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    need_libtoolize=yes
+	    break
+	fi
+    done
+    if [ "x$need_libtoolize" = "xyes" ] ; then
+	if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+	    $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+	    libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+	    ret=$?
+	    $VERBOSE_ECHO "$libtoolize_output"
+	    
+	    if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+	else
+	    if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+		$VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+		libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+		ret=$?
+		$VERBOSE_ECHO "$libtoolize_output"
+		
+		if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+	    fi
+	fi
+
+	###########
+	# aclocal #
+	###########
+	# re-run again as instructed by libtoolize
+	$VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+	aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$aclocal_output"
+
+	# libtoolize might put ltmain.sh in the wrong place
+	if test -f ltmain.sh ; then
+	    if test ! -f "${_aux_dir}/ltmain.sh" ; then
+		$ECHO
+		$ECHO "Warning:  $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+		$ECHO
+		$ECHO "Fortunately, the problem can be worked around by simply copying the"
+		$ECHO "file to the appropriate location (${_aux_dir}/).  This has been done for you."
+		$ECHO
+		$VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+		cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+		$ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+	    fi
+	fi # ltmain.sh
+    fi # need_libtoolize
+
+    ############
+    # autoconf #
+    ############
+    $VERBOSE_ECHO
+    $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+    autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$autoconf_output"
+
+    if [ ! $ret = 0 ] ; then
+	# retry without the -f and check for usage of macros that are too new
+	ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+	ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+	ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+	macros_to_search=""
+	ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+	ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+	
+	if [ $ac_major -lt 2 ] ; then
+	    macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+	else
+	    if [ $ac_minor -lt 54 ] ; then
+		macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+	    elif [ $ac_minor -lt 55 ] ; then
+		macros_to_search="$ac2_59_macros $ac2_55_macros"
+	    elif [ $ac_minor -lt 59 ] ; then
+		macros_to_search="$ac2_59_macros"
+	    fi
+	fi
+
+	configure_ac_macros=__none__
+	for feature in $macros_to_search ; do
+	    $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	    found="`grep \"^$feature.*\" $CONFIGURE`"
+	    if [ ! "x$found" = "x" ] ; then
+		if [ "x$configure_ac_macros" = "x__none__" ] ; then
+		    configure_ac_macros="$feature"
+		else
+		    configure_ac_macros="$feature $configure_ac_macros"
+		fi
+	    fi
+	done
+	if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+	    $ECHO
+	    $ECHO "Warning:  Unsupported macros were found in $CONFIGURE"
+	    $ECHO
+	    $ECHO "The `echo $CONFIGURE | basename` file was scanned in order to determine if any"
+	    $ECHO "unsupported macros are used that exceed the minimum version"
+	    $ECHO "settings specified within this file.  As such, the following macros"
+	    $ECHO "should be removed from configure.ac or the version numbers in this"
+	    $ECHO "file should be increased:"
+	    $ECHO
+	    $ECHO "$configure_ac_macros"
+	    $ECHO
+	    $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+	fi
+
+	###################
+	# autoconf, retry #
+	###################
+	$VERBOSE_ECHO
+	$VERBOSE_ECHO "$AUTOCONF"
+	autoconf_output="`$AUTOCONF 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$autoconf_output"
+
+	if [ ! $ret = 0 ] ; then
+	    # test if libtool is busted
+	    libtool_failure "$autoconf_output"
+
+	    # let the user know what went wrong
+	    cat <<EOF
+$autoconf_output
+EOF
+	    $ECHO "ERROR: $AUTOCONF failed"
+	    exit 2
+	else
+	    # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
+	    $ECHO
+	    $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
+	    $ECHO "	AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
+	    $ECHO
+	    $ECHO "Removing those options should not be necessary and indicate some other"
+	    $ECHO "problem with the build system.  The build preparation is highly suspect"
+	    $ECHO "and may result in configuration or compilation errors.  Consider"
+	    if [ "x$VERBOSE_ECHO" = "x:" ] ; then
+		$ECHO "rerunning the build preparation with verbose output enabled."
+		$ECHO "	$AUTOGEN_SH --verbose"
+	    else
+		$ECHO "reviewing the minimum GNU Autotools version settings contained in"
+		$ECHO "this script along with the macros being used in your `echo $CONFIGURE | basename` file."
+	    fi
+	    $ECHO
+	    $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+	fi # autoconf ret = 0
+    fi # autoconf ret = 0
+
+    ##############
+    # autoheader #
+    ##############
+    need_autoheader=no
+    for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    need_autoheader=yes
+	    break
+	fi
+    done
+    if [ "x$need_autoheader" = "xyes" ] ; then
+	$VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
+	autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$autoheader_output"
+	if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
+    fi # need_autoheader
+
+    ############
+    # automake #
+    ############
+    need_automake=no
+    for feature in AM_INIT_AUTOMAKE ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    need_automake=yes
+	    break
+	fi
+    done
+
+    if [ "x$need_automake" = "xyes" ] ; then
+	$VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
+	automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$automake_output"
+	
+	if [ ! $ret = 0 ] ; then
+
+	    ###################
+	    # automake, retry #
+	    ###################
+	    $VERBOSE_ECHO
+	    $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
+	    # retry without the -f
+	    automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
+	    ret=$?
+	    $VERBOSE_ECHO "$automake_output"
+	    
+	    if [ ! $ret = 0 ] ; then
+	 	# test if libtool is busted
+		libtool_failure "$automake_output"
+
+		# let the user know what went wrong
+		cat <<EOF
+$automake_output
+EOF
+		$ECHO "ERROR: $AUTOMAKE failed"
+		exit 2
+	    fi # automake retry
+	fi # automake ret = 0
+    fi # need_automake
+} # end of manual_autogen
+
+
+#####################################
+# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
+#####################################
+recursive_manual_autogen ( ) {
+
+    # run the build preparation steps manually for this directory
+    manual_autogen
+
+    # for projects using recursive configure, run the build
+    # preparation steps for the subdirectories.
+    if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively configuring the following directories:"
+	$VERBOSE_ECHO "  $CONFIG_SUBDIRS"
+	for dir in $CONFIG_SUBDIRS ; do
+	    $VERBOSE_ECHO "Processing recursive configure in $dir"
+	    cd "$START_PATH"
+	    cd "$dir"
+
+	    # new directory, prepare
+	    initialize
+
+	    # run manual steps for the subdir and any others below
+	    recursive_manual_autogen
+	done
+    fi
+}
+
+
+################################
+# run manual preparation steps #
+################################
+if [ "x$reconfigure_manually" = "xyes" ] ; then
+    $ECHO
+    $ECHO $ECHO_N "Preparing build ... $ECHO_C"
+
+    recursive_manual_autogen
+fi
+
+
+#########################
+# restore and summarize #
+#########################
+cd "$START_PATH"
+
+# restore COPYING and INSTALL from backup if necessary
+recursive_restore
+
+# make sure we end up with a configure script
+config_ac="`locate_configure_template`"
+config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
+if [ "x$config" = "x" ] ; then
+    $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
+fi
+
+# summarize
+$ECHO "done"
+$ECHO
+if test "x$config" = "x" -o ! -f "$config" ; then
+    $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
+    $ECHO "does not seem to be a resulting configure file.  This is unexpected"
+    $ECHO "and likely the result of an error.  You should run $NAME_OF_AUTOGEN"
+    $ECHO "with the --verbose option to get more details on a potential"
+    $ECHO "misconfiguration."
+else
+    $ECHO "The $PROJECT build system is now prepared.  To build here, run:"
+    $ECHO "  $config"
+    $ECHO "  make"
+fi
+
+
+# Local Variables:
+# mode: sh
+# tab-width: 8
+# sh-basic-offset: 4
+# sh-indentation: 4
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=4 tabstop=8
diff --git a/logrotate/basenames.c b/logrotate/basenames.c
new file mode 100644
index 0000000..5073b85
--- /dev/null
+++ b/logrotate/basenames.c
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "basenames.h"
+
+/* Return NAME with any leading path stripped off.  */
+
+char *ourBaseName(char *name)
+{
+    char *base;
+
+    base = strrchr(name, '/');
+    return base ? base + 1 : name;
+}
+
+static void stripTrailingSlashes(char *path)
+{
+    char *last;
+
+    last = path + strlen(path) - 1;
+    while (last > path && *last == '/')
+	*last-- = '\0';
+}
+
+char *ourDirName(char *origname)
+{
+    char *slash;
+    char *name;
+
+    name = strdup(origname);
+
+    stripTrailingSlashes(name);
+
+    slash = strrchr(name, '/');
+
+    if (!slash) {
+	/* No slash, must be current directory */
+	free(name);
+	/* strdup used, as the return value will be free()ed at some point */
+	return strdup(".");
+    } else {
+	/* Remove any trailing slashes and final element. */
+	while (slash > name && *slash == '/')
+	    --slash;
+	slash[1] = '\0';
+	return name;
+    }
+}
diff --git a/logrotate/basenames.h b/logrotate/basenames.h
new file mode 100644
index 0000000..078b923
--- /dev/null
+++ b/logrotate/basenames.h
@@ -0,0 +1,9 @@
+#ifndef H_BASENAMES
+#define H_BASENAMES
+
+/* returns a pointer inside of name */
+char *ourBaseName(char *name);
+/* returns a malloc'd string which must be freed by the caller */
+char *ourDirName(char *origname);
+
+#endif
diff --git a/logrotate/config.c b/logrotate/config.c
new file mode 100644
index 0000000..188cb97
--- /dev/null
+++ b/logrotate/config.c
@@ -0,0 +1,1702 @@
+#include "queue.h"
+/* Alloca is defined in stdlib.h in NetBSD */
+#ifndef __NetBSD__
+#include <alloca.h>
+#endif
+#include <limits.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <grp.h>
+#include <popt.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <fnmatch.h>
+#include <sys/mman.h>
+
+#include "basenames.h"
+#include "log.h"
+#include "logrotate.h"
+
+#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+
+#define REALLOC_STEP    10
+
+#if defined(SunOS)
+#include <limits.h>
+#if !defined(isblank)
+#define isblank(c) 	( (c) == ' ' || (c) == '\t' ) ? 1 : 0
+#endif
+#endif
+
+#ifdef __hpux
+#include "asprintf.c"
+#endif
+
+#if !defined(HAVE_ASPRINTF) && !defined(_FORTIFY_SOURCE)
+#include <stdarg.h>
+
+int asprintf(char **string_ptr, const char *format, ...)
+{
+	va_list arg;
+	char *str;
+	int size;
+	int rv;
+
+	va_start(arg, format);
+	size = vsnprintf(NULL, 0, format, arg);
+	size++;
+	va_start(arg, format);
+	str = malloc(size);
+	if (str == NULL) {
+		va_end(arg);
+		/*
+		 * Strictly speaking, GNU asprintf doesn't do this,
+		 * but the caller isn't checking the return value.
+		 */
+		fprintf(stderr, "failed to allocate memory\\n");
+		exit(1);
+	}
+	rv = vsnprintf(str, size, format, arg);
+	va_end(arg);
+
+	*string_ptr = str;
+	return (rv);
+}
+
+#endif
+
+#if !defined(HAVE_STRNDUP)
+char *strndup(const char *s, size_t n)
+{
+       size_t nAvail;
+       char *p;
+
+       if(!s)
+               return NULL;
+
+       /* min() */
+       nAvail = strlen(s) + 1;
+       if ( (n + 1) < nAvail)
+               nAvail = n + 1;
+
+       p = malloc(nAvail);
+       if (!p)
+               return NULL;
+       memcpy(p, s, nAvail);
+       p[nAvail - 1] = 0;
+       return p;
+}
+#endif
+
+enum {
+	STATE_DEFAULT = 2,
+	STATE_SKIP_LINE = 4,
+	STATE_DEFINITION_END = 8,
+	STATE_SKIP_CONFIG = 16,
+	STATE_LOAD_SCRIPT = 32,
+	STATE_ERROR = 64,
+};
+
+static char *defTabooExts[] = { ".rpmsave", ".rpmorig", "~", ",v",
+    ".disabled", ".dpkg-old", ".dpkg-dist", ".dpkg-new", ".cfsaved",
+    ".ucf-old", ".ucf-dist", ".ucf-new",
+    ".rpmnew", ".swp", ".cfsaved", ".rhn-cfg-tmp-*"
+};
+static int defTabooCount = sizeof(defTabooExts) / sizeof(char *);
+
+/* I shouldn't use globals here :-( */
+static char **tabooExts = NULL;
+int tabooCount = 0;
+static int glob_errno = 0;
+
+static int readConfigFile(const char *configFile, struct logInfo *defConfig);
+static int globerr(const char *pathname, int theerr);
+
+static char *isolateLine(char **strt, char **buf, size_t length) {
+	char *endtag, *start, *tmp;
+	start = *strt;
+	endtag = start;
+	while (endtag - *buf < length && *endtag != '\n') {
+		endtag++;}
+	if (endtag - *buf > length)
+		return NULL;
+	tmp = endtag - 1;
+	while (isspace((unsigned char)*endtag))
+		endtag--;
+	char *key = strndup(start, endtag - start + 1);
+	*strt = tmp;
+	return key;
+}
+
+static char *isolateValue(const char *fileName, int lineNum, char *key,
+			char **startPtr, char **buf, size_t length)
+{
+    char *chptr = *startPtr;
+
+    while (chptr - *buf < length && isblank((unsigned char)*chptr))
+	chptr++;
+    if (chptr - *buf < length && *chptr == '=') {
+	chptr++;
+	while ( chptr - *buf < length && isblank((unsigned char)*chptr))
+	    chptr++;
+    }
+
+    if (chptr - *buf < length && *chptr == '\n') {
+		message(MESS_ERROR, "%s:%d argument expected after %s\n",
+			fileName, lineNum, key);
+		return NULL;
+    }
+
+	*startPtr = chptr;
+	return isolateLine(startPtr, buf, length);
+}
+
+static char *isolateWord(char **strt, char **buf, size_t length) {
+	char *endtag, *start;
+	start = *strt;
+	while (start - *buf < length && isblank((unsigned char)*start))
+		start++;
+	endtag = start;
+	while (endtag - *buf < length && isalpha((unsigned char)*endtag)) {
+		endtag++;}
+	if (endtag - *buf > length)
+		return NULL;
+	char *key = strndup(start, endtag - start);
+	*strt = endtag;
+	return key;
+}
+
+static char *readPath(const char *configFile, int lineNum, char *key,
+		      char **startPtr, char **buf, size_t length)
+{
+    char *chptr;
+    char *start = *startPtr;
+    char *path;
+
+    wchar_t pwc;
+    size_t len;
+
+    if ((start = isolateValue(configFile, lineNum, key, startPtr, buf, length)) != NULL) {
+
+	chptr = start;
+
+	while( (len = mbrtowc(&pwc, chptr, strlen(chptr), NULL)) != 0 && strlen(chptr) != 0) {
+		if( len == (size_t)(-1) || len == (size_t)(-2) || !iswprint(pwc) || iswblank(pwc) ) {
+		    message(MESS_ERROR, "%s:%d bad %s path %s\n",
+			    configFile, lineNum, key, start);
+		    return NULL;
+		}
+		chptr += len;
+	}
+
+/*
+	while (*chptr && isprint((unsigned char)*chptr) && *chptr != ' ')
+	    chptr++;
+	if (*chptr) {
+	    message(MESS_ERROR, "%s:%d bad %s path %s\n",
+		    configFile, lineNum, key, start);
+	    return NULL;
+	}
+*/
+
+	path = strdup(start);
+	free(start);
+
+	return path;
+    } else
+	return NULL;
+}
+
+static int readModeUidGid(const char *configFile, int lineNum, char *key,
+							const char *directive, mode_t *mode, uid_t *uid,
+							gid_t *gid) {
+	char u[200], g[200];
+	int m;
+	char tmp;
+	int rc;
+	struct group *group;
+	struct passwd *pw = NULL;
+
+	if (!strcmp("su", directive))
+	    /* do not read <mode> for the 'su' directive */
+	    rc = 0;
+	else
+	    rc = sscanf(key, "%o %199s %199s%c", &m, u, g, &tmp);
+
+	/* We support 'key <owner> <group> notation now */
+	if (rc == 0) {
+		rc = sscanf(key, "%199s %199s%c", u, g, &tmp);
+		/* Simulate that we have read mode and keep the default value. */
+		if (rc > 0) {
+			m = *mode;
+			rc += 1;
+		}
+	}
+
+	if (rc == 4) {
+		message(MESS_ERROR, "%s:%d extra arguments for "
+			"%s\n", configFile, lineNum, directive);
+		return -1;
+	}
+
+	if (rc > 0) {
+		*mode = m;
+	}
+
+	if (rc > 1) {
+		pw = getpwnam(u);
+		if (!pw) {
+			message(MESS_ERROR, "%s:%d unknown user '%s'\n",
+				configFile, lineNum, u);
+			return -1;
+		}
+		*uid = pw->pw_uid;
+		endpwent();
+	}
+	if (rc > 2) {
+		group = getgrnam(g);
+		if (!group) {
+			message(MESS_ERROR, "%s:%d unknown group '%s'\n",
+				configFile, lineNum, g);
+			return -1;
+		}
+		*gid = group->gr_gid;
+		endgrent();
+	}
+
+	return 0;
+}
+
+static char *readAddress(const char *configFile, int lineNum, char *key,
+			 char **startPtr, char **buf, size_t length)
+{
+    char *endtag, *chptr;
+    char *start = *startPtr;
+    char *address;
+	
+    if ((endtag = isolateValue(configFile, lineNum, key, startPtr, buf, length)) != NULL) {
+
+	chptr = endtag;
+	while (*chptr && isprint((unsigned char)*chptr) && *chptr != ' ') {
+	    chptr++;
+	}
+
+	if (*chptr) {
+	    message(MESS_ERROR, "%s:%d bad %s address %s\n",
+		    configFile, lineNum, key, start);
+	    return NULL;
+	}
+
+	address = strdup(endtag);
+	
+	free(endtag);
+
+	return address;
+    } else
+	return NULL;
+}
+
+static int do_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+	struct stat sb;
+
+	if (stat(path, &sb) != 0) {
+		if (mkdir(path, mode) != 0 && errno != EEXIST) {
+			message(MESS_ERROR, "error creating %s: %s\n",
+				path, strerror(errno));
+			return -1;
+		}
+		if (chown(path, uid, gid) != 0) {
+			message(MESS_ERROR, "error setting owner of %s to uid %d and gid %d: %s\n",
+				path, uid, gid, strerror(errno));
+			return -1;
+		}
+		if (chmod(path, mode) != 0) {
+			message(MESS_ERROR, "error setting permissions of %s to 0%o: %s\n",
+				path, mode, strerror(errno));
+			return -1;
+		}
+	}
+	else if (!S_ISDIR(sb.st_mode)) {
+		message(MESS_ERROR, "path %s already exists, but it is not a directory\n",
+			path);
+		errno = ENOTDIR;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mkpath(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+	char *pp;
+	char *sp;
+	int rv;
+	char *copypath = strdup(path);
+
+	rv = 0;
+	pp = copypath;
+	while (rv == 0 && (sp = strchr(pp, '/')) != 0) {
+		if (sp != pp) {
+			*sp = '\0';
+			rv = do_mkdir(copypath, mode, uid, gid);
+			*sp = '/';
+		}
+		pp = sp + 1;
+	}
+	if (rv == 0) {
+		rv = do_mkdir(path, mode, uid, gid);
+	}
+	free(copypath);
+	return rv;
+}
+
+static int checkFile(const char *fname)
+{
+	int i;
+	char *pattern;
+
+	/* Check if fname is '.' or '..'; if so, return false */
+	if (fname[0] == '.' && (!fname[1] || (fname[1] == '.' && !fname[2])))
+		return 0;
+
+	/* Check if fname is ending in a taboo-extension; if so, return false */
+	for (i = 0; i < tabooCount; i++) {
+		if (asprintf(&pattern, "*%s", tabooExts[i]) < 0) {
+			message(MESS_FATAL, "failed to allocate taboo pattern memory\n");
+		}
+		if (!fnmatch(pattern, fname, 0))
+		{
+			free(pattern);
+			message(MESS_DEBUG, "Ignoring %s, because of %s ending\n",
+					fname, tabooExts[i]);
+			return 0;
+		}
+	}
+	free(pattern);
+	/* All checks have been passed; return true */
+	return 1;
+}
+
+/* Used by qsort to sort filelist */
+static int compar(const void *p, const void *q)
+{
+    return strcoll(*((char **) p), *((char **) q));
+}
+
+/* Free memory blocks pointed to by pointers in a 2d array and the array itself */
+static void free_2d_array(char **array, int lines_count)
+{
+    int i;
+    for (i = 0; i < lines_count; ++i)
+	free(array[i]);
+    free(array);
+}
+
+static void copyLogInfo(struct logInfo *to, struct logInfo *from)
+{
+    memset(to, 0, sizeof(*to));
+    if (from->oldDir)
+	to->oldDir = strdup(from->oldDir);
+    to->criterium = from->criterium;
+    to->threshhold = from->threshhold;
+    to->minsize = from->minsize;
+    to->maxsize = from->maxsize;
+    to->rotateCount = from->rotateCount;
+    to->rotateMinAge = from->rotateMinAge;
+    to->rotateAge = from->rotateAge;
+    to->logStart = from->logStart;
+    if (from->pre)
+	to->pre = strdup(from->pre);
+    if (from->post)
+	to->post = strdup(from->post);
+    if (from->first)
+	to->first = strdup(from->first);
+    if (from->last)
+	to->last = strdup(from->last);
+    if (from->preremove)
+	to->preremove = strdup(from->preremove);
+    if (from->logAddress)
+	to->logAddress = strdup(from->logAddress);
+    if (from->extension)
+	to->extension = strdup(from->extension);
+    if (from->compress_prog)
+	to->compress_prog = strdup(from->compress_prog);
+    if (from->uncompress_prog)
+	to->uncompress_prog = strdup(from->uncompress_prog);
+    if (from->compress_ext)
+	to->compress_ext = strdup(from->compress_ext);
+    to->flags = from->flags;
+	to->shred_cycles = from->shred_cycles;
+    to->createMode = from->createMode;
+    to->createUid = from->createUid;
+    to->createGid = from->createGid;
+    to->suUid = from->suUid;
+    to->suGid = from->suGid;
+    to->olddirMode = from->olddirMode;
+    to->olddirUid = from->olddirUid;
+    to->olddirGid = from->olddirGid;
+    if (from->compress_options_count) {
+        poptDupArgv(from->compress_options_count, from->compress_options_list, 
+                    &to->compress_options_count,  &to->compress_options_list);
+    }
+	if (from->dateformat)
+		to->dateformat = strdup(from->dateformat);
+}
+
+static void freeLogInfo(struct logInfo *log)
+{
+	free(log->pattern);
+	free_2d_array(log->files, log->numFiles);
+	free(log->oldDir);
+	free(log->pre);
+	free(log->post);
+	free(log->first);
+	free(log->last);
+	free(log->preremove);
+	free(log->logAddress);
+	free(log->extension);
+	free(log->compress_prog);
+	free(log->uncompress_prog);
+	free(log->compress_ext);
+	free(log->compress_options_list);
+	free(log->dateformat);
+}
+
+static struct logInfo *newLogInfo(struct logInfo *template)
+{
+	struct logInfo *new;
+
+	if ((new = malloc(sizeof(*new))) == NULL)
+		return NULL;
+
+	copyLogInfo(new, template);
+	TAILQ_INSERT_TAIL(&logs, new, list);
+	numLogs++;
+
+	return new;
+}
+
+static void removeLogInfo(struct logInfo *log)
+{
+	if (log == NULL)
+		return;
+
+	freeLogInfo(log);
+	TAILQ_REMOVE(&logs, log, list);
+	numLogs--;
+}
+
+static void freeTailLogs(int num)
+{
+	message(MESS_DEBUG, "removing last %d log configs\n", num);
+
+	while (num--)
+		removeLogInfo(TAILQ_LAST(&logs, logInfoHead));
+
+}
+
+static int readConfigPath(const char *path, struct logInfo *defConfig)
+{
+    struct stat sb;
+    int here, oldnumlogs, result = 1;
+	struct logInfo defConfigBackup;
+
+    if (stat(path, &sb)) {
+	message(MESS_ERROR, "cannot stat %s: %s\n", path, strerror(errno));
+	return 1;
+    }
+
+    if (S_ISDIR(sb.st_mode)) {
+	char **namelist, **p;
+	struct dirent *dp;
+	int files_count, i;
+	DIR *dirp;
+
+	here = open(".", O_RDONLY);
+
+	if ((dirp = opendir(path)) == NULL) {
+	    message(MESS_ERROR, "cannot open directory %s: %s\n", path,
+		    strerror(errno));
+	    close(here);
+	    return 1;
+	}
+	files_count = 0;
+	namelist = NULL;
+	while ((dp = readdir(dirp)) != NULL) {
+	    if (checkFile(dp->d_name)) {
+		/* Realloc memory for namelist array if necessary */
+		if (files_count % REALLOC_STEP == 0) {
+		    p = (char **) realloc(namelist,
+					  (files_count +
+					   REALLOC_STEP) * sizeof(char *));
+		    if (p) {
+			namelist = p;
+			memset(namelist + files_count, '\0',
+			       REALLOC_STEP * sizeof(char *));
+		    } else {
+			free_2d_array(namelist, files_count);
+			closedir(dirp);
+			close(here);
+			message(MESS_ERROR, "cannot realloc: %s\n",
+				strerror(errno));
+			return 1;
+		    }
+		}
+		/* Alloc memory for file name */
+		if ((namelist[files_count] =
+		     (char *) malloc(strlen(dp->d_name) + 1))) {
+		    strcpy(namelist[files_count], dp->d_name);
+		    files_count++;
+		} else {
+		    free_2d_array(namelist, files_count);
+		    closedir(dirp);
+		    close(here);
+		    message(MESS_ERROR, "cannot realloc: %s\n",
+			    strerror(errno));
+		    return 1;
+		}
+	    }
+	}
+	closedir(dirp);
+
+	if (files_count > 0) {
+	    qsort(namelist, files_count, sizeof(char *), compar);
+	} else {
+	    close(here);
+	    return 0;
+	}
+
+	if (chdir(path)) {
+	    message(MESS_ERROR, "error in chdir(\"%s\"): %s\n", path,
+		    strerror(errno));
+	    close(here);
+	    free_2d_array(namelist, files_count);
+	    return 1;
+	}
+
+	for (i = 0; i < files_count; ++i) {
+	    assert(namelist[i] != NULL);
+	    oldnumlogs = numLogs;
+	    copyLogInfo(&defConfigBackup, defConfig);
+	    if (readConfigFile(namelist[i], defConfig)) {
+		message(MESS_ERROR, "found error in file %s, skipping\n", namelist[i]);
+		freeTailLogs(numLogs - oldnumlogs);
+		freeLogInfo(defConfig);
+		copyLogInfo(defConfig, &defConfigBackup);
+		freeLogInfo(&defConfigBackup);
+		continue;
+	    } else {
+		result = 0;
+	    }
+	    freeLogInfo(&defConfigBackup);
+	}
+
+	if (fchdir(here) < 0) {
+		message(MESS_ERROR, "could not change directory to '.'");
+	}
+	close(here);
+	free_2d_array(namelist, files_count);
+    } else {
+    	oldnumlogs = numLogs;
+	copyLogInfo(&defConfigBackup, defConfig);
+	if (readConfigFile(path, defConfig)) {
+	    freeTailLogs(numLogs - oldnumlogs);
+	    freeLogInfo(defConfig);
+	    copyLogInfo(defConfig, &defConfigBackup);
+	} else {
+	    result = 0;
+	}
+	freeLogInfo(&defConfigBackup);
+    }
+
+    return result;
+}
+
+int readAllConfigPaths(const char **paths)
+{
+    int i, result = 0;
+    const char **file;
+    struct logInfo defConfig = {
+		.pattern = NULL,
+		.files = NULL,
+		.numFiles = 0,
+		.oldDir = NULL,
+		.criterium = ROT_SIZE,
+		.threshhold = 1024 * 1024,
+		.minsize = 0,
+		.maxsize = 0,
+		.rotateCount = 0,
+		.rotateMinAge = 0,
+		.rotateAge = 0,
+		.logStart = -1,
+		.pre = NULL,
+		.post = NULL,
+		.first = NULL,
+		.last = NULL,
+		.preremove = NULL,
+		.logAddress = NULL,
+		.extension = NULL,
+		.addextension = NULL,
+		.compress_prog = NULL,
+		.uncompress_prog = NULL,
+		.compress_ext = NULL,
+		.dateformat = NULL,
+		.flags = LOG_FLAG_IFEMPTY,
+		.shred_cycles = 0,
+		.createMode = NO_MODE,
+		.createUid = NO_UID,
+		.createGid = NO_GID,
+		.olddirMode = NO_MODE,
+		.olddirUid = NO_UID,
+		.olddirGid = NO_GID,
+		.suUid = NO_UID,
+		.suGid = NO_GID,
+		.compress_options_list = NULL,
+		.compress_options_count = 0
+    };
+
+    tabooExts = malloc(sizeof(*tabooExts) * defTabooCount);
+    for (i = 0; i < defTabooCount; i++) {
+	if ((tabooExts[i] = (char *) malloc(strlen(defTabooExts[i]) + 1))) {
+	    strcpy(tabooExts[i], defTabooExts[i]);
+	    tabooCount++;
+	} else {
+	    free_2d_array(tabooExts, tabooCount);
+	    message(MESS_ERROR, "cannot malloc: %s\n", strerror(errno));
+	    return 1;
+	}
+    }
+
+    for (file = paths; *file; file++) {
+	if (readConfigPath(*file, &defConfig)) {
+	    result = 1;
+	    break;
+	}
+    }
+    free_2d_array(tabooExts, tabooCount);
+    freeLogInfo(&defConfig);
+    return result;
+}
+
+static int globerr(const char *pathname, int theerr)
+{
+    /* A missing directory is not an error, so return 0 */
+    if (theerr == ENOTDIR)
+        return 0;
+
+    glob_errno = theerr;
+
+    /* We want the glob operation to abort on error, so return 1 */
+    return 1;
+}
+
+#define freeLogItem(what) \
+	do { \
+		free(newlog->what); \
+		newlog->what = NULL; \
+	} while (0);
+#define RAISE_ERROR() \
+	if (newlog != defConfig) { \
+		state = STATE_ERROR; \
+		continue; \
+	} else { \
+		goto error; \
+	}
+#define MAX_NESTING 16U
+
+static int readConfigFile(const char *configFile, struct logInfo *defConfig)
+{
+    int fd;
+    char *buf, *endtag, *key = NULL;
+    off_t length;
+    int lineNum = 1;
+    unsigned long long multiplier;
+    int i, k;
+    char *scriptStart = NULL;
+    char **scriptDest = NULL;
+    struct logInfo *newlog = defConfig;
+    char *start, *chptr;
+    char *dirName;
+    struct passwd *pw = NULL;
+    int rc;
+    struct stat sb, sb2;
+    glob_t globResult;
+    const char **argv;
+    int argc, argNum;
+	int flags;
+	int state = STATE_DEFAULT;
+    int logerror = 0;
+    struct logInfo *log;
+	static unsigned recursion_depth = 0U;
+	char *globerr_msg = NULL;
+	int in_config = 0;
+	int rv;
+	struct flock fd_lock = {
+		.l_start = 0,
+		.l_len = 0,
+		.l_whence = SEEK_SET,
+		.l_type = F_RDLCK
+	};
+
+    /* FIXME: createOwner and createGroup probably shouldn't be fixed
+       length arrays -- of course, if we aren't run setuid it doesn't
+       matter much */
+
+	fd = open(configFile, O_RDONLY);
+	if (fd < 0) {
+		message(MESS_ERROR, "failed to open config file %s: %s\n",
+			configFile, strerror(errno));
+		return 1;
+	}
+	if ((flags = fcntl(fd, F_GETFD)) == -1) {
+		message(MESS_ERROR, "Could not retrieve flags from file %s\n",
+				configFile);
+		close(fd);
+		return 1;
+	}
+	flags |= FD_CLOEXEC;
+	if (fcntl(fd, F_SETFD, flags) == -1) {
+		message(MESS_ERROR, "Could not set flags on file %s\n",
+				configFile);
+		close(fd);
+		return 1;
+	}
+	/* We don't want anybody to change the file while we parse it,
+	 * let's try to lock it for reading. */
+	if (fcntl(fd, F_SETLK, &fd_lock) == -1) {
+	message(MESS_ERROR, "Could not lock file %s for reading\n",
+			configFile);
+	}
+    if (fstat(fd, &sb)) {
+	message(MESS_ERROR, "fstat of %s failed: %s\n", configFile,
+		strerror(errno));
+	close(fd);
+	return 1;
+    }
+    if (!S_ISREG(sb.st_mode)) {
+	message(MESS_DEBUG,
+		"Ignoring %s because it's not a regular file.\n",
+		configFile);
+	close(fd);
+	return 0;
+    }
+    
+	if (!(pw = getpwuid(getuid()))) {
+		message(MESS_ERROR, "Logrotate UID is not in passwd file.\n");
+		close(fd);
+		return 1;
+	}
+
+	if (getuid() == ROOT_UID) {
+		if ((sb.st_mode & 07533) != 0400) {
+			message(MESS_ERROR,
+				"Ignoring %s because of bad file mode - must be 0644 or 0444.\n",
+				configFile);
+			close(fd);
+			return 0;
+		}
+
+		if ((pw = getpwuid(ROOT_UID)) == NULL) {
+			message(MESS_DEBUG,
+				"Ignoring %s because there's no password entry for the owner.\n",
+				configFile);
+			close(fd);
+			return 0;
+		}
+
+		if (sb.st_uid != ROOT_UID && (pw == NULL ||
+				sb.st_uid != pw->pw_uid ||
+				pw->pw_uid != ROOT_UID)) {
+			message(MESS_DEBUG,
+				"Ignoring %s because the file owner is wrong (should be root or user with uid 0).\n",
+				configFile);
+			close(fd);
+			return 0;
+		}
+	}
+
+	length = sb.st_size;
+
+	if (length > 0xffffff) {
+		message(MESS_ERROR, "file %s too large, probably not a config file.\n",
+				configFile);
+		close(fd);
+		return 1;
+	}   
+
+	/* We can't mmap empty file... */
+	if (length == 0) {
+		message(MESS_DEBUG,
+			"Ignoring %s because it's empty.\n",
+			configFile);
+		close(fd);
+		return 0;
+	}
+
+#ifdef MAP_POPULATE
+ 	buf = mmap(NULL, (size_t) length, PROT_READ,
+ 			MAP_PRIVATE | MAP_POPULATE, fd, (off_t) 0);
+#else /* MAP_POPULATE */
+	buf = mmap(NULL, (size_t) length, PROT_READ,
+			MAP_PRIVATE, fd, (off_t) 0);
+#endif /* MAP_POPULATE */
+
+	if (buf == MAP_FAILED) {
+		message(MESS_ERROR, "Error mapping config file %s: %s\n",
+				configFile, strerror(errno));
+		close(fd);
+		return 1;
+	}
+
+#ifdef HAVE_MADVISE
+#ifdef MADV_DONTFORK
+	madvise(buf, (size_t)(length + 2),
+			MADV_SEQUENTIAL | MADV_WILLNEED | MADV_DONTFORK);
+#else /* MADV_DONTFORK */
+	madvise(buf, (size_t)(length + 2),
+			MADV_SEQUENTIAL | MADV_WILLNEED);
+#endif /* MADV_DONTFORK */
+#endif /* HAVE_MADVISE */
+
+    message(MESS_DEBUG, "reading config file %s\n", configFile);
+
+	start = buf;
+    for (start = buf; start - buf < length; start++) {
+	if (key) {
+		free(key);
+		key = NULL;
+	}
+	switch (state) {
+		case STATE_DEFAULT:
+			if (isblank((unsigned char)*start))
+				continue;
+			/* Skip comment */
+			if (*start == '#') {
+				state = STATE_SKIP_LINE;
+				continue;
+			}
+			
+			if (isalpha((unsigned char)*start)) {
+				if ((key = isolateWord(&start, &buf, length)) == NULL)
+					continue;
+				if (!strcmp(key, "compress")) {
+					newlog->flags |= LOG_FLAG_COMPRESS;
+				} else if (!strcmp(key, "nocompress")) {
+					newlog->flags &= ~LOG_FLAG_COMPRESS;
+				} else if (!strcmp(key, "compress")) {
+					newlog->flags |= LOG_FLAG_COMPRESS;
+				} else if (!strcmp(key, "nocompress")) {
+					newlog->flags &= ~LOG_FLAG_COMPRESS;
+				} else if (!strcmp(key, "delaycompress")) {
+					newlog->flags |= LOG_FLAG_DELAYCOMPRESS;
+				} else if (!strcmp(key, "nodelaycompress")) {
+					newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;
+				} else if (!strcmp(key, "shred")) {
+					newlog->flags |= LOG_FLAG_SHRED;
+				} else if (!strcmp(key, "noshred")) { 
+					newlog->flags &= ~LOG_FLAG_SHRED;
+				} else if (!strcmp(key, "sharedscripts")) {
+					newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;
+				} else if (!strcmp(key, "nosharedscripts")) {
+					newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;
+				} else if (!strcmp(key, "copytruncate")) {
+					newlog->flags |= LOG_FLAG_COPYTRUNCATE;
+				} else if (!strcmp(key, "nocopytruncate")) {
+					newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;
+				} else if (!strcmp(key, "renamecopy")) {
+					newlog->flags |= LOG_FLAG_TMPFILENAME;
+				} else if (!strcmp(key, "norenamecopy")) {
+					newlog->flags &= ~LOG_FLAG_TMPFILENAME;
+				} else if (!strcmp(key, "copy")) {
+					newlog->flags |= LOG_FLAG_COPY;
+				} else if (!strcmp(key, "nocopy")) {
+					newlog->flags &= ~LOG_FLAG_COPY;
+				} else if (!strcmp(key, "ifempty")) {
+					newlog->flags |= LOG_FLAG_IFEMPTY;
+				} else if (!strcmp(key, "notifempty")) {
+					newlog->flags &= ~LOG_FLAG_IFEMPTY;
+				} else if (!strcmp(key, "dateext")) {
+					newlog->flags |= LOG_FLAG_DATEEXT;
+				} else if (!strcmp(key, "nodateext")) {
+					newlog->flags &= ~LOG_FLAG_DATEEXT;
+				} else if (!strcmp(key, "dateyesterday")) {
+					newlog->flags |= LOG_FLAG_DATEYESTERDAY;
+				} else if (!strcmp(key, "dateformat")) {
+					freeLogItem(dateformat);
+					newlog->dateformat = isolateLine(&start, &buf, length);
+					if (newlog->dateformat == NULL)
+						continue;
+				} else if (!strcmp(key, "noolddir")) {
+					newlog->oldDir = NULL;
+				} else if (!strcmp(key, "mailfirst")) {
+					newlog->flags |= LOG_FLAG_MAILFIRST;
+				} else if (!strcmp(key, "maillast")) {
+					newlog->flags &= ~LOG_FLAG_MAILFIRST;
+				} else if (!strcmp(key, "su")) {
+					mode_t tmp_mode = NO_MODE;
+					free(key);
+					key = isolateLine(&start, &buf, length);
+					if (key == NULL)
+						continue;
+
+					rv = readModeUidGid(configFile, lineNum, key, "su", 
+								   &tmp_mode, &newlog->suUid,
+								   &newlog->suGid);
+					if (rv == -1) {
+						RAISE_ERROR();
+					}
+					else if (tmp_mode != NO_MODE) {
+						message(MESS_ERROR, "%s:%d extra arguments for "
+								"su\n", configFile, lineNum);
+						RAISE_ERROR();
+					}
+
+					newlog->flags |= LOG_FLAG_SU;
+				} else if (!strcmp(key, "create")) {
+					free(key);
+					key = isolateLine(&start, &buf, length);
+					if (key == NULL)
+						continue;
+
+					rv = readModeUidGid(configFile, lineNum, key, "create",
+								   &newlog->createMode, &newlog->createUid,
+								   &newlog->createGid);
+					if (rv == -1) {
+						RAISE_ERROR();
+					}
+
+					newlog->flags |= LOG_FLAG_CREATE;
+				} else if (!strcmp(key, "createolddir")) {
+					free(key);
+					key = isolateLine(&start, &buf, length);
+					if (key == NULL)
+						continue;
+
+					rv = readModeUidGid(configFile, lineNum, key, "createolddir",
+								   &newlog->olddirMode, &newlog->olddirUid,
+								   &newlog->olddirGid);
+					if (rv == -1) {
+						RAISE_ERROR();
+					}
+
+					newlog->flags |= LOG_FLAG_OLDDIRCREATE;
+				} else if (!strcmp(key, "nocreateolddir")) {
+					newlog->flags &= ~LOG_FLAG_OLDDIRCREATE;
+				} else if (!strcmp(key, "nocreate")) {
+					newlog->flags &= ~LOG_FLAG_CREATE;
+				} else if (!strcmp(key, "size") || !strcmp(key, "minsize") ||
+							!strcmp(key, "maxsize")) {
+					unsigned long long size = 0;
+					char *opt = key;
+							
+					if ((key = isolateValue(configFile, lineNum, opt, &start,
+							&buf, length)) != NULL) {
+						int l = strlen(key) - 1;
+						if (key[l] == 'k' || key[l] == 'K') {
+							key[l] = '\0';
+							multiplier = 1024;
+						} else if (key[l] == 'M') {
+							key[l] = '\0';
+							multiplier = 1024 * 1024;
+						} else if (key[l] == 'G') {
+							key[l] = '\0';
+							multiplier = 1024 * 1024 * 1024;
+						} else if (!isdigit((unsigned char)key[l])) {
+							free(opt);
+							message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
+								configFile, lineNum, key[l]);
+							RAISE_ERROR();
+						} else {
+							multiplier = 1;
+						}
+
+						size = multiplier * strtoull(key, &chptr, 0);
+						if (*chptr) {
+							message(MESS_ERROR, "%s:%d bad size '%s'\n",
+								configFile, lineNum, key);
+							free(opt);
+							RAISE_ERROR();
+						}
+						if (!strncmp(opt, "size", 4)) {
+						  newlog->criterium = ROT_SIZE;
+						  newlog->threshhold = size;
+						} else if (!strncmp(opt, "maxsize", 7)) {
+						  newlog->maxsize = size;
+						} else {
+						  newlog->minsize = size;
+						}
+						free(opt);
+					}
+					else {
+						free(opt);
+						continue;
+					}
+				} else if (!strcmp(key, "shredcycles")) {
+					free(key);
+					if ((key = isolateValue(configFile, lineNum, "shred cycles", 
+							&start, &buf, length)) != NULL) {
+						newlog->shred_cycles = strtoul(key, &chptr, 0);
+						if (*chptr || newlog->shred_cycles < 0) {
+							message(MESS_ERROR, "%s:%d bad shred cycles '%s'\n",
+									configFile, lineNum, key);
+							goto error;
+						}
+					}
+					else continue;
+				} else if (!strcmp(key, "hourly")) {
+					newlog->criterium = ROT_HOURLY;
+				} else if (!strcmp(key, "daily")) {
+					newlog->criterium = ROT_DAYS;
+					newlog->threshhold = 1;
+				} else if (!strcmp(key, "monthly")) {
+					newlog->criterium = ROT_MONTHLY;
+				} else if (!strcmp(key, "weekly")) {
+					newlog->criterium = ROT_WEEKLY;
+				} else if (!strcmp(key, "yearly")) {
+					newlog->criterium = ROT_YEARLY;
+				} else if (!strcmp(key, "rotate")) {
+					free(key);
+					if ((key = isolateValue
+						(configFile, lineNum, "rotate count", &start,
+						&buf, length)) != NULL) {
+
+						newlog->rotateCount = strtoul(key, &chptr, 0);
+						if (*chptr || newlog->rotateCount < 0) {
+							message(MESS_ERROR,
+								"%s:%d bad rotation count '%s'\n",
+								configFile, lineNum, key);
+							RAISE_ERROR();
+						}
+					}
+					else continue;
+				} else if (!strcmp(key, "start")) {
+					free(key);
+					if ((key = isolateValue
+						(configFile, lineNum, "start count", &start,
+						&buf, length)) != NULL) {
+
+						newlog->logStart = strtoul(key, &chptr, 0);
+						if (*chptr || newlog->logStart < 0) {
+							message(MESS_ERROR, "%s:%d bad start count '%s'\n",
+								configFile, lineNum, key);
+							RAISE_ERROR();
+						}
+					}
+					else continue;
+				} else if (!strcmp(key, "minage")) {
+					free(key);
+					if ((key = isolateValue
+						(configFile, lineNum, "minage count", &start,
+						&buf, length)) != NULL) {
+						newlog->rotateMinAge = strtoul(key, &chptr, 0);
+						if (*chptr || newlog->rotateMinAge < 0) {
+							message(MESS_ERROR, "%s:%d bad minimum age '%s'\n",
+								configFile, lineNum, start);
+							RAISE_ERROR();
+						}
+					}
+					else continue; 
+				} else if (!strcmp(key, "maxage")) {
+					free(key);
+					if ((key = isolateValue
+						(configFile, lineNum, "maxage count", &start,
+						&buf, length)) != NULL) {
+						newlog->rotateAge = strtoul(key, &chptr, 0);
+						if (*chptr || newlog->rotateAge < 0) {
+							message(MESS_ERROR, "%s:%d bad maximum age '%s'\n",
+								configFile, lineNum, start);
+							RAISE_ERROR();
+						}
+					}
+					else continue;
+				} else if (!strcmp(key, "errors")) {
+					message(MESS_DEBUG,
+						"%s: %d: the errors directive is deprecated and no longer used.\n",
+						configFile, lineNum);
+				} else if (!strcmp(key, "mail")) {
+					freeLogItem(logAddress);
+					if (!(newlog->logAddress = readAddress(configFile, lineNum,
+										"mail", &start, &buf, length))) {
+						RAISE_ERROR();
+					}
+					else continue;
+				} else if (!strcmp(key, "nomail")) {
+					freeLogItem(logAddress);
+				} else if (!strcmp(key, "missingok")) {
+					newlog->flags |= LOG_FLAG_MISSINGOK;
+				} else if (!strcmp(key, "nomissingok")) {
+					newlog->flags &= ~LOG_FLAG_MISSINGOK;
+				} else if (!strcmp(key, "prerotate")) {
+					freeLogItem (pre);
+					scriptStart = start;
+					scriptDest = &newlog->pre;
+					state = STATE_LOAD_SCRIPT;
+				} else if (!strcmp(key, "firstaction")) {
+					freeLogItem (first);
+					scriptStart = start;
+					scriptDest = &newlog->first;
+					state = STATE_LOAD_SCRIPT;
+				} else if (!strcmp(key, "postrotate")) {
+					freeLogItem (post);
+					scriptStart = start;
+					scriptDest = &newlog->post;
+					state = STATE_LOAD_SCRIPT;
+				} else if (!strcmp(key, "lastaction")) {
+					freeLogItem (last);
+					scriptStart = start;
+					scriptDest = &newlog->last;
+					state = STATE_LOAD_SCRIPT;
+				} else if (!strcmp(key, "preremove")) {
+					freeLogItem (preremove);
+					scriptStart = start;
+					scriptDest = &newlog->preremove;
+					state = STATE_LOAD_SCRIPT;
+				} else if (!strcmp(key, "tabooext")) {
+					if (newlog != defConfig) {
+						message(MESS_ERROR,
+							"%s:%d tabooext may not appear inside "
+							"of log file definition\n", configFile,
+							lineNum);
+						state = STATE_ERROR;
+						continue;
+					}
+					free(key);
+					if ((key = isolateValue(configFile, lineNum, "tabooext", &start,
+							&buf, length)) != NULL) {
+						endtag = key;
+						if (*endtag == '+') {
+							endtag++;
+							while (isspace((unsigned char)*endtag) && *endtag)
+								endtag++;
+						} else {
+							free_2d_array(tabooExts, tabooCount);
+							tabooCount = 0;
+							tabooExts = malloc(1);
+						}
+
+
+						while (*endtag) {
+							chptr = endtag;
+							while (!isspace((unsigned char)*chptr) && *chptr != ',' && *chptr)
+								chptr++;
+
+							tabooExts = realloc(tabooExts, sizeof(*tabooExts) *
+										(tabooCount + 1));
+							tabooExts[tabooCount] = malloc(chptr - endtag + 1);
+							strncpy(tabooExts[tabooCount], endtag,
+								chptr - endtag);
+							tabooExts[tabooCount][chptr - endtag] = '\0';
+							tabooCount++;
+
+							endtag = chptr;
+							if (*endtag == ',')
+								endtag++;
+							while (*endtag && isspace((unsigned char)*endtag))
+								endtag++;
+						}
+					}
+					else continue;
+				} else if (!strcmp(key, "include")) {
+					free(key);
+					if ((key = isolateValue(configFile, lineNum, "include", &start,
+							&buf, length)) != NULL) {
+
+						message(MESS_DEBUG, "including %s\n", key);
+						if (++recursion_depth > MAX_NESTING) {
+							message(MESS_ERROR, "%s:%d include nesting too deep\n",
+									configFile, lineNum);
+							--recursion_depth;
+							goto error;
+						}
+						if (readConfigPath(key, newlog)) {
+							--recursion_depth;
+							goto error;
+						}
+						--recursion_depth;
+					}
+					else continue;
+				} else if (!strcmp(key, "olddir")) {
+					freeLogItem (oldDir);
+
+					if (!(newlog->oldDir = readPath(configFile, lineNum,
+									"olddir", &start, &buf, length))) {
+						RAISE_ERROR();
+					}
+					message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
+				} else if (!strcmp(key, "extension")) {
+					if ((key = isolateValue
+						(configFile, lineNum, "extension name", &start,
+							&buf, length)) != NULL) {
+						freeLogItem (extension);
+						newlog->extension = key;
+						key = NULL;
+					}
+					else continue;
+
+					message(MESS_DEBUG, "extension is now %s\n",
+						newlog->extension);
+
+				} else if (!strcmp(key, "addextension")) {
+					if ((key = isolateValue
+						(configFile, lineNum, "addextension name", &start,
+							&buf, length)) != NULL) {
+						freeLogItem (addextension);
+						newlog->addextension = key;
+						key = NULL;
+					}
+					else continue;
+
+					message(MESS_DEBUG, "addextension is now %s\n",
+						newlog->addextension);
+
+				} else if (!strcmp(key, "compresscmd")) {
+					freeLogItem (compress_prog);
+
+					if (!
+						(newlog->compress_prog =
+							readPath(configFile, lineNum, "compress", &start, &buf, length))) {
+						RAISE_ERROR();
+					}
+
+					if (access(newlog->compress_prog, X_OK)) {
+						message(MESS_ERROR,
+							"%s:%d compression program %s is not an executable file\n",
+							configFile, lineNum, newlog->compress_prog);
+						RAISE_ERROR();
+					}
+
+					message(MESS_DEBUG, "compress_prog is now %s\n",
+						newlog->compress_prog);
+
+				} else if (!strcmp(key, "uncompresscmd")) {
+					freeLogItem (uncompress_prog);
+
+					if (!
+						(newlog->uncompress_prog =
+							readPath(configFile, lineNum, "uncompress",
+								&start, &buf, length))) {
+						RAISE_ERROR();
+					}
+
+					if (access(newlog->uncompress_prog, X_OK)) {
+						message(MESS_ERROR,
+							"%s:%d uncompression program %s is not an executable file\n",
+							configFile, lineNum, newlog->uncompress_prog);
+						RAISE_ERROR();
+					}
+
+					message(MESS_DEBUG, "uncompress_prog is now %s\n",
+						newlog->uncompress_prog);
+
+				} else if (!strcmp(key, "compressoptions")) {
+					char *options;
+
+					if (newlog->compress_options_list) {
+						free(newlog->compress_options_list);
+						newlog->compress_options_list = NULL;
+						newlog->compress_options_count = 0;
+					}
+
+					if (!(options = isolateLine(&start, &buf, length))) {
+						RAISE_ERROR();
+					}
+
+					if (poptParseArgvString(options,
+								&newlog->compress_options_count,
+								&newlog->compress_options_list)) {
+						message(MESS_ERROR,
+							"%s:%d invalid compression options\n",
+							configFile, lineNum);
+						free(options);
+						RAISE_ERROR();
+					}
+
+					message(MESS_DEBUG, "compress_options is now %s\n",
+						options);
+					free(options);
+				} else if (!strcmp(key, "compressext")) {
+					freeLogItem (compress_ext);
+
+					if (!
+						(newlog->compress_ext =
+							readPath(configFile, lineNum, "compress-ext",
+								&start, &buf, length))) {
+						RAISE_ERROR();
+					}
+
+					message(MESS_DEBUG, "compress_ext is now %s\n",
+						newlog->compress_ext);
+				} else {
+					message(MESS_ERROR, "%s:%d unknown option '%s' "
+						"-- ignoring line\n", configFile, lineNum, key);
+					if (*start != '\n')
+						state = STATE_SKIP_LINE;
+				}
+				free(key);
+				key = NULL;
+			} else if (*start == '/' || *start == '"' || *start == '\''
+#ifdef GLOB_TILDE
+                                                                           || *start == '~'
+#endif
+                                                                           ) {
+				in_config = 0;
+				if (newlog != defConfig) {
+					message(MESS_ERROR, "%s:%d unexpected log filename\n",
+						configFile, lineNum);
+					state = STATE_ERROR;
+					continue;
+				}
+
+				/* If no compression options were found in config file, set
+				default values */
+				if (!newlog->compress_prog)
+					newlog->compress_prog = strdup(COMPRESS_COMMAND);
+				if (!newlog->uncompress_prog)
+					newlog->uncompress_prog = strdup(UNCOMPRESS_COMMAND);
+				if (!newlog->compress_ext)
+					newlog->compress_ext = strdup(COMPRESS_EXT);
+
+				/* Allocate a new logInfo structure and insert it into the logs
+				queue, copying the actual values from defConfig */
+				if ((newlog = newLogInfo(defConfig)) == NULL)
+					goto error;
+
+				endtag = start;
+				while (endtag - buf < length && *endtag != '{' && *endtag != '}' && *endtag != '\0') {
+					endtag++;}
+				if (endtag - buf > length)
+					continue;
+				if (*endtag == '}') {
+					message(MESS_ERROR, "%s:%d unexpected } (missing previous '{')\n", configFile,
+						lineNum);
+					goto error;
+				}
+				if (*endtag == '{') {
+					in_config = 1;
+				}
+				else {
+					message(MESS_ERROR, "%s:%d missing '{' after log files definition\n", configFile,
+						lineNum);
+					goto error;
+				}
+				char *key = strndup(start, endtag - start);
+				start = endtag;
+
+				if (poptParseArgvString(key, &argc, &argv)) {
+				message(MESS_ERROR, "%s:%d error parsing filename\n",
+					configFile, lineNum);
+				free(key);
+				goto error;
+				} else if (argc < 1) {
+				message(MESS_ERROR,
+					"%s:%d { expected after log file name(s)\n",
+					configFile, lineNum);
+				free(key);
+				goto error;
+				}
+
+				newlog->files = NULL;
+				newlog->numFiles = 0;
+				for (argNum = 0; argNum < argc && logerror != 1; argNum++) {
+				if (globerr_msg) {
+					free(globerr_msg);
+					globerr_msg = NULL;
+				}
+					
+				rc = glob(argv[argNum], GLOB_NOCHECK
+#ifdef GLOB_TILDE
+                                                        | GLOB_TILDE
+#endif 
+                                                    , globerr, &globResult);
+				if (rc == GLOB_ABORTED) {
+					if (newlog->flags & LOG_FLAG_MISSINGOK) {
+						continue;
+					}
+
+				/* We don't yet know whether this stanza has "missingok"
+					* set, so store the error message for later. */
+					rc = asprintf(&globerr_msg, "%s:%d glob failed for %s: %s\n",
+						configFile, lineNum, argv[argNum], strerror(glob_errno));
+					if (rc == -1)
+					globerr_msg = NULL;
+					
+					globResult.gl_pathc = 0;
+				}
+
+				newlog->files =
+					realloc(newlog->files,
+						sizeof(*newlog->files) * (newlog->numFiles +
+									globResult.
+									gl_pathc));
+
+				for (i = 0; i < globResult.gl_pathc; i++) {
+					/* if we glob directories we can get false matches */
+					if (!lstat(globResult.gl_pathv[i], &sb) &&
+					S_ISDIR(sb.st_mode)) {
+						continue;
+					}
+
+					for (log = logs.tqh_first; log != NULL;
+						log = log->list.tqe_next) {
+					for (k = 0; k < log->numFiles; k++) {
+						if (!strcmp(log->files[k],
+							globResult.gl_pathv[i])) {
+						message(MESS_ERROR,
+							"%s:%d duplicate log entry for %s\n",
+							configFile, lineNum,
+							globResult.gl_pathv[i]);
+						logerror = 1;
+						goto duperror;
+						}
+					}
+					}
+
+					newlog->files[newlog->numFiles] =
+					strdup(globResult.gl_pathv[i]);
+					newlog->numFiles++;
+				}
+		duperror:
+				globfree(&globResult);
+				}
+
+				newlog->pattern = key;
+
+				free(argv);
+
+			} else if (*start == '}') {
+				if (newlog == defConfig) {
+					message(MESS_ERROR, "%s:%d unexpected }\n", configFile,
+						lineNum);
+					goto error;
+				}
+				if (!in_config) {
+					message(MESS_ERROR, "%s:%d unexpected } (missing previous '{')\n", configFile,
+						lineNum);
+					goto error;
+				}
+				in_config = 0;
+			if (globerr_msg) {
+				if (!(newlog->flags & LOG_FLAG_MISSINGOK))
+					message(MESS_ERROR, "%s", globerr_msg);
+				free(globerr_msg);
+				globerr_msg = NULL;
+				if (!(newlog->flags & LOG_FLAG_MISSINGOK))
+					goto error;
+			}
+
+			if (newlog->oldDir) {
+				for (i = 0; i < newlog->numFiles; i++) {
+					char *ld;
+					int rv;
+					dirName = ourDirName(newlog->files[i]);
+					if (stat(dirName, &sb2)) {
+						if (!(newlog->flags & LOG_FLAG_MISSINGOK)) {
+							message(MESS_ERROR,
+								"%s:%d error verifying log file "
+								"path %s: %s\n", configFile, lineNum,
+								dirName, strerror(errno));
+							free(dirName);
+							goto error;
+						}
+						else {
+							message(MESS_DEBUG,
+								"%s:%d verifying log file "
+								"path failed %s: %s, log is probably missing, "
+								"but missingok is set, so this is not an error.\n",
+								configFile, lineNum,
+								dirName, strerror(errno));
+							free(dirName);
+							continue;
+						}
+					}
+					ld = alloca(strlen(dirName) + strlen(newlog->oldDir) + 2);
+					sprintf(ld, "%s/%s", dirName, newlog->oldDir);
+					free(dirName);
+
+					if (newlog->oldDir[0] != '/') {
+						dirName = ld;
+					}
+					else {
+						dirName = newlog->oldDir;
+					}
+
+					rv = stat(dirName, &sb);
+					if (rv) {
+						if (errno == ENOENT && newlog->flags & LOG_FLAG_OLDDIRCREATE) {
+							if (mkpath(dirName, newlog->olddirMode,
+								newlog->olddirUid, newlog->olddirGid)) {
+								goto error;
+							}
+						}
+						else {
+							message(MESS_ERROR, "%s:%d error verifying olddir "
+								"path %s: %s\n", configFile, lineNum,
+								dirName, strerror(errno));
+							goto error;
+						}
+					}
+
+					if (sb.st_dev != sb2.st_dev
+						&& !(newlog->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY | LOG_FLAG_TMPFILENAME))) {
+						message(MESS_ERROR,
+							"%s:%d olddir %s and log file %s "
+							"are on different devices\n", configFile,
+							lineNum, newlog->oldDir, newlog->files[i]);
+						goto error;
+					}
+				}
+			}
+
+				newlog = defConfig;
+				state = STATE_DEFINITION_END;
+			} else if (*start != '\n') {
+				message(MESS_ERROR, "%s:%d lines must begin with a keyword "
+					"or a filename (possibly in double quotes)\n",
+					configFile, lineNum);
+					state = STATE_SKIP_LINE;
+			}
+			break;
+		case STATE_SKIP_LINE:
+		case STATE_SKIP_LINE | STATE_SKIP_CONFIG:
+			if (*start == '\n')
+				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
+			break;
+		case STATE_SKIP_LINE | STATE_LOAD_SCRIPT:
+			if (*start == '\n')
+				state = STATE_LOAD_SCRIPT;
+			break;
+		case STATE_SKIP_LINE | STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
+			if (*start == '\n')
+				state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
+			break;
+		case STATE_DEFINITION_END:
+		case STATE_DEFINITION_END | STATE_SKIP_CONFIG:
+			if (isblank((unsigned char)*start))
+				continue;
+			if (*start != '\n') {
+				message(MESS_ERROR, "%s:%d, unexpected text after }\n",
+					configFile, lineNum);
+				state = STATE_SKIP_LINE | (state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
+			}
+			else
+				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
+			break;
+		case STATE_ERROR:
+			assert(newlog != defConfig);
+
+			message(MESS_ERROR, "found error in %s, skipping\n",
+				newlog->pattern ? newlog->pattern : "log config");
+
+			state = STATE_SKIP_CONFIG;
+			break;
+		case STATE_LOAD_SCRIPT:
+		case STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
+			if ((key = isolateWord(&start, &buf, length)) == NULL)
+				continue;
+
+			if (strcmp(key, "endscript") == 0) {
+				if (state & STATE_SKIP_CONFIG) {
+					state = STATE_SKIP_CONFIG;
+				}
+				else {
+					endtag = start - 9;
+					while (*endtag != '\n')
+					endtag--;
+					endtag++;
+					*scriptDest = malloc(endtag - scriptStart + 1);
+					strncpy(*scriptDest, scriptStart,
+						endtag - scriptStart);
+					(*scriptDest)[endtag - scriptStart] = '\0';
+
+					scriptDest = NULL;
+					scriptStart = NULL;
+				}
+				state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
+			}
+			else {
+				state = (*start == '\n' ? 0 : STATE_SKIP_LINE) |
+					STATE_LOAD_SCRIPT |
+					(state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
+			}
+			break;
+		case STATE_SKIP_CONFIG:
+			if (*start == '}') {
+				state = STATE_DEFAULT;
+				freeTailLogs(1);
+				newlog = defConfig;
+			}
+			else {
+				if ((key = isolateWord(&start, &buf, length)) == NULL)
+					continue;
+				if (
+					(strcmp(key, "postrotate") == 0) ||
+					(strcmp(key, "prerotate") == 0) ||
+					(strcmp(key, "firstaction") == 0) ||
+					(strcmp(key, "lastaction") == 0) ||
+					(strcmp(key, "preremove") == 0)
+					) {
+					state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
+				}
+				else {
+					/* isolateWord moves the "start" pointer.
+					 * If we have a line like
+					 *    rotate 5 
+					 * after isolateWord "start" points to "5" and it
+					 * is OK to skip the line, but if we have a line
+					 * like the following
+					 *    nocompress
+					 * after isolateWord "start" points to "\n". In
+					 * this case if we skip a line, we skip the next 
+					 * line, not the current "nocompress" one, 
+					 * because in the for cycle the "start"
+					 * pointer is increased by one and, after this, 
+					 * "start" points to the beginning of the next line.
+					*/
+					if (*start != '\n') {
+						state = STATE_SKIP_LINE | STATE_SKIP_CONFIG;
+					}
+				}
+				free(key);
+				key = NULL;
+			}
+			break;
+	}
+	if (key) {
+		free(key);
+		key = NULL;
+	}
+	if (*start == '\n') {
+	    lineNum++;
+	}
+
+    }
+ 
+    if (scriptStart) {
+	message(MESS_ERROR,
+		"%s:prerotate, postrotate or preremove without endscript\n",
+		configFile);
+	goto error;
+    }
+
+	munmap(buf, (size_t) length);
+	close(fd);
+    return 0;
+error:
+	if (key)
+		free(key);
+	munmap(buf, (size_t) length);
+	close(fd);
+    return 1;
+}
diff --git a/logrotate/config.h b/logrotate/config.h
new file mode 100644
index 0000000..eed1174
--- /dev/null
+++ b/logrotate/config.h
@@ -0,0 +1,51 @@
+/*
+ * OS-specific definitions
+ */
+
+#define ROOT_UID 0
+
+#ifdef __hpux
+#define DEFAULT_MAIL_COMMAND "/usr/bin/mailx"
+#define COMPRESS_COMMAND "/usr/contrib/bin/gzip"
+#define UNCOMPRESS_COMMAND "/usr/contrib/bin/gunzip"
+#define STATEFILE "/var/run/logrotate.status"
+#endif
+
+#ifdef SunOS
+#define DEFAULT_MAIL_COMMAND "/usr/bin/mailx"
+#define STATEFILE "/var/log/logrotate.status"
+#endif
+
+#ifdef __NetBSD__
+#define DEFAULT_MAIL_COMMAND "/usr/bin/mail -s"
+#define COMPRESS_COMMAND "/usr/bin/gzip"
+#define UNCOMPRESS_COMMAND "/usr/bin/gunzip"
+#define STATEFILE "/var/log/logrotate.status"
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define COMPRESS_COMMAND "/usr/bin/gzip"
+#define UNCOMPRESS_COMMAND "/usr/bin/gunzip"
+#endif
+
+/*
+ * Default settings for Linux - leave these last.
+ */
+#ifndef DEFAULT_MAIL_COMMAND
+#define DEFAULT_MAIL_COMMAND ""
+#endif
+#ifndef COMPRESS_COMMAND
+#define COMPRESS_COMMAND "/system/bin/gzip"
+#endif
+
+#ifndef COMPRESS_EXT
+#define COMPRESS_EXT ".gz"
+#endif
+
+#ifndef UNCOMPRESS_COMMAND
+#define UNCOMPRESS_COMMAND "/system/bin/gunzip"
+#endif
+
+#ifndef STATEFILE
+#define STATEFILE "/data/misc/logrotate/logrotate.status"
+#endif
diff --git a/logrotate/configure.ac b/logrotate/configure.ac
new file mode 100644
index 0000000..7de899e
--- /dev/null
+++ b/logrotate/configure.ac
@@ -0,0 +1,52 @@
+AC_INIT([logrotate],[3.10.0])
+
+dnl foreign: Do not require AUTHORS, ChangeLog, NEWS, and README to exist
+dnl serial-tests: Do not hide standard output of our sequential test-suite
+AM_INIT_AUTOMAKE([foreign serial-tests])
+
+AC_DEFINE(_GNU_SOURCE)
+
+AM_EXTRA_RECURSIVE_TARGETS([test])
+
+AC_PROG_CC
+AC_PROG_CC_STDC
+AC_STRUCT_ST_BLKSIZE
+AC_STRUCT_ST_BLOCKS
+
+dnl Use 64-bit file offsets on 32-bit systems (defines C macros if necessary)
+AC_SYS_LARGEFILE
+
+AC_CHECK_LIB([popt],[poptParseArgvString],,
+  AC_MSG_ERROR([libpopt required but not found]))
+
+dnl Needed for out-of-source builds
+mkdir -p test
+
+AC_ARG_WITH([selinux],
+  [AS_HELP_STRING([--with-selinux],
+    [support handling SELinux contexts (yes,no,check) @<:@default=check@:>@])],
+  [],
+  [with_selinux=check])
+AS_CASE(["$with_selinux"],
+  [yes], [AC_CHECK_LIB([selinux],[getfscreatecon_raw])],
+  [no], [],
+  [AC_CHECK_LIB([selinux],[getfscreatecon_raw])])
+AS_IF([test "$ac_cv_lib_selinux_getfscreatecon_raw" = yes],
+  echo "1" > ./test/test.SELINUX;, echo "0" > ./test/test.SELINUX;)
+
+AC_ARG_WITH([acl],
+  [AS_HELP_STRING([--with-acl],
+    [support handling ACL (yes,no,check) @<:@default=check@:>@])],
+  [],
+  [with_acl=check])
+AS_CASE(["$with_acl"],
+  [yes], [AC_CHECK_LIB([acl],[acl_get_file])],
+  [no], [],
+  [AC_CHECK_LIB([acl],[acl_get_file])])
+AS_IF([test "$ac_cv_lib_acl_acl_get_file" = yes],
+  echo "1" > ./test/test.ACL;, echo "0" > ./test/test.ACL;)
+
+AC_CHECK_FUNCS([asprintf fork madvise qsort strndup strptime vfork vsyslog])
+
+AC_CONFIG_FILES([Makefile test/Makefile logrotate.spec])
+AC_OUTPUT
diff --git a/logrotate/examples/logrotate-default b/logrotate/examples/logrotate-default
new file mode 100644
index 0000000..56e9103
--- /dev/null
+++ b/logrotate/examples/logrotate-default
@@ -0,0 +1,35 @@
+# see "man logrotate" for details
+# rotate log files weekly
+weekly
+
+# keep 4 weeks worth of backlogs
+rotate 4
+
+# create new (empty) log files after rotating old ones
+create
+
+# use date as a suffix of the rotated file
+dateext
+
+# uncomment this if you want your log files compressed
+#compress
+
+# RPM packages drop log rotation information into this directory
+include /etc/logrotate.d
+
+# no packages own wtmp and btmp -- we'll rotate them here
+/var/log/wtmp {
+    monthly
+    create 0664 root utmp
+    minsize 1M
+    rotate 1
+}
+
+/var/log/btmp {
+    missingok
+    monthly
+    create 0600 root utmp
+    rotate 1
+}
+
+# system-specific logs may be also be configured here.
diff --git a/logrotate/examples/logrotate.cron b/logrotate/examples/logrotate.cron
new file mode 100644
index 0000000..c6d50d4
--- /dev/null
+++ b/logrotate/examples/logrotate.cron
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+/usr/sbin/logrotate /etc/logrotate.conf
+EXITVALUE=$?
+if [ $EXITVALUE != 0 ]; then
+    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
+fi
+exit 0
diff --git a/logrotate/examples/logrotate.service b/logrotate/examples/logrotate.service
new file mode 100644
index 0000000..e276878
--- /dev/null
+++ b/logrotate/examples/logrotate.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Rotate log files
+Documentation=man:logrotate(8) man:logrotate.conf(5)
+ConditionACPower=true
+
+[Service]
+Type=oneshot
+ExecStart=/usr/sbin/logrotate /etc/logrotate.conf
+Nice=19
+IOSchedulingClass=best-effort
+IOSchedulingPriority=7
diff --git a/logrotate/examples/logrotate.timer b/logrotate/examples/logrotate.timer
new file mode 100644
index 0000000..8f405d5
--- /dev/null
+++ b/logrotate/examples/logrotate.timer
@@ -0,0 +1,11 @@
+[Unit]
+Description=Daily rotation of log files
+Documentation=man:logrotate(8) man:logrotate.conf(5)
+
+[Timer]
+OnCalendar=daily
+AccuracySec=12h
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/logrotate/glob.c b/logrotate/glob.c
new file mode 100644
index 0000000..1418aa7
--- /dev/null
+++ b/logrotate/glob.c
@@ -0,0 +1,905 @@
+/*
+ * Natanael Arndt, 2011: removed collate.h dependencies
+ *  (my changes are trivial)
+ *
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ *	Escaping convention: \ inhibits any special meaning the following
+ *	character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ *	Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ *	Same as GLOB_NOCHECK, but it will only append pattern if it did
+ *	not contain any magic characters.  [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ *	Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ *	expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ *	expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ *	Number of matches in the current invocation of glob.
+ */
+
+/*
+ * Some notes on multibyte character support:
+ * 1. Patterns with illegal byte sequences match nothing - even if
+ *    GLOB_NOCHECK is specified.
+ * 2. Illegal byte sequences in filenames are handled by treating them as
+ *    single-byte characters with a value of the first byte of the sequence
+ *    cast to wchar_t.
+ * 3. State-dependent encodings are not currently supported.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <glob.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#define	DOLLAR		'$'
+#define	DOT		'.'
+#define	EOS		'\0'
+#define	LBRACKET	'['
+#define	NOT		'!'
+#define	QUESTION	'?'
+#define	QUOTE		'\\'
+#define	RANGE		'-'
+#define	RBRACKET	']'
+#define	SEP		'/'
+#define	STAR		'*'
+#define	TILDE		'~'
+#define	UNDERSCORE	'_'
+#define	LBRACE		'{'
+#define	RBRACE		'}'
+#define	SLASH		'/'
+#define	COMMA		','
+
+#ifndef DEBUG
+
+#define	M_QUOTE		0x8000000000ULL
+#define	M_PROTECT	0x4000000000ULL
+#define	M_MASK		0xffffffffffULL
+#define	M_CHAR		0x00ffffffffULL
+
+typedef uint_fast64_t Char;
+
+#else
+
+#define	M_QUOTE		0x80
+#define	M_PROTECT	0x40
+#define	M_MASK		0xff
+#define	M_CHAR		0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define	CHAR(c)		((Char)((c)&M_CHAR))
+#define	META(c)		((Char)((c)|M_QUOTE))
+#define	M_ALL		META('*')
+#define	M_END		META(']')
+#define	M_NOT		META('!')
+#define	M_ONE		META('?')
+#define	M_RNG		META('-')
+#define	M_SET		META('[')
+#define	ismeta(c)	(((c)&M_QUOTE) != 0)
+
+
+static int	 compare(const void *, const void *);
+static int	 g_Ctoc(const Char *, char *, size_t);
+static int	 g_lstat(Char *, struct stat *, glob_t *);
+static DIR	*g_opendir(Char *, glob_t *);
+static const Char *g_strchr(const Char *, wchar_t);
+#ifdef notdef
+static Char	*g_strcat(Char *, const Char *);
+#endif
+static int	 g_stat(Char *, struct stat *, glob_t *);
+static int	 glob0(const Char *, glob_t *, size_t *);
+static int	 glob1(Char *, glob_t *, size_t *);
+static int	 glob2(Char *, Char *, Char *, Char *, glob_t *, size_t *);
+static int	 glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, size_t *);
+static int	 globextend(const Char *, glob_t *, size_t *);
+static const Char *	
+		 globtilde(const Char *, Char *, size_t, glob_t *);
+static int	 globexp1(const Char *, glob_t *, size_t *);
+static int	 globexp2(const Char *, const Char *, glob_t *, int *, size_t *);
+static int	 match(Char *, Char *, Char *);
+#ifdef DEBUG
+static void	 qprintf(const char *, Char *);
+#endif
+
+int
+glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob)
+{
+	const char *patnext;
+	size_t limit;
+	Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot;
+	mbstate_t mbs;
+	wchar_t wc;
+	size_t clen;
+
+	patnext = pattern;
+	if (!(flags & GLOB_APPEND)) {
+		pglob->gl_pathc = 0;
+		pglob->gl_pathv = NULL;
+		if (!(flags & GLOB_DOOFFS))
+			pglob->gl_offs = 0;
+	}
+	if (flags & GLOB_LIMIT) {
+		limit = pglob->gl_matchc;
+		if (limit == 0)
+			limit = _SC_ARG_MAX;
+	} else
+		limit = 0;
+	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+	pglob->gl_errfunc = errfunc;
+	pglob->gl_matchc = 0;
+
+	bufnext = patbuf;
+	bufend = bufnext + MAXPATHLEN - 1;
+	if (flags & GLOB_NOESCAPE) {
+		memset(&mbs, 0, sizeof(mbs));
+		while (bufend - bufnext >= MB_CUR_MAX) {
+			clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs);
+			if (clen == (size_t)-1 || clen == (size_t)-2)
+				return (GLOB_NOMATCH);
+			else if (clen == 0)
+				break;
+			*bufnext++ = wc;
+			patnext += clen;
+		}
+	} else {
+		/* Protect the quoted characters. */
+		memset(&mbs, 0, sizeof(mbs));
+		while (bufend - bufnext >= MB_CUR_MAX) {
+			if (*patnext == QUOTE) {
+				if (*++patnext == EOS) {
+					*bufnext++ = QUOTE | M_PROTECT;
+					continue;
+				}
+				prot = M_PROTECT;
+			} else
+				prot = 0;
+			clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs);
+			if (clen == (size_t)-1 || clen == (size_t)-2)
+				return (GLOB_NOMATCH);
+			else if (clen == 0)
+				break;
+			*bufnext++ = wc | prot;
+			patnext += clen;
+		}
+	}
+	*bufnext = EOS;
+
+	if (flags & GLOB_BRACE)
+	    return globexp1(patbuf, pglob, &limit);
+	else
+	    return glob0(patbuf, pglob, &limit);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int
+globexp1(const Char *pattern, glob_t *pglob, size_t *limit)
+{
+	const Char* ptr = pattern;
+	int rv;
+
+	/* Protect a single {}, for find(1), like csh */
+	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+		return glob0(pattern, pglob, limit);
+
+	while ((ptr = g_strchr(ptr, LBRACE)) != NULL)
+		if (!globexp2(ptr, pattern, pglob, &rv, limit))
+			return rv;
+
+	return glob0(pattern, pglob, limit);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int
+globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, size_t *limit)
+{
+	int     i;
+	Char   *lm, *ls;
+	const Char *pe, *pm, *pm1, *pl;
+	Char    patbuf[MAXPATHLEN];
+
+	/* copy part up to the brace */
+	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+		continue;
+	*lm = EOS;
+	ls = lm;
+
+	/* Find the balanced brace */
+	for (i = 0, pe = ++ptr; *pe; pe++)
+		if (*pe == LBRACKET) {
+			/* Ignore everything between [] */
+			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+				continue;
+			if (*pe == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pe = pm;
+			}
+		}
+		else if (*pe == LBRACE)
+			i++;
+		else if (*pe == RBRACE) {
+			if (i == 0)
+				break;
+			i--;
+		}
+
+	/* Non matching braces; just glob the pattern */
+	if (i != 0 || *pe == EOS) {
+		*rv = glob0(patbuf, pglob, limit);
+		return 0;
+	}
+
+	for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+		switch (*pm) {
+		case LBRACKET:
+			/* Ignore everything between [] */
+			for (pm1 = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+				continue;
+			if (*pm == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pm = pm1;
+			}
+			break;
+
+		case LBRACE:
+			i++;
+			break;
+
+		case RBRACE:
+			if (i) {
+			    i--;
+			    break;
+			}
+			/* FALLTHROUGH */
+		case COMMA:
+			if (i && *pm == COMMA)
+				break;
+			else {
+				/* Append the current string */
+				for (lm = ls; (pl < pm); *lm++ = *pl++)
+					continue;
+				/*
+				 * Append the rest of the pattern after the
+				 * closing brace
+				 */
+				for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+					continue;
+
+				/* Expand the current pattern */
+#ifdef DEBUG
+				qprintf("globexp2:", patbuf);
+#endif
+				*rv = globexp1(patbuf, pglob, limit);
+
+				/* move after the comma, to the next string */
+				pl = pm + 1;
+			}
+			break;
+
+		default:
+			break;
+		}
+	*rv = 0;
+	return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
+{
+	struct passwd *pwd;
+	char *h;
+	const Char *p;
+	Char *b, *eb;
+
+	if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+		return pattern;
+
+	/* 
+	 * Copy up to the end of the string or / 
+	 */
+	eb = &patbuf[patbuf_len - 1];
+	for (p = pattern + 1, h = (char *) patbuf;
+	    h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+		continue;
+
+	*h = EOS;
+
+	if (((char *) patbuf)[0] == EOS) {
+		/*
+		 * handle a plain ~ or ~/ by expanding $HOME first (iff
+		 * we're not running setuid or setgid) and then trying
+		 * the password file
+		 */
+		if ((h = getenv("HOME")) == NULL) {
+			if (((h = getlogin()) != NULL &&
+			     (pwd = getpwnam(h)) != NULL) ||
+			    (pwd = getpwuid(getuid())) != NULL)
+				h = pwd->pw_dir;
+			else
+				return pattern;
+		}
+	}
+	else {
+		/*
+		 * Expand a ~user
+		 */
+		if ((pwd = getpwnam((char*) patbuf)) == NULL)
+			return pattern;
+		else
+			h = pwd->pw_dir;
+	}
+
+	/* Copy the home directory */
+	for (b = patbuf; b < eb && *h; *b++ = *h++)
+		continue;
+
+	/* Append the rest of the pattern */
+	while (b < eb && (*b++ = *p++) != EOS)
+		continue;
+	*b = EOS;
+
+	return patbuf;
+}
+
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.
+ */
+static int
+glob0(const Char *pattern, glob_t *pglob, size_t *limit)
+{
+	const Char *qpatnext;
+	int err;
+	size_t oldpathc;
+	Char *bufnext, c, patbuf[MAXPATHLEN];
+
+	qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
+	oldpathc = pglob->gl_pathc;
+	bufnext = patbuf;
+
+	/* We don't need to check for buffer overflow any more. */
+	while ((c = *qpatnext++) != EOS) {
+		switch (c) {
+		case LBRACKET:
+			c = *qpatnext;
+			if (c == NOT)
+				++qpatnext;
+			if (*qpatnext == EOS ||
+			    g_strchr(qpatnext+1, RBRACKET) == NULL) {
+				*bufnext++ = LBRACKET;
+				if (c == NOT)
+					--qpatnext;
+				break;
+			}
+			*bufnext++ = M_SET;
+			if (c == NOT)
+				*bufnext++ = M_NOT;
+			c = *qpatnext++;
+			do {
+				*bufnext++ = CHAR(c);
+				if (*qpatnext == RANGE &&
+				    (c = qpatnext[1]) != RBRACKET) {
+					*bufnext++ = M_RNG;
+					*bufnext++ = CHAR(c);
+					qpatnext += 2;
+				}
+			} while ((c = *qpatnext++) != RBRACKET);
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_END;
+			break;
+		case QUESTION:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_ONE;
+			break;
+		case STAR:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			/* collapse adjacent stars to one,
+			 * to avoid exponential behavior
+			 */
+			if (bufnext == patbuf || bufnext[-1] != M_ALL)
+			    *bufnext++ = M_ALL;
+			break;
+		default:
+			*bufnext++ = CHAR(c);
+			break;
+		}
+	}
+	*bufnext = EOS;
+#ifdef DEBUG
+	qprintf("glob0:", patbuf);
+#endif
+
+	if ((err = glob1(patbuf, pglob, limit)) != 0)
+		return(err);
+
+	/*
+	 * If there was no match we are going to append the pattern
+	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+	 * and the pattern did not contain any magic characters
+	 * GLOB_NOMAGIC is there just for compatibility with csh.
+	 */
+	if (pglob->gl_pathc == oldpathc) {
+		if (((pglob->gl_flags & GLOB_NOCHECK) ||
+		    ((pglob->gl_flags & GLOB_NOMAGIC) &&
+			!(pglob->gl_flags & GLOB_MAGCHAR))))
+			return(globextend(pattern, pglob, limit));
+		else
+			return(GLOB_NOMATCH);
+	}
+	if (!(pglob->gl_flags & GLOB_NOSORT))
+		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+		    pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+	return(0);
+}
+
+static int
+compare(const void *p, const void *q)
+{
+	return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(Char *pattern, glob_t *pglob, size_t *limit)
+{
+	Char pathbuf[MAXPATHLEN];
+
+	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+	if (*pattern == EOS)
+		return(0);
+	return(glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1,
+	    pattern, pglob, limit));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern,
+      glob_t *pglob, size_t *limit)
+{
+	struct stat sb;
+	Char *p, *q;
+	int anymeta;
+
+	/*
+	 * Loop over pattern segments until end of pattern or until
+	 * segment with meta character found.
+	 */
+	for (anymeta = 0;;) {
+		if (*pattern == EOS) {		/* End of pattern? */
+			*pathend = EOS;
+			if (g_lstat(pathbuf, &sb, pglob))
+				return(0);
+
+			if (((pglob->gl_flags & GLOB_MARK) &&
+			    pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+			    || (S_ISLNK(sb.st_mode) &&
+			    (g_stat(pathbuf, &sb, pglob) == 0) &&
+			    S_ISDIR(sb.st_mode)))) {
+				if (pathend + 1 > pathend_last)
+					return (GLOB_ABORTED);
+				*pathend++ = SEP;
+				*pathend = EOS;
+			}
+			++pglob->gl_matchc;
+			return(globextend(pathbuf, pglob, limit));
+		}
+
+		/* Find end of next segment, copy tentatively to pathend. */
+		q = pathend;
+		p = pattern;
+		while (*p != EOS && *p != SEP) {
+			if (ismeta(*p))
+				anymeta = 1;
+			if (q + 1 > pathend_last)
+				return (GLOB_ABORTED);
+			*q++ = *p++;
+		}
+
+		if (!anymeta) {		/* No expansion, do next segment. */
+			pathend = q;
+			pattern = p;
+			while (*pattern == SEP) {
+				if (pathend + 1 > pathend_last)
+					return (GLOB_ABORTED);
+				*pathend++ = *pattern++;
+			}
+		} else			/* Need expansion, recurse. */
+			return(glob3(pathbuf, pathend, pathend_last, pattern, p,
+			    pglob, limit));
+	}
+	/* NOTREACHED */
+}
+
+static int
+glob3(Char *pathbuf, Char *pathend, Char *pathend_last,
+      Char *pattern, Char *restpattern,
+      glob_t *pglob, size_t *limit)
+{
+	struct dirent *dp;
+	DIR *dirp;
+	int err;
+	char buf[MAXPATHLEN];
+
+	/*
+	 * The readdirfunc declaration can't be prototyped, because it is
+	 * assigned, below, to two functions which are prototyped in glob.h
+	 * and dirent.h as taking pointers to differently typed opaque
+	 * structures.
+	 */
+	struct dirent *(*readdirfunc)();
+
+	if (pathend > pathend_last)
+		return (GLOB_ABORTED);
+	*pathend = EOS;
+	errno = 0;
+
+	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+		/* TODO: don't call for ENOENT or ENOTDIR? */
+		if (pglob->gl_errfunc) {
+			if (g_Ctoc(pathbuf, buf, sizeof(buf)))
+				return (GLOB_ABORTED);
+			if (pglob->gl_errfunc(buf, errno) ||
+			    pglob->gl_flags & GLOB_ERR)
+				return (GLOB_ABORTED);
+		}
+		return(0);
+	}
+
+	err = 0;
+
+	/* Search directory for matching names. */
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		readdirfunc = pglob->gl_readdir;
+	else
+		readdirfunc = readdir;
+	while ((dp = (*readdirfunc)(dirp))) {
+		char *sc;
+		Char *dc;
+		wchar_t wc;
+		size_t clen;
+		mbstate_t mbs;
+
+		/* Initial DOT must be matched literally. */
+		if (dp->d_name[0] == DOT && *pattern != DOT)
+			continue;
+		memset(&mbs, 0, sizeof(mbs));
+		dc = pathend;
+		sc = dp->d_name;
+		while (dc < pathend_last) {
+			clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs);
+			if (clen == (size_t)-1 || clen == (size_t)-2) {
+				wc = *sc;
+				clen = 1;
+				memset(&mbs, 0, sizeof(mbs));
+			}
+			if ((*dc++ = wc) == EOS)
+				break;
+			sc += clen;
+		}
+		if (!match(pathend, pattern, restpattern)) {
+			*pathend = EOS;
+			continue;
+		}
+		err = glob2(pathbuf, --dc, pathend_last, restpattern,
+		    pglob, limit);
+		if (err)
+			break;
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		(*pglob->gl_closedir)(dirp);
+	else
+		closedir(dirp);
+	return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(const Char *path, glob_t *pglob, size_t *limit)
+{
+	char **pathv;
+	size_t i, newsize, len;
+	char *copy;
+	const Char *p;
+
+	if (*limit && pglob->gl_pathc > *limit) {
+		errno = 0;
+		return (GLOB_NOSPACE);
+	}
+
+	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+	pathv = pglob->gl_pathv ?
+		    realloc((char *)pglob->gl_pathv, newsize) :
+		    malloc(newsize);
+	if (pathv == NULL) {
+		if (pglob->gl_pathv) {
+			free(pglob->gl_pathv);
+			pglob->gl_pathv = NULL;
+		}
+		return(GLOB_NOSPACE);
+	}
+
+	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+		/* first time around -- clear initial gl_offs items */
+		pathv += pglob->gl_offs;
+		for (i = pglob->gl_offs + 1; --i > 0; )
+			*--pathv = NULL;
+	}
+	pglob->gl_pathv = pathv;
+
+	for (p = path; *p++;)
+		continue;
+	len = MB_CUR_MAX * (size_t)(p - path);	/* XXX overallocation */
+	if ((copy = malloc(len)) != NULL) {
+		if (g_Ctoc(path, copy, len)) {
+			free(copy);
+			return (GLOB_NOSPACE);
+		}
+		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+	}
+	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+	return(copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(Char *name, Char *pat, Char *patend)
+{
+	int ok, negate_range;
+	Char c, k;
+
+	while (pat < patend) {
+		c = *pat++;
+		switch (c & M_MASK) {
+		case M_ALL:
+			if (pat == patend)
+				return(1);
+			do
+			    if (match(name, pat, patend))
+				    return(1);
+			while (*name++ != EOS);
+			return(0);
+		case M_ONE:
+			if (*name++ == EOS)
+				return(0);
+			break;
+		case M_SET:
+			ok = 0;
+			if ((k = *name++) == EOS)
+				return(0);
+			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+				++pat;
+			while (((c = *pat++) & M_MASK) != M_END)
+				if ((*pat & M_MASK) == M_RNG) {
+					if (CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1])) ok = 1;
+					pat += 2;
+				} else if (c == k)
+					ok = 1;
+			if (ok == negate_range)
+				return(0);
+			break;
+		default:
+			if (*name++ != c)
+				return(0);
+			break;
+		}
+	}
+	return(*name == EOS);
+}
+
+/* Free allocated data belonging to a glob_t structure. */
+void
+globfree(glob_t *pglob)
+{
+	size_t i;
+	char **pp;
+
+	if (pglob->gl_pathv != NULL) {
+		pp = pglob->gl_pathv + pglob->gl_offs;
+		for (i = pglob->gl_pathc; i--; ++pp)
+			if (*pp)
+				free(*pp);
+		free(pglob->gl_pathv);
+		pglob->gl_pathv = NULL;
+	}
+}
+
+static DIR *
+g_opendir(Char *str, glob_t *pglob)
+{
+	char buf[MAXPATHLEN];
+
+	if (!*str)
+		strcpy(buf, ".");
+	else {
+		if (g_Ctoc(str, buf, sizeof(buf)))
+			return (NULL);
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_opendir)(buf));
+
+	return(opendir(buf));
+}
+
+static int
+g_lstat(Char *fn, struct stat *sb, glob_t *pglob)
+{
+	char buf[MAXPATHLEN];
+
+	if (g_Ctoc(fn, buf, sizeof(buf))) {
+		errno = ENAMETOOLONG;
+		return (-1);
+	}
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_lstat)(buf, sb));
+	return(lstat(buf, sb));
+}
+
+static int
+g_stat(Char *fn, struct stat *sb, glob_t *pglob)
+{
+	char buf[MAXPATHLEN];
+
+	if (g_Ctoc(fn, buf, sizeof(buf))) {
+		errno = ENAMETOOLONG;
+		return (-1);
+	}
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_stat)(buf, sb));
+	return(stat(buf, sb));
+}
+
+static const Char *
+g_strchr(const Char *str, wchar_t ch)
+{
+
+	do {
+		if (*str == ch)
+			return (str);
+	} while (*str++);
+	return (NULL);
+}
+
+static int
+g_Ctoc(const Char *str, char *buf, size_t len)
+{
+	mbstate_t mbs;
+	size_t clen;
+
+	memset(&mbs, 0, sizeof(mbs));
+	while (len >= MB_CUR_MAX) {
+		clen = wcrtomb(buf, *str, &mbs);
+		if (clen == (size_t)-1)
+			return (1);
+		if (*str == L'\0')
+			return (0);
+		str++;
+		buf += clen;
+		len -= clen;
+	}
+	return (1);
+}
+
+#ifdef DEBUG
+static void
+qprintf(const char *str, Char *s)
+{
+	Char *p;
+
+	(void)printf("%s:\n", str);
+	for (p = s; *p; p++)
+		(void)printf("%c", CHAR(*p));
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", ismeta(*p) ? '_' : ' ');
+	(void)printf("\n");
+}
+#endif
diff --git a/logrotate/glob.h b/logrotate/glob.h
new file mode 100644
index 0000000..cf0c88e
--- /dev/null
+++ b/logrotate/glob.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)glob.h	8.1 (Berkeley) 6/2/93
+ * $FreeBSD$
+ */
+
+#ifndef _GLOB_H_
+#define	_GLOB_H_
+
+#include <sys/cdefs.h>
+
+#ifndef	_SIZE_T_DECLARED
+typedef	__size_t	size_t;
+#define	_SIZE_T_DECLARED
+#endif
+
+struct stat;
+typedef struct {
+	size_t gl_pathc;	/* Count of total paths so far. */
+	size_t gl_matchc;	/* Count of paths matching pattern. */
+	size_t gl_offs;		/* Reserved at beginning of gl_pathv. */
+	int gl_flags;		/* Copy of flags parameter to glob. */
+	char **gl_pathv;	/* List of paths matching pattern. */
+				/* Copy of errfunc parameter to glob. */
+	int (*gl_errfunc)(const char *, int);
+
+	/*
+	 * Alternate filesystem access methods for glob; replacement
+	 * versions of closedir(3), readdir(3), opendir(3), stat(2)
+	 * and lstat(2).
+	 */
+	void (*gl_closedir)(void *);
+	struct dirent *(*gl_readdir)(void *);
+	void *(*gl_opendir)(const char *);
+	int (*gl_lstat)(const char *, struct stat *);
+	int (*gl_stat)(const char *, struct stat *);
+} glob_t;
+
+/* Believed to have been introduced in 1003.2-1992 */
+#define	GLOB_APPEND	0x0001	/* Append to output from previous call. */
+#define	GLOB_DOOFFS	0x0002	/* Use gl_offs. */
+#define	GLOB_ERR	0x0004	/* Return on error. */
+#define	GLOB_MARK	0x0008	/* Append / to matching directories. */
+#define	GLOB_NOCHECK	0x0010	/* Return pattern itself if nothing matches. */
+#define	GLOB_NOSORT	0x0020	/* Don't sort. */
+#define	GLOB_NOESCAPE	0x2000	/* Disable backslash escaping. */
+
+/* Error values returned by glob(3) */
+#define	GLOB_NOSPACE	(-1)	/* Malloc call failed. */
+#define	GLOB_ABORTED	(-2)	/* Unignored error. */
+#define	GLOB_NOMATCH	(-3)	/* No match and GLOB_NOCHECK was not set. */
+#define	GLOB_NOSYS	(-4)	/* Obsolete: source comptability only. */
+
+#define	GLOB_ALTDIRFUNC	0x0040	/* Use alternately specified directory funcs. */
+#define	GLOB_BRACE	0x0080	/* Expand braces ala csh. */
+#define	GLOB_MAGCHAR	0x0100	/* Pattern had globbing characters. */
+#define	GLOB_NOMAGIC	0x0200	/* GLOB_NOCHECK without magic chars (csh). */
+#define	GLOB_QUOTE	0x0400	/* Quote special chars with \. */
+#define	GLOB_TILDE	0x0800	/* Expand tilde names from the passwd file. */
+#define	GLOB_LIMIT	0x1000	/* limit number of returned paths */
+
+/* source compatibility, these are the old names */
+#define GLOB_MAXPATH	GLOB_LIMIT
+#define	GLOB_ABEND	GLOB_ABORTED
+
+__BEGIN_DECLS
+int	glob(const char *, int, int (*)(const char *, int), glob_t *);
+void	globfree(glob_t *);
+__END_DECLS
+
+#endif /* !_GLOB_H_ */
diff --git a/logrotate/log.c b/logrotate/log.c
new file mode 100644
index 0000000..b3df49e
--- /dev/null
+++ b/logrotate/log.c
@@ -0,0 +1,134 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_VSYSLOG
+#include <syslog.h>
+#endif
+
+#include "log.h"
+#include <android/log.h>
+#define LOG_TAG "logrotate"
+
+int logLevel = MESS_DEBUG;
+static FILE *errorFile = NULL;
+static FILE *messageFile = NULL;
+static int _logToSyslog = 0;
+int flags = 0;
+
+void logSetLevel(int level)
+{
+    logLevel = level;
+}
+
+void logSetErrorFile(FILE * f)
+{
+    errorFile = f;
+}
+
+void logSetMessageFile(FILE * f)
+{
+    messageFile = f;
+}
+
+void logToSyslog(int enable) {
+	_logToSyslog = enable;
+
+#ifdef HAVE_VSYSLOG
+	if (_logToSyslog) {
+		openlog("logrotate", 0, LOG_USER);
+	}
+	else {
+		closelog();
+	}
+#endif
+}
+
+void logSetFlags(int newFlags)
+{
+    flags |= newFlags;
+}
+
+void logClearFlags(int newFlags)
+{
+    flags &= ~newFlags;
+}
+
+static void log_once(FILE *where, int level, char *format, va_list args)
+{
+	int showTime = 0;
+
+	switch (level) {
+	case MESS_DEBUG:
+		showTime = 1;
+		break;
+	case MESS_NORMAL:
+	case MESS_VERBOSE:
+		break;
+	default:
+		if (flags & LOG_TIMES)
+		fprintf(where, "%ld: ", (long) time(NULL));
+		fprintf(where, "error: ");
+		break;
+	}
+
+	if (showTime && (flags & LOG_TIMES)) {
+		fprintf(where, "%ld:", (long) time(NULL));
+	}
+
+	vfprintf(where, format, args);
+	fflush(where);
+}
+
+void message(int level, char *format, ...)
+{
+	va_list args;
+    
+	if (level >= logLevel) {
+		va_start(args, format);
+		__android_log_vprint(level, LOG_TAG, format, args);
+		log_once(stderr, level, format, args);
+		va_end(args);
+	}
+    
+	if (messageFile != NULL) {
+		va_start(args, format);
+		log_once(messageFile, level, format, args);
+		va_end(args);
+	}
+
+#ifdef HAVE_VSYSLOG
+	if (_logToSyslog) {
+		int priority = LOG_USER;
+
+		switch(level) {
+			case MESS_REALDEBUG:
+				priority |= LOG_DEBUG;
+				break;
+			case MESS_DEBUG:
+			case MESS_VERBOSE:
+			case MESS_NORMAL:
+				priority |= LOG_INFO;
+				break;
+			case MESS_ERROR:
+				priority |= LOG_ERR;
+				break;
+			case MESS_FATAL:
+				priority |= LOG_CRIT;
+				break;
+			default:
+				priority |= LOG_INFO;
+				break;
+		};
+
+		va_start(args, format);
+		vsyslog(priority, format, args);
+		va_end(args);
+	}
+#endif
+
+	if (level == MESS_FATAL)
+		exit(1);
+}
diff --git a/logrotate/log.h b/logrotate/log.h
new file mode 100644
index 0000000..4a9f161
--- /dev/null
+++ b/logrotate/log.h
@@ -0,0 +1,29 @@
+#ifndef H_LOG
+#define H_LOG
+
+#include <stdio.h>
+
+#define MESS_REALDEBUG	1
+#define MESS_DEBUG	2
+#define MESS_VERBOSE	3
+#define MESS_NORMAL	4 //ANDROID_LOG_INFO
+#define MESS_WARN	5 //ANDROID_LOG_WARN
+#define MESS_ERROR	6 //ANDROID_LOG_ERROR
+#define MESS_FATAL	7 //ANDROID_LOG_FATAL
+
+#define LOG_TIMES	(1 << 0)
+
+void message(int level, char *format, ...)
+#ifdef __GNUC__
+    __attribute__ ((format(printf, 2, 3)));
+#else
+;
+#endif
+void logSetErrorFile(FILE * f);
+void logSetMessageFile(FILE * f);
+void logToSyslog(int enable);
+void logSetFlags(int flags);
+void logClearFlags(int flags);
+void logSetLevel(int level);
+
+#endif
diff --git a/logrotate/log_sender b/logrotate/log_sender
new file mode 100755
index 0000000..7455b48
--- /dev/null
+++ b/logrotate/log_sender
@@ -0,0 +1,198 @@
+#!/system/bin/sh
+
+# Copyright 2016 Nest Labs, Inc. All rights reserved.
+
+# Base directory that contains any log reporter state files.
+LOG_STATE_DIR="/data/misc/logrotate"
+LOG_DIR="/data/misc/logd/"
+
+# Max upload/download rate
+LIMIT_RATE="200K"
+
+# Log sender lock in case the sender is already running.
+LOG_SENDER_LOCK="${LOG_STATE_DIR}/lock/log_sender"
+# File descriptor to reference lock file, single digit only in Android Shell(mksh)
+LOG_SENDER_LOCK_FD=9
+
+# Path to a CA certificate file for log server
+CA_CERTIFICATES_FILE_PATH="/system/etc/dropcam_calist.pem"
+SSL_CERT="/data/misc/certs/device_cert.pem"
+SSL_CERT_TYPE="PEM"
+SSL_KEY="/data/misc/certs/device_cert.key"
+SSL_KEY_TYPE="PEM"
+
+# File whose existence implies we're running and not to start again.
+RUN_FILE="${LOG_STATE_DIR}/run/log_sender.pid"
+
+# The tag for all logging we emit.
+TAG="$(basename $0)[$$]"
+
+# Directory to store timestamp files indicating the uploads in the past 24
+# hours.
+TIMESTAMPS_DIR="${LOG_STATE_DIR}/log_sender"
+
+# Temp directory for this process.
+TMP_DIR=""
+
+lecho() {
+  log -t "${TAG}" "$@"
+}
+
+lwarn() {
+  lecho -psyslog.warn "$@"
+}
+
+die () {
+    lecho $@
+    lecho "Exit"
+    exit 1
+}
+
+cleanup() {
+  if [ -n "${TMP_DIR}" ]; then
+    rm -rf "${TMP_DIR}"
+  fi
+  rm -f "${RUN_FILE}"
+  # clean all log_sender files in case things are going wrong
+  rm -rf /data/misc/logrotate/tmp/log_sender*
+}
+
+# Returns true if uploading is enabled
+is_upload_enabled() {
+  [ "$(get_key_value "log.upload_enabled")" -eq 1 ] && return 0
+  return 1
+}
+
+get_key_value() {
+  local key="$1" value
+
+  value=`getprop persist.nl.${key}`
+
+  echo "${value:-undefined}"
+}
+
+send_log() {
+  local log_path="$1"
+  local url="$(get_key_value "log.server")"
+  local send_size="$(stat -c "%s" "${log_path}" 2>/dev/null)"
+  local mac=`sysenv get hwaddr0`
+  # Cloud does not parse ":" in MAC address, so remove all of them
+  mac=${mac//":"/""}
+
+  # If log_reporter.server is not set return with an error.
+  if [ -z "${url}" ]; then
+    lecho "Configuration error: log server not set."
+    return 1
+  fi
+
+  if [[ -z "${mac}" || ${#mac} -ne 12 ]]; then
+    lecho "Configuration error: mac address is invalid."
+    return 1
+  fi
+
+  lecho "Sending log:"
+    lecho "  MAC: ${mac}"
+    lecho "  File: ${log_path}"
+    lecho "  URL: ${url}"
+    lecho "  Size: ${send_size}"
+
+  local curl_stderr="${TMP_DIR}/curl_stderr"
+  local curl_report="${TMP_DIR}/report"
+
+  set +e
+  curl -vvv "${url}" \
+    --cacert "${CA_CERTIFICATES_FILE_PATH}" \
+    --cert "${SSL_CERT}" \
+    --cert-type "${SSL_CERT_TYPE}" \
+    --key "${SSL_KEY}" \
+    --key-type "${SSL_KEY_TYPE}" \
+    --limit-rate "${LIMIT_RATE}" \
+    -F "mac=${mac}" \
+    -F "file=@${log_path};type=application/octet-stream" \
+    -o "${curl_report}" \
+    2>"${curl_stderr}"
+  curl_result=$?
+  set -e
+
+  if [ -f ${curl_report} ]; then
+    lecho "Message from server: " \
+      "$(cat "${curl_report}")"
+  fi
+
+  if [ ${curl_result} -eq 0 ]; then
+    local timestamp="$(date +%s)"
+    lecho "Log ${log_path} has been sent at ${timestamp}"
+  else
+    lecho "Log sending failed with exit code ${curl_result}: " \
+      "$(cat "${curl_stderr}")"
+  fi
+
+  rm -f "${curl_report}"
+  rm -f "${curl_stderr}"
+
+  return ${curl_result}
+}
+
+# *.meta files always end with done=1 so we can tell if they are complete.
+# Remove the given report path.
+remove_report() {
+  rm -f -- "${1}"
+}
+
+# Send all logs from the given directory.
+send_logs() {
+  local dir="$1"
+  lecho "Sending logs for ${dir}"
+
+  if [ ! -d "${dir}" ]; then
+      die "Can't find directory ${dir}"
+  fi
+
+  # start from old
+  for log_path in $(ls -1tr "${dir}"/messages.*.gz 2>/dev/null); do
+    lecho "Considering log ${log_path}."
+
+    # Try to upload.
+    if ! send_log "${log_path}"; then
+      lecho "Problem sending ${log_path}, not removing."
+      continue
+    fi
+
+    # Send was successful, now remove.
+    lecho "Successfully sent log ${log_path} and removing."
+    remove_report "${log_path}"
+  done
+}
+
+main() {
+
+  lecho "Starting log uploading..."
+
+  if ! is_upload_enabled;
+  then
+    lecho "Log uploading is disabled. Exit."
+    exit 0;
+  fi
+
+  # We don't perform checks on this because we have a master lock with the
+  # LOG_SENDER_LOCK file.  This pid file is for the system to keep track
+  # that we're still running.
+  echo $$ > "${RUN_FILE}"
+
+  TMP_DIR="$(mktemp -d "${LOG_STATE_DIR}/tmp/log_sender.XXXXXX")"
+  if [ $? -ne 0 ]; then
+    die "Failed to create TMP_DIR: ${LOG_STATE_DIR}/tmp/log_sender.XXXXX"
+  fi
+
+  # Send system-wide logs
+  send_logs "${LOG_DIR}"
+}
+
+trap cleanup EXIT INT TERM
+
+mkdir -p $(dirname ${LOG_SENDER_LOCK})
+(
+    # -x:Exclusive lock, -n:Non-blocking
+    flock -xn 9 || die "Failed to acquire lock!"
+    main "$@"
+) 9>$LOG_SENDER_LOCK
diff --git a/logrotate/logrotate.8 b/logrotate/logrotate.8
new file mode 100644
index 0000000..f6e6bc6
--- /dev/null
+++ b/logrotate/logrotate.8
@@ -0,0 +1,594 @@
+.TH LOGROTATE 8 "Wed Nov 5 2002" "Linux" "System Administrator's Manual"
+.SH NAME
+logrotate \(hy rotates, compresses, and mails system logs
+.SH SYNOPSIS
+\fBlogrotate\fR [\fB\-dv\fR] [\fB\-f\fR|\fB\-\-force\fR]
+[\fB\-s\fR|\fB\-\-state \fIfile\fR] \fIconfig_file\fR ..
+.SH DESCRIPTION
+\fBlogrotate\fR is designed to ease administration of systems that generate
+large numbers of log files.  It allows automatic rotation, compression, 
+removal, and mailing of log files.  Each log file may be handled daily,
+weekly, monthly, or when it grows too large.
+.P
+Normally, \fBlogrotate\fR is run as a daily cron job.  It will not modify
+a log more than once in one day unless the criterion for that log is
+based on the log's size and \fBlogrotate\fR is being run more than once
+each day, or unless the \fB\-f\fR or \fB\-\-force\fR option is used.
+.P
+Any number of config files may be given on the command line. Later config
+files may override the options given in earlier files, so the order
+in which the \fBlogrotate\fR config files are listed is important.
+Normally, a single config file which includes any other config files
+which are needed should be used.  See below for more information on how
+to use the \fBinclude\fR directive to accomplish this.  If a directory
+is given on the command line, every file in that directory is used as
+a config file.
+.P
+If no command line arguments are given, \fBlogrotate\fR will print
+version and copyright information, along with a short usage summary.  If
+any errors occur while rotating logs, \fBlogrotate\fR will exit with
+non-zero status.
+
+.SH OPTIONS
+.TP
+\fB\-?\fR, \fB\-\-help\fR
+Prints help message.
+
+.TP
+\fB\-d\fR, \fB\-\-debug\fR
+Turns on debug mode and implies \fB-v\fR.  In debug mode, no changes will
+be made to the logs or to the \fBlogrotate\fR state file.
+
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+Tells \fBlogrotate\fR to force the rotation, even if it doesn't think
+this is necessary.  Sometimes this is useful after adding new entries to
+a \fBlogrotate\fR config file, or if old log files have been removed
+by hand, as the new files will be created, and logging will continue
+correctly.
+
+.TP
+\fB\-l <log_file>\fR
+Tells \fBlogrotate\fR to log verbose output into the log_file. The verbose
+output logged to that file is the same as when running \fBlogrotate\fR with
+\fB-v\fR switch. The log file is overwritten on every logrotate execution.
+
+.TP
+\fB\-m\fR, \fB\-\-mail <command>\fR
+Tells \fBlogrotate\fR which command to use when mailing logs. This
+command should accept two arguments: 1) the subject of the message, and
+2) the recipient. The command must then read a message on standard input
+and mail it to the recipient. The default mail command is \fB/bin/mail
+-s\fR.
+
+.TP
+\fB\-s\fR, \fB\-\-state <statefile>\fR
+Tells \fBlogrotate\fR to use an alternate state file.  This is useful
+if logrotate is being run as a different user for various sets of
+log files.  The default state file is \fI/var/lib/logrotate.status\fR.
+
+.TP
+\fB\-\-usage\fR
+Prints a short usage message.
+
+.TP
++\fB\-v\fR, \fB\-\-verbose\fR
+Turns on verbose mode, ie. display messages during rotation.
+
+.SH CONFIGURATION FILE
+
+\fBlogrotate\fR reads everything about the log files it should be handling
+from the series of configuration files specified on the command line.  Each
+configuration file can set global options (local definitions override
+global ones, and later definitions override earlier ones) and specify
+logfiles to rotate. A simple configuration file looks like this:
+
+.nf
+.ta +8n
+# sample logrotate configuration file
+compress
+
+/var/log/messages {
+    rotate 5
+    weekly
+    postrotate
+        /usr/bin/killall \-HUP syslogd
+    endscript
+}
+
+"/var/log/httpd/access.log" /var/log/httpd/error.log {
+    rotate 5
+    mail www@my.org
+    size 100k
+    sharedscripts
+    postrotate
+        /usr/bin/killall \-HUP httpd
+    endscript
+}
+
+/var/log/news/* {
+    monthly
+    rotate 2
+    olddir /var/log/news/old
+    missingok
+    postrotate
+        kill \-HUP `cat /var/run/inn.pid`
+    endscript
+    nocompress
+}
+
+~/log/*.log {}
+
+.fi
+
+.PP
+The first few lines set global options; in the example, logs are
+compressed after they are rotated.  Note that comments may appear
+anywhere in the config file as long as the first non-whitespace
+character on the line is a \fB#\fR.
+
+Values are separated from directives by whitespace and/or an optional =.
+Numbers must be specified in a format understood by \fBstrtoul(3)\fR.
+
+The next section of the config file defines how to handle the log file
+\fI/var/log/messages\fR. The log will go through five weekly rotations before
+being removed. After the log file has been rotated (but before the old
+version of the log has been compressed), the command 
+\fI/sbin/killall \-HUP syslogd\fR will be executed.
+
+The next section defines the parameters for both
+\fI/var/log/httpd/access.log\fR and \fI/var/log/httpd/error.log\fR.
+Each is rotated whenever it grows over 100k in size, and the old logs
+files are mailed (uncompressed) to www@my.org after going through 5
+rotations, rather than being removed. The \fBsharedscripts\fR means that
+the \fBpostrotate\fR script will only be run once (after the old logs have 
+been compressed), not once for each log which is rotated.
+Note that log file names may be enclosed in
+quotes (and that quotes are required if the name contains spaces).
+Normal shell quoting rules apply, with \fB'\fR, \fB"\fR, and \fB\\\fR
+characters supported.
+
+The next section defines the parameters for all of the files in
+\fI/var/log/news\fR. Each file is rotated on a monthly basis.  This is
+considered a single rotation directive and if errors occur for more than
+one file, the log files are not compressed.
+
+The last section uses tilde expansion to rotate log files in the home
+directory of the current user. This is only available, if your glob
+library supports tilde expansion. GNU glob does support this.
+
+Please use wildcards with caution.  If you specify *, \fBlogrotate\fR will
+rotate all files, including previously rotated ones.  A way around this
+is to use the \fBolddir\fR directive or a more exact wildcard (such as *.log).
+
+Here is more information on the directives which may be included in
+a \fBlogrotate\fR configuration file:
+
+.TP
+\fBcompress\fR
+Old versions of log files are compressed with \fBgzip\fR(1) by default. See also
+\fBnocompress\fR. 
+
+.TP
+\fBcompresscmd\fR
+Specifies which command to use to compress log files.  The default is
+\fBgzip\fR(1).  See also \fBcompress\fR.
+
+.TP
+\fBuncompresscmd\fR
+Specifies which command to use to uncompress log files.  The default is
+\fBgunzip\fR(1).
+
+.TP
+\fBcompressext\fR
+Specifies which extension to use on compressed logfiles, if compression
+is enabled.  The default follows that of the configured compression
+command.
+
+.TP
+\fBcompressoptions\fR
+Command line options may be passed to the compression program, if one is
+in use.  The default, for \fBgzip\fR(1), is "\-6" (biased towards high
+compression at the expense of speed).
+If you use a different compression command, you may need to change the
+\fBcompressoptions\fR to match.
+
+
+.TP
+\fBcopy\fR
+Make a copy of the log file, but don't change the original at all.
+This option can be used, for instance, to make a snapshot of the current
+log file, or when some other utility needs to truncate or parse the file.
+When this option is used, the \fBcreate\fR option will have no effect,
+as the old log file stays in place.
+
+.TP
+\fBcopytruncate\fR
+Truncate the original log file to zero size in place after creating a copy,
+instead of moving the old log file and optionally creating a new one.
+It can be used when some program cannot be told to close its logfile
+and thus might continue writing (appending) to the previous log file forever.
+Note that there is a very small time slice between copying the file and
+truncating it, so some logging data might be lost.
+When this option is used, the \fBcreate\fR option will have no effect,
+as the old log file stays in place.
+
+.TP
+\fBcreate \fImode\fR \fIowner\fR \fIgroup\fR, \fBcreate \fIowner\fR \fIgroup\fR
+Immediately after rotation (before the \fBpostrotate\fR script is run)
+the log file is created (with the same name as the log file just rotated).
+\fImode\fR specifies the mode for the log file in octal (the same
+as \fBchmod\fR(2)), \fIowner\fR specifies the user name who will own the
+log file, and \fIgroup\fR specifies the group the log file will belong
+to. Any of the log file attributes may be omitted, in which case those
+attributes for the new file will use the same values as the original log
+file for the omitted attributes. This option can be disabled using the
+\fBnocreate\fR option.
+
+.TP
+\fBcreateolddir \fImode\fR \fIowner\fR \fIgroup\fR
+If the directory specified by \fBolddir\fR directive does not exist, it is
+created. \fImode\fR specifies the mode for the \fBolddir\fR directory
+in octal (the same as \fBchmod\fR(2)), \fIowner\fR specifies the user name
+who will own the \fBolddir\fR directory, and \fIgroup\fR specifies the group
+the \fBolddir\fR directory will belong to. This option can be disabled using the
+\fBnocreateolddir\fR option.
+
+
+.TP
+\fBdaily\fR
+Log files are rotated every day.
+
+.TP
+\fBdateext\fR
+Archive old versions of log files adding a date extension like YYYYMMDD
+instead of simply adding a number. The extension may be configured using
+the \fBdateformat\fR and \fBdateyesterday\fR options.
+
+.TP
+\fBdateformat\fR \fIformat_string\fR
+Specify the extension for \fBdateext\fR using the notation similar to
+\fBstrftime\fR(3) function. Only %Y %m %d %H %M %S %V and %s specifiers are
+allowed.
+The default value is \-%Y%m%d except hourly, which uses \-%Y%m%d%H as default
+value.  Note that also the character separating log name from the extension is
+part of the dateformat string. The system clock must be set past Sep 9th 2001
+for %s to work correctly.
+Note that the datestamps generated by this format must be lexically sortable
+(i.e., first the year, then the month then the day. e.g., 2001/12/01 is ok,
+but 01/12/2001 is not, since 01/11/2002 would sort lower while it is later).
+This is because when using the \fBrotate\fR option, logrotate sorts all
+rotated filenames to find out which logfiles are older and should be removed.
+
+.TP
+\fBdateyesterday\fR
+Use yesterday's instead of today's date to create the \fBdateext\fR
+extension, so that the rotated log file has a date in its name that is
+the same as the timestamps within it.
+
+.TP
+\fBdelaycompress\fR
+Postpone compression of the previous log file to the next rotation cycle.
+This only has effect when used in combination with \fBcompress\fR.
+It can be used when some program cannot be told to close its logfile
+and thus might continue writing to the previous log file for some time.
+
+.TP
+\fBextension \fIext\fR
+Log files with \fIext\fR extension can keep it after the rotation. 
+If compression  is  used,  the compression extension (normally \fI.gz\fR) 
+appears after \fIext\fR. For example you have a logfile named mylog.foo 
+and want to rotate it to mylog.1.foo.gz instead of mylog.foo.1.gz.
+
+.TP
+\fBhourly\fR
+Log files are rotated every hour. Note that usually \fIlogrotate\fR is
+configured to be run by cron daily. You have to change this configuration
+and run \fIlogrotate\fR hourly to be able to really rotate logs hourly.
+
+.TP
+\fBaddextension \fIext\fR
+Log files are given the final extension \fIext\fR after rotation. If
+the original file already ends with \fIext\fR, the extension is not
+duplicated, but merely moved to the end, i.e. both \fBfilename\fR and
+\fBfilename\fIext\fR would get rotated to filename.1\fIext\fR. If
+compression is used, the compression extension (normally \fB.gz\fR)
+appears after \fIext\fR.
+
+.TP
+\fBifempty\fR
+Rotate the log file even if it is empty, overriding the \fBnotifempty\fR
+option (\fBifempty\fR is the default).
+
+.TP
+\fBinclude \fIfile_or_directory\fR
+Reads the file given as an argument as if it was included inline
+where the \fBinclude\fR directive appears. If a directory is given,
+most of the files in that directory are read in alphabetic order
+before processing of the including file continues. The only files
+which are ignored are files which are not regular files (such as
+directories and named pipes) and files whose names end with one of
+the taboo extensions, as specified by the \fBtabooext\fR directive.
+
+.TP
+\fBmail \fIaddress\fR
+When a log is rotated out of existence, it is mailed to \fIaddress\fR. If
+no mail should be generated by a particular log, the \fBnomail\fR directive
+may be used.
+
+.TP
+\fBmailfirst\fR
+When using the \fBmail\fR command, mail the just-rotated file,
+instead of the about-to-expire file.
+
+.TP
+\fBmaillast\fR
+When using the \fBmail\fR command, mail the about-to-expire file,
+instead of the just-rotated file (this is the default).
+
+.TP
+\fBminage\fR \fIcount\fR
+Do not rotate logs which are less than <count> days old.
+
+.TP
+\fBmaxage\fR \fIcount\fR
+Remove rotated logs older than <count> days. The age is only checked
+if the logfile is to be rotated. The files are mailed to the
+configured address if \fBmaillast\fR and \fBmail\fR are configured.
+
+.TP
+\fBmaxsize\fR \fIsize\fR
+Log files are rotated when they grow bigger than \fIsize\fR bytes even
+before the additionally specified time interval (\fBdaily\fR, \fBweekly\fR,
+\fBmonthly\fR, or \fByearly\fR).  The related \fBsize\fR option is similar
+except that it is mutually exclusive with the time interval options, and it
+causes log files to be rotated without regard for the last rotation time.
+When \fBmaxsize\fR is used, both the size and timestamp of a log file are
+considered.
+
+.TP
+\fBminsize\fR  \fIsize\fR
+Log files are rotated when they grow bigger than \fIsize\fR bytes, but not
+before the additionally specified time interval (\fBdaily\fR, \fBweekly\fR,
+\fBmonthly\fR, or \fByearly\fR).  The related \fBsize\fR option is similar
+except that it is mutually exclusive with the time interval options, and it
+causes log files to be rotated without regard for the last rotation time.
+When \fBminsize\fR is used, both the size and timestamp of a log file are
+considered.
+
+.TP
+\fBmissingok\fR
+If the log file is missing, go on to the next one without issuing an error
+message. See also \fBnomissingok\fR.
+
+.TP
+\fBmonthly\fR
+Log files are rotated the first time \fBlogrotate\fR is run in a month
+(this is normally on the first day of the month).
+
+.TP
+\fBnocompress\fR
+Old versions of log files are not compressed. See also \fBcompress\fR.
+
+.TP
+\fBnocopy\fR
+Do not copy the original log file and leave it in place.
+(this overrides the \fBcopy\fR option).
+
+.TP
+\fBnocopytruncate\fR
+Do not truncate the original log file in place after creating a copy
+(this overrides the \fBcopytruncate\fR option).
+
+.TP
+\fBnocreate\fR
+New log files are not created (this overrides the \fBcreate\fR option).
+
+.TP
+\fBnocreateolddir\fR
+\fBolddir\fR directory is not created by logrotate when it does not exist.
+
+.TP
+\fBnodelaycompress\fR
+Do not postpone compression of the previous log file to the next rotation cycle
+(this overrides the \fBdelaycompress\fR option).
+
+.TP
+\fBnodateext\fR
+Do not archive  old versions of log files with date extension
+(this overrides the \fBdateext\fR option).
+
+.TP
+\fBnomail\fR
+Do not mail old log files to any address.
+
+.TP
+\fBnomissingok\fR
+If a log file does not exist, issue an error. This is the default.
+
+.TP
+\fBnoolddir\fR
+Logs are rotated in the directory they normally reside in (this 
+overrides the \fBolddir\fR option).
+
+.TP
+\fBnosharedscripts\fR
+Run \fBprerotate\fR and \fBpostrotate\fR scripts for every log file which
+is rotated (this is the default, and overrides the \fBsharedscripts\fR
+option). The absolute path to the log file is passed as first argument 
+to the script. If the scripts exit with error, the remaining actions will
+not be executed for the affected log only.
+
+.TP
+\fBnoshred\fR
+Do not use \fBshred\fR when deleting old log files. See also \fBshred\fR. 
+
+.TP
+\fBnotifempty\fR
+Do not rotate the log if it is empty (this overrides the \fBifempty\fR option).
+
+.TP
+\fBolddir \fIdirectory\fR
+Logs are moved into \fIdirectory\fR for rotation. The \fIdirectory\fR must be
+on the same physical device as the log file being rotated, unless \fBcopy\fR,
+\fBcopytruncate\fR or \fBrenamecopy\fR option is used. The \fIdirectory\fR
+is assumed to be relative to the directory holding the log file
+unless an absolute path name is specified. When this option is used all
+old versions of the log end up in \fIdirectory\fR.  This option may be
+overridden by the \fBnoolddir\fR option.
+
+.TP
+\fBpostrotate\fR/\fBendscript\fR
+The lines between \fBpostrotate\fR and \fBendscript\fR (both of which
+must appear on lines by themselves) are executed (using \fB/bin/sh\fR) 
+after the log file is rotated. These directives may only appear inside 
+a log file definition. Normally, the absolute path to the log file is 
+passed as first argument to the script. If \fBsharedscripts\fR is specified, 
+whole pattern is passed to the script.
+See also \fBprerotate\fR. See \fBsharedscripts\fR and \fBnosharedscripts\fR
+for error handling.
+
+.TP
+\fBprerotate\fR/\fBendscript\fR
+The lines between \fBprerotate\fR and \fBendscript\fR (both of which
+must appear on lines by themselves) are executed (using \fB/bin/sh\fR) before 
+the log file is rotated and only if the log will actually be rotated. These 
+directives may only appear inside a log file definition. Normally, 
+the absolute path to the log file is passed as first argument to the script.
+If  \fBsharedscripts\fR is specified, whole pattern is passed to the script.
+See also \fBpostrotate\fR.
+See \fBsharedscripts\fR and \fBnosharedscripts\fR for error handling.
+
+.TP
+\fBfirstaction\fR/\fBendscript\fR
+The lines between \fBfirstaction\fR and \fBendscript\fR (both of which
+must appear on lines by themselves) are executed (using \fB/bin/sh\fR) once 
+before all log files that match the wildcarded pattern are rotated, before 
+prerotate script is run and only if at least one log will actually be rotated. 
+These directives may only appear inside a log file definition. Whole pattern is
+passed to the script as first argument. If the script exits with error, 
+no further processing is done. See also \fBlastaction\fR.
+
+.TP
+\fBlastaction\fR/\fBendscript\fR
+The lines between \fBlastaction\fR and \fBendscript\fR (both of which
+must appear on lines by themselves) are executed (using \fB/bin/sh\fR) once 
+after all log files that match the wildcarded pattern are rotated, after 
+postrotate script is run and only if at least one log is rotated. These 
+directives may only appear inside a log file definition. Whole pattern is
+passed to the script as first argument. If the script exits 
+with error, just an error message is shown (as this is the last
+action). See also \fBfirstaction\fR.
+
+.TP
+\fBpreremove\fR/\fBendscript\fR
+The lines between \fBpreremove\fR and \fBendscript\fR (both of which must
+appear on lines by themselves) are executed (using \fB/bin/sh\fR) once just
+before removal of a log file.  The logrotate will pass
+the name of file which is soon to be removed. See also \fBfirstaction\fR.
+
+.TP
+\fBrotate \fIcount\fR
+Log files are rotated \fIcount\fR times before being removed or mailed to the
+address specified in a \fBmail\fR directive. If \fIcount\fR is 0, old versions
+are removed rather than rotated. Default is 0.
+
+.TP
+\fBrenamecopy\fR
+Log file is renamed to temporary filename in the same directory by adding
+".tmp" extension to it. After that, \fBpostrotate\fR script is run
+and log file is copied from temporary filename to final filename. This allows
+storing rotated log files on the different devices using \fBolddir\fR
+directive. In the end, temporary filename is removed.
+
+.TP
+\fBsize \fIsize\fR
+Log files are rotated only if they grow bigger than \fIsize\fR bytes. If
+\fIsize\fR is followed by \fIk\fR, the size is assumed to be in kilobytes.
+If the \fIM\fR is used, the size is in megabytes, and if \fIG\fR is used, the
+size is in gigabytes. So \fIsize 100\fR, \fIsize 100k\fR, \fIsize 100M\fR and
+\fIsize 100G\fR are all valid.
+
+.TP
+\fBsharedscripts\fR
+Normally, \fBprerotate\fR and \fBpostrotate\fR scripts are run for each
+log which is rotated and the absolute path to the log file is passed as first 
+argument to the script. That means a single script may be run multiple
+times for log file entries which match multiple files (such as the 
+\fI/var/log/news/*\fR example). If \fBsharedscripts\fR is specified, the scripts
+are only run once, no matter how many logs match the wildcarded pattern, 
+and whole pattern is passed to them.
+However, if none of the logs in the pattern require rotating, the scripts
+will not be run at all. If the scripts exit with error, the remaining
+actions will not be executed for any logs. This option overrides the
+\fBnosharedscripts\fR option and implies \fBcreate\fR option.
+
+.TP
+\fBshred\fR
+Delete log files using \fBshred\fR \-u instead of unlink().  This should
+ensure that logs are not readable after their scheduled deletion; this is
+off by default.  See also \fBnoshred\fR.
+
+.TP
+\fBshredcycles\fR \fIcount\fR
+Asks GNU \fBshred\fR(1) to overwrite log files \fBcount\fR times before 
+deletion.  Without this option, \fBshred\fR's default will be used.
+
+.TP
+\fBstart \fIcount\fR
+This is the number to use as the base for rotation. For example, if
+you specify 0, the logs will be created with a .0 extension as they are
+rotated from the original log files.  If you specify 9, log files will
+be created with a .9, skipping 0-8.  Files will still be rotated the
+number of times specified with the \fBrotate\fR directive.
+
+.TP
+\fBsu \fIuser\fR \fIgroup\fR
+Rotate log files set under this user and group instead of using default
+user/group (usually root). \fIuser\fR specifies the user name used for
+rotation and \fIgroup\fR specifies the group used for rotation. If the
+user/group you specify here does not have sufficient privilege to make 
+files with the ownership you've specified in a \fIcreate\fR instruction, 
+it will cause an error.
+
+.TP
+\fBtabooext\fR [+] \fIlist\fR
+The current taboo extension list is changed (see the \fBinclude\fR directive
+for information on the taboo extensions). If a + precedes the list of
+extensions, the current taboo extension list is augmented, otherwise it
+is replaced. At startup, the taboo extension list 
+contains .rpmsave, .rpmorig, ~, .disabled, .dpkg\-old, .dpkg\-dist, .dpkg\-new, .cfsaved, .ucf\-old, .ucf\-dist, .ucf\-new, .rpmnew, .swp, .cfsaved, .rhn\-cfg\-tmp\-*
+
+.TP
+\fBweekly\fR
+Log files are rotated if the current weekday is less than the weekday
+of the last rotation or if more than a week has passed since the last
+rotation. This is normally the same as rotating logs on the first day
+of the week, but it works better if \fIlogrotate\fR is not run every
+night.
+
+.TP
+\fByearly\fR
+Log files are rotated if the current year is not the same as the last rotation.
+
+.SH FILES
+.PD 0
+.TP 27
+\fI/var/lib/logrotate.status\fR
+Default state file.
+.TP 27
+\fI/etc/logrotate.conf\fR
+Configuration options.
+
+.SH SEE ALSO
+.BR gzip (1)
+
+<https://github.com/logrotate/logrotate>
+
+.SH AUTHORS
+.nf
+Erik Troan, Preston Brown, Jan Kaluza.
+
+<https://github.com/logrotate/logrotate>
+
+.fi
diff --git a/logrotate/logrotate.c b/logrotate/logrotate.c
new file mode 100644
index 0000000..674fac2
--- /dev/null
+++ b/logrotate/logrotate.c
@@ -0,0 +1,2559 @@
+#include "queue.h"
+/* alloca() is defined in stdlib.h in NetBSD */
+#ifndef __NetBSD__
+#include <alloca.h>
+#endif
+#include <limits.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <glob.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <stdint.h>
+
+#if defined(SunOS)
+#include <limits.h>
+#endif
+
+#include "basenames.h"
+#include "log.h"
+#include "logrotate.h"
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+static security_context_t prev_context = NULL;
+int selinux_enabled = 0;
+int selinux_enforce = 0;
+#else
+void * prev_context = NULL;
+#endif
+
+#ifdef WITH_ACL
+#include "sys/acl.h"
+#define acl_type acl_t
+#define ACL_NOT_WELL_SUPPORTED(Err) \
+     ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
+#else
+#define acl_type void *
+#endif
+
+static acl_type prev_acl = NULL;
+
+#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+
+#ifdef PATH_MAX
+#define STATEFILE_BUFFER_SIZE 2 * PATH_MAX + 16
+#else
+#define STATEFILE_BUFFER_SIZE 4096
+#endif
+
+#ifdef __hpux
+extern int asprintf(char **str, const char *fmt, ...);
+#endif
+
+#if defined(HAVE_FORK)
+#define FORK_OR_VFORK fork
+#define DOEXIT exit
+#elif defined(HAVE_VFORK)
+#define FORK_OR_VFORK vfork
+#define DOEXIT _exit
+#else
+#define FORK_OR_VFORK fork
+#define DOEXIT exit
+#endif
+
+// Number of seconds in a day
+#define DAY_SECONDS 86400
+
+struct logState {
+	char *fn;
+	struct tm lastRotated;	/* only tm_hour, tm_mday, tm_mon, tm_year are good! */
+	struct stat sb;
+	int doRotate;
+	int isUsed;	/* True if there is real log file in system for this state. */
+	LIST_ENTRY(logState) list;
+};
+
+struct logNames {
+    char *firstRotated;
+    char *disposeName;
+    char *finalName;
+    char *dirName;
+    char *baseName;
+};
+
+struct compData {
+	int prefix_len;
+	const char *dformat;
+};
+
+struct logStates {
+	LIST_HEAD(stateSet, logState) head;
+} **states;
+
+unsigned int hashSize;
+int numLogs = 0;
+int debug = 0;
+char *mailCommand = DEFAULT_MAIL_COMMAND;
+time_t nowSecs = 0;
+static uid_t save_euid;
+static gid_t save_egid;
+
+static int shred_file(int fd, char *filename, struct logInfo *log);
+
+static int globerr(const char *pathname, int theerr)
+{
+    message(MESS_ERROR, "error accessing %s: %s\n", pathname,
+	    strerror(theerr));
+
+    /* We want the glob operation to continue, so return 0 */
+    return 1;
+}
+
+#if defined(HAVE_STRPTIME) && defined(HAVE_QSORT)
+
+/* We could switch to qsort_r to get rid of this global variable,
+ * but qsort_r is not portable enough (Linux vs. *BSD vs ...)... */
+static struct compData _compData;
+
+static int compGlobResult(const void *result1, const void *result2)  {
+	struct tm time;
+	time_t t1, t2;
+	const char *r1 = *(const char **)(result1);
+	const char *r2 = *(const char **)(result2);
+
+	memset(&time, 0, sizeof(struct tm));
+	strptime(r1 + _compData.prefix_len, _compData.dformat, &time);
+	t1 = mktime(&time);
+
+	memset(&time, 0, sizeof(struct tm));
+	strptime(r2 + _compData.prefix_len, _compData.dformat, &time);
+	t2 = mktime(&time);
+
+	if (t1 < t2) return -1;
+	if (t1 > t2) return  1;
+	return 0;
+}
+
+static void sortGlobResult(glob_t *result, int prefix_len, const char *dformat) {
+	if (!dformat || *dformat == '\0') {
+		return;
+	}
+
+	_compData.prefix_len = prefix_len;
+	_compData.dformat = dformat;
+	qsort(result->gl_pathv, result->gl_pathc, sizeof(char *), compGlobResult);
+}
+#else
+static void sortGlobResult(glob_t *result, int prefix_len, const char *dformat) {
+	/* TODO */
+}
+#endif
+
+int switch_user(uid_t user, gid_t group) {
+	save_egid = getegid();
+	save_euid = geteuid();
+	if (save_euid == user && save_egid == group)
+		return 0;
+	message(MESS_DEBUG, "switching euid to %d and egid to %d\n",
+		user, group);
+	if (setegid(group) || seteuid(user)) {
+		message(MESS_ERROR, "error switching euid to %d and egid to %d: %s\n",
+			user, group, strerror(errno));
+		return 1;
+	}
+	return 0;
+}
+
+int switch_user_permanently(const struct logInfo *log) {
+	gid_t group = getegid();
+	uid_t user = geteuid();
+	if (!(log->flags & LOG_FLAG_SU)) {
+		return 0;
+	}
+	if (getuid() == user && getgid() == group)
+		return 0;
+	// switch to full root first
+	if (setgid(getgid()) || setuid(getuid())) {
+		message(MESS_ERROR, "error getting rid of euid != uid\n");
+		return 1;
+	}
+	message(MESS_DEBUG, "switching uid to %d and gid to %d\n",
+		user, group);
+	if (setgid(group) || setuid(user)) {
+		message(MESS_ERROR, "error switching euid to %d and egid to %d: %s\n",
+			user, group, strerror(errno));
+		return 1;
+	}
+	return 0;
+}
+
+int switch_user_back() {
+	return switch_user(save_euid, save_egid);
+}
+
+int switch_user_back_permanently() {
+	gid_t tmp_egid = save_egid;
+	uid_t tmp_euid = save_euid;
+	int ret = switch_user(save_euid, save_egid);
+	save_euid = tmp_euid;
+	save_egid = tmp_egid;
+	return ret;
+}
+
+static void unescape(char *arg)
+{
+	char *p = arg;
+	char *next;
+	char escaped;
+	while ((next = strchr(p, '\\')) != NULL) {
+
+		p = next;
+
+		switch (p[1]) {
+		case 'n':
+			escaped = '\n';
+			break;
+		case '\\':
+			escaped = '\\';
+			break;
+		default:
+			++p;
+			continue;
+		}
+
+		/* Overwrite the backslash with the intended character,
+		 * and shift everything down one */
+		*p++ = escaped;
+		memmove(p, p+1, 1 + strlen(p+1));
+	}
+}
+
+#define HASH_SIZE_MIN 64
+static int allocateHash(unsigned int hs)
+{
+	int i;
+
+	/* Enforce some reasonable minimum hash size */
+	if (hs < HASH_SIZE_MIN)
+		hs = HASH_SIZE_MIN;
+
+	message(MESS_DEBUG, "Allocating hash table for state file, size %d entries\n",
+			hs);
+
+	states = calloc(hs, sizeof(struct logStates *));
+	if (states == NULL) {
+		message(MESS_ERROR, "could not allocate memory for "
+				"hash table\n");
+		return 1;
+	}
+
+	for (i = 0; i < hs; i++) {
+		states[i] = malloc(sizeof(struct logState));
+		if (states[i] == NULL) {
+			message(MESS_ERROR, "could not allocate memory for "
+				"hash element\n");
+			return 1;
+		}
+		LIST_INIT(&(states[i]->head));
+	}
+
+	hashSize = hs;
+
+	return 0;
+}
+
+#define HASH_CONST 13
+static unsigned hashIndex(const char *fn)
+{
+	unsigned hash = 0;
+
+	while (*fn) {
+		hash *= HASH_CONST;
+		hash += *fn++;
+	}
+
+	return hash % hashSize;
+}
+
+static int setSecCtx(int fdSrc, const char *src, void **pPrevCtx)
+{
+#ifdef WITH_SELINUX
+    security_context_t srcCtx;
+    *pPrevCtx = NULL;
+
+    if (!selinux_enabled)
+	/* pretend success */
+	return 0;
+
+    /* read security context of fdSrc */
+    if (fgetfilecon_raw(fdSrc, &srcCtx) < 0) {
+	if (errno == ENOTSUP)
+	    /* pretend success */
+	    return 0;
+
+	message(MESS_ERROR, "getting file context %s: %s\n", src,
+		strerror(errno));
+	return selinux_enforce;
+    }
+
+    /* save default security context for restoreSecCtx() */
+    if (getfscreatecon_raw((security_context_t *)pPrevCtx) < 0) {
+	message(MESS_ERROR, "getting default context: %s\n", strerror(errno));
+	return selinux_enforce;
+    }
+
+    /* set default security context to match fdSrc */
+    if (setfscreatecon_raw(srcCtx) < 0) {
+	message(MESS_ERROR, "setting default context to %s: %s\n", srcCtx,
+		strerror(errno));
+	freecon(srcCtx);
+	return selinux_enforce;
+    }
+
+    message(MESS_DEBUG, "set default create context to %s\n", srcCtx);
+    freecon(srcCtx);
+#else
+    (void) fdSrc;
+    (void) src;
+    (void) pPrevCtx;
+#endif
+    return 0;
+}
+
+static int setSecCtxByName(const char *src, void **pPrevCtx)
+{
+    int hasErrors = 0;
+#ifdef WITH_SELINUX
+    int fd = open(src, O_RDONLY | O_NOFOLLOW);
+    if (fd < 0) {
+	message(MESS_ERROR, "error opening %s: %s\n", src, strerror(errno));
+	return 1;
+    }
+    hasErrors = setSecCtx(fd, src, pPrevCtx);
+    close(fd);
+#else
+    (void) src;
+    (void) pPrevCtx;
+#endif
+    return hasErrors;
+}
+
+static void restoreSecCtx(void **pPrevCtx)
+{
+#ifdef WITH_SELINUX
+    const security_context_t prevCtx = (security_context_t) *pPrevCtx;
+    if (!prevCtx)
+	/* no security context saved for restoration */
+	return;
+
+    /* set default security context to the previously stored one */
+    if (selinux_enabled && setfscreatecon_raw(prevCtx) < 0)
+	message(MESS_ERROR, "setting default context to %s: %s\n", prevCtx,
+		strerror(errno));
+
+    /* free the memory allocated to save the security context */
+    freecon(prevCtx);
+    *pPrevCtx = NULL;
+#else
+    (void) pPrevCtx;
+#endif
+}
+
+static struct logState *newState(const char *fn)
+{
+	struct tm now = *localtime(&nowSecs);
+	struct logState *new;
+	time_t lr_time;
+
+	message(MESS_DEBUG, "Creating new state\n");
+
+	if ((new = malloc(sizeof(*new))) == NULL)
+		return NULL;
+
+	if ((new->fn = strdup(fn)) == NULL) {
+		free(new);
+		return NULL;
+	}
+
+	new->doRotate = 0;
+	new->isUsed = 0;
+
+	memset(&new->lastRotated, 0, sizeof(new->lastRotated));
+	new->lastRotated.tm_hour = now.tm_hour;
+	new->lastRotated.tm_mday = now.tm_mday;
+	new->lastRotated.tm_mon = now.tm_mon;
+	new->lastRotated.tm_year = now.tm_year;
+	new->lastRotated.tm_isdst = now.tm_isdst;
+
+	/* fill in the rest of the new->lastRotated fields */
+	lr_time = mktime(&new->lastRotated);
+	new->lastRotated = *localtime(&lr_time);
+
+	return new;
+}
+
+static struct logState *findState(const char *fn)
+{
+	unsigned int i = hashIndex(fn);
+	struct logState *p;
+
+	for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
+		if (!strcmp(fn, p->fn))
+			break;
+
+	/* new state */
+	if (p == NULL) {
+		if ((p = newState(fn)) == NULL)
+			return NULL;
+
+		LIST_INSERT_HEAD(&(states[i]->head), p, list);
+	}
+
+	return p;
+}
+
+static int runScript(struct logInfo *log, char *logfn, char *script)
+{
+	int rc;
+
+	if (debug) {
+		message(MESS_DEBUG, "running script with arg %s: \"%s\"\n",
+			logfn, script);
+		return 0;
+	}
+
+	if (!FORK_OR_VFORK()) {
+		if (log->flags & LOG_FLAG_SU) {
+			if (switch_user_back_permanently() != 0) {
+				DOEXIT(1);
+			}
+		}
+		execl("/system/bin/sh", "sh", "-c", script, "logrotate_script", logfn, NULL);
+		DOEXIT(1);
+	}
+
+	wait(&rc);
+	return rc;
+}
+
+int createOutputFile(char *fileName, int flags, struct stat *sb, acl_type acl, int force_mode)
+{
+    int fd = -1;
+    struct stat sb_create;
+    int acl_set = 0;
+    int i;
+
+    for (i = 0; i < 2; ++i) {
+	fd = open(fileName, (flags | O_EXCL | O_NOFOLLOW),
+		(S_IRUSR | S_IWUSR) & sb->st_mode);
+
+	if ((fd >= 0) || (errno != EEXIST))
+	    break;
+
+	/* the destination file already exists, while it should not. It may caused by a failed compression */
+	message(MESS_ERROR, "destination %s already exists, delete it...\n", fileName);
+	if (unlink(fileName) != 0) {
+	    message(MESS_ERROR, "error deleting already existing output file"
+		    " %s: %s\n", fileName, strerror(errno));
+	    return -1;
+	}
+	/* existing file deleted, try it once again */
+    }
+
+    if (fd < 0) {
+	message(MESS_ERROR, "error creating output file %s: %s\n",
+		fileName, strerror(errno));
+	return -1;
+    }
+    if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
+	message(MESS_ERROR, "error setting mode of %s: %s\n",
+		fileName, strerror(errno));
+	close(fd);
+	return -1;
+    }
+
+	if (fstat(fd, &sb_create)) {
+		message(MESS_ERROR, "fstat of %s failed: %s\n", fileName,
+			strerror(errno));
+		close(fd);
+		return -1;
+	}
+ 
+    if ((sb_create.st_uid != sb->st_uid || sb_create.st_gid != sb->st_gid) && 
+		fchown(fd, sb->st_uid, sb->st_gid)) {
+	message(MESS_ERROR, "error setting owner of %s to uid %d and gid %d: %s\n",
+		fileName, sb->st_uid, sb->st_gid, strerror(errno));
+	close(fd);
+	return -1;
+    }
+
+#ifdef WITH_ACL
+	if (!force_mode && acl) {
+		if (acl_set_fd(fd, acl) == -1) {
+			if (!ACL_NOT_WELL_SUPPORTED(errno)) {
+				message(MESS_ERROR, "setting ACL for %s: %s\n",
+				fileName, strerror(errno));
+				close(fd);
+				return -1;
+			}
+			acl_set = 0;
+		}
+		else {
+			acl_set = 1;
+		}
+	}
+#endif
+
+	if (!acl_set || force_mode) {
+		if (fchmod(fd, sb->st_mode)) {
+		message(MESS_ERROR, "error setting mode of %s: %s\n",
+			fileName, strerror(errno));
+		close(fd);
+		return -1;
+		}
+	}
+
+    return fd;
+}
+
+#define DIGITS 10
+
+/* unlink, but try to call shred from GNU fileutils */
+static int shred_file(int fd, char *filename, struct logInfo *log)
+{
+	char count[DIGITS];    /*  that's a lot of shredding :)  */
+	const char **fullCommand;
+	int id = 0;
+	int status;
+
+	if (log->preremove) {
+	    message(MESS_DEBUG, "running preremove script\n");
+	    if (runScript(log, filename, log->preremove)) {
+		    message(MESS_ERROR,
+			    "error running preremove script "
+			    "for %s of '%s'. Not removing this file.\n",
+			    filename, log->pattern);
+		    /* What ever was supposed to happen did not happen,
+		     * therefore do not unlink the file yet.  */
+		    return 1;
+	    }
+	}
+
+	if (!(log->flags & LOG_FLAG_SHRED)) {
+		return unlink(filename);
+	}
+
+	message(MESS_DEBUG, "Using shred to remove the file %s\n", filename);
+
+	if (log->shred_cycles != 0) {
+		fullCommand = alloca(sizeof(*fullCommand) * 6);
+	}
+	else {
+		fullCommand = alloca(sizeof(*fullCommand) * 4);
+	}
+	fullCommand[id++] = "shred";
+	fullCommand[id++] = "-u";
+
+	if (log->shred_cycles != 0) {
+		fullCommand[id++] = "-n";
+		snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
+		fullCommand[id++] = count;
+	}
+	fullCommand[id++] = "-";
+	fullCommand[id++] = NULL;
+
+	if (!FORK_OR_VFORK()) {
+		dup2(fd, 1);
+		close(fd);
+
+		if (switch_user_permanently(log) != 0) {
+			DOEXIT(1);
+		}
+
+		execvp(fullCommand[0], (void *) fullCommand);
+		DOEXIT(1);
+	}
+	
+	wait(&status);
+
+	if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+		message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
+		return unlink(filename);
+	}
+
+	/* We have to unlink it after shred anyway,
+	 * because it doesn't remove the file itself */
+	return unlink(filename);
+}
+
+static int removeLogFile(char *name, struct logInfo *log)
+{
+	int fd;
+	message(MESS_DEBUG, "removing old log %s\n", name);
+
+	if ((fd = open(name, O_RDWR | O_NOFOLLOW)) < 0) {
+		message(MESS_ERROR, "error opening %s: %s\n",
+			name, strerror(errno));
+		return 1;
+	}
+
+	if (!debug && shred_file(fd, name, log)) {
+		message(MESS_ERROR, "Failed to remove old log %s: %s\n",
+			name, strerror(errno));
+		close(fd);
+		return 1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
+{
+    char *compressedName;
+    char *envInFilename;
+    const char **fullCommand;
+    struct utimbuf utim;
+    int inFile;
+    int outFile;
+    int i;
+    int status;
+    int compressPipe[2];
+    char buff[4092];
+    int error_printed = 0;
+    void *prevCtx;
+
+    message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
+    if (debug)
+	return 0;
+
+    fullCommand = alloca(sizeof(*fullCommand) *
+			 (log->compress_options_count + 2));
+    fullCommand[0] = log->compress_prog;
+    for (i = 0; i < log->compress_options_count; i++)
+	fullCommand[i + 1] = log->compress_options_list[i];
+    fullCommand[log->compress_options_count + 1] = NULL;
+
+    compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
+    sprintf(compressedName, "%s%s", name, log->compress_ext);
+
+    if ((inFile = open(name, O_RDWR | O_NOFOLLOW)) < 0) {
+	message(MESS_ERROR, "unable to open %s for compression\n", name);
+	return 1;
+    }
+
+    if (setSecCtx(inFile, name, &prevCtx) != 0) {
+	/* error msg already printed */
+	close(inFile);
+	return 1;
+    }
+
+#ifdef WITH_ACL
+	if ((prev_acl = acl_get_fd(inFile)) == NULL) {
+		if (!ACL_NOT_WELL_SUPPORTED(errno)) {
+			message(MESS_ERROR, "getting file ACL %s: %s\n",
+				name, strerror(errno));
+			restoreSecCtx(&prevCtx);
+			close(inFile);
+			return 1;
+		}
+	}
+#endif
+
+    outFile =
+	createOutputFile(compressedName, O_RDWR | O_CREAT, sb, prev_acl, 0);
+    restoreSecCtx(&prevCtx);
+#ifdef WITH_ACL
+	if (prev_acl) {
+		acl_free(prev_acl);
+		prev_acl = NULL;
+	}
+#endif
+    if (outFile < 0) {
+	close(inFile);
+	return 1;
+    }
+
+	if (pipe(compressPipe) < 0) {
+		message(MESS_ERROR, "error opening pipe for compress: %s",
+				strerror(errno));
+		close(inFile);
+		close(outFile);
+		return 1;
+	}
+
+    if (!FORK_OR_VFORK()) {
+	dup2(inFile, 0);
+	close(inFile);
+	dup2(outFile, 1);
+	close(outFile);
+	dup2(compressPipe[1], 2);
+	close(compressPipe[0]);
+	close(compressPipe[1]);
+
+	if (switch_user_permanently(log) != 0) {
+		DOEXIT(1);
+	}
+
+	envInFilename = alloca(strlen("LOGROTATE_COMPRESSED_FILENAME=") + strlen(name) + 2);
+	sprintf(envInFilename, "LOGROTATE_COMPRESSED_FILENAME=%s", name);
+	putenv(envInFilename);
+	execvp(fullCommand[0], (void *) fullCommand);
+	DOEXIT(1);
+    }
+
+    close(compressPipe[1]);
+    while ((i = read(compressPipe[0], buff, sizeof(buff) - 1)) > 0) {
+	if (!error_printed) {
+	    error_printed = 1;
+	    message(MESS_ERROR, "Compressing program wrote following message "
+		    "to stderr when compressing log %s:\n", name);
+	}
+	buff[i] = '\0';
+	fprintf(stderr, "%s", buff);
+    }
+    close(compressPipe[0]);
+    wait(&status);
+
+    fsync(outFile);
+    close(outFile);
+
+    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+	message(MESS_ERROR, "failed to compress log %s\n", name);
+	close(inFile);
+	unlink(compressedName);
+	return 1;
+    }
+
+    utim.actime = sb->st_atime;
+    utim.modtime = sb->st_mtime;
+    utime(compressedName,&utim);
+    /* If we can't change atime/mtime, it's not a disaster.
+       It might possibly fail under SELinux. */
+
+    shred_file(inFile, name, log);
+    close(inFile);
+
+    return 0;
+}
+
+static int mailLog(struct logInfo *log, char *logFile, char *mailCommand,
+		   char *uncompressCommand, char *address, char *subject)
+{
+    int mailInput;
+    pid_t mailChild, uncompressChild = 0;
+    int mailStatus, uncompressStatus;
+    int uncompressPipe[2];
+    char *mailArgv[] = { mailCommand, "-s", subject, address, NULL };
+    int rc = 0;
+
+    if ((mailInput = open(logFile, O_RDONLY | O_NOFOLLOW)) < 0) {
+	message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
+		strerror(errno));
+	return 1;
+    }
+
+    if (uncompressCommand) {
+		if (pipe(uncompressPipe) < 0) {
+			message(MESS_ERROR, "error opening pipe for uncompress: %s",
+					strerror(errno));
+			close(mailInput);
+			return 1;
+		}
+		if (!(uncompressChild = FORK_OR_VFORK())) {
+			/* uncompress child */
+			dup2(mailInput, 0);
+			close(mailInput);
+			dup2(uncompressPipe[1], 1);
+			close(uncompressPipe[0]);
+			close(uncompressPipe[1]);
+
+			if (switch_user_permanently(log) != 0) {
+				DOEXIT(1);
+			}
+
+			execlp(uncompressCommand, uncompressCommand, NULL);
+			DOEXIT(1);
+		}
+
+		close(mailInput);
+		mailInput = uncompressPipe[0];
+		close(uncompressPipe[1]);
+    }
+
+    if (!(mailChild = FORK_OR_VFORK())) {
+	dup2(mailInput, 0);
+	close(mailInput);
+	close(1);
+
+	// mail command runs as root
+	if (log->flags & LOG_FLAG_SU) {
+		if (switch_user_back_permanently() != 0) {
+			DOEXIT(1);
+		}
+	}
+
+	execvp(mailArgv[0], mailArgv);
+	DOEXIT(1);
+    }
+
+    close(mailInput);
+
+    waitpid(mailChild, &mailStatus, 0);
+
+    if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
+	message(MESS_ERROR, "mail command failed for %s\n", logFile);
+	rc = 1;
+    }
+
+    if (uncompressCommand) {
+	waitpid(uncompressChild, &uncompressStatus, 0);
+
+	if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
+	    message(MESS_ERROR, "uncompress command failed mailing %s\n",
+		    logFile);
+	    rc = 1;
+	}
+    }
+
+    return rc;
+}
+
+static int mailLogWrapper(char *mailFilename, char *mailCommand,
+			  int logNum, struct logInfo *log)
+{
+	/* if the log is compressed (and we're not mailing a
+	* file whose compression has been delayed), we need
+	* to uncompress it */
+	if ((log->flags & LOG_FLAG_COMPRESS) && !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
+		if (mailLog(log, mailFilename, mailCommand,
+			log->uncompress_prog, log->logAddress,
+			(log->flags & LOG_FLAG_MAILFIRST) ? log->files[logNum] : mailFilename))
+			return 1;
+	} else {
+		if (mailLog(log, mailFilename, mailCommand, NULL,
+			log->logAddress, mailFilename))
+			return 1;
+	}
+	return 0;
+}
+
+/* Use a heuristic to determine whether stat buffer SB comes from a file
+   with sparse blocks.  If the file has fewer blocks than would normally
+   be needed for a file of its size, then at least one of the blocks in
+   the file is a hole.  In that case, return true.  */
+static int is_probably_sparse(struct stat const *sb)
+{
+#if defined(HAVE_STRUCT_STAT_ST_BLOCKS) && defined(HAVE_STRUCT_STAT_ST_BLKSIZE)
+	return (S_ISREG (sb->st_mode)
+          && sb->st_blocks < sb->st_size / sb->st_blksize);
+#else
+	return 0;
+#endif
+}
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* Return whether the buffer consists entirely of NULs.
+   Note the word after the buffer must be non NUL. */
+
+static inline int is_nul (void const *buf, size_t bufsize)
+{
+  char const *cbuf = buf;
+  char const *cp = buf;
+
+  /* Find the first nonzero *byte*, or the sentinel.  */
+  while (*cp++ == 0)
+    continue;
+
+  return cbuf + bufsize < cp;
+}
+
+static size_t full_write(int fd, const void *buf, size_t count)
+{
+  size_t total = 0;
+  const char *ptr = (const char *) buf;
+
+  while (count > 0)
+    {
+      size_t n_rw;
+	for (;;)
+	{
+		n_rw = write (fd, buf, count);
+		if (errno == EINTR)
+			continue;
+		else
+			break;
+	}
+	if (n_rw == (size_t) -1)
+		break;
+	if (n_rw == 0)
+		break;
+	total += n_rw;
+	ptr += n_rw;
+	count -= n_rw;
+    }
+
+  return total;
+}
+
+static int sparse_copy(int src_fd, int dest_fd, struct stat *sb,
+		       const char *saveLog, const char *currLog)
+{
+	int make_holes = is_probably_sparse(sb);
+	size_t max_n_read = SIZE_MAX;
+	int last_write_made_hole = 0;
+	off_t total_n_read = 0;
+	char buf[BUFSIZ + 1];
+
+	while (max_n_read) {
+		int make_hole = 0;
+
+		ssize_t n_read = read (src_fd, buf, MIN (max_n_read, BUFSIZ));
+		if (n_read < 0) {
+			if (errno == EINTR) {
+				continue;
+			}
+			message(MESS_ERROR, "error reading %s: %s\n",
+				currLog, strerror(errno));
+			return 0;
+		}
+
+		if (n_read == 0)
+			break;
+
+		max_n_read -= n_read;
+		total_n_read += n_read;
+
+		if (make_holes) {
+			/* Sentinel required by is_nul().  */
+			buf[n_read] = '\1';
+
+			if ((make_hole = is_nul(buf, n_read))) {
+				if (lseek (dest_fd, n_read, SEEK_CUR) < 0) {
+					message(MESS_ERROR, "error seeking %s: %s\n",
+						saveLog, strerror(errno));
+					return 0;
+				}
+			}
+		}
+
+		if (!make_hole) {
+			size_t n = n_read;
+			if (full_write (dest_fd, buf, n) != n) {
+				message(MESS_ERROR, "error writing to %s: %s\n",
+					saveLog, strerror(errno));
+				return 0;
+			}
+		}
+
+		last_write_made_hole = make_hole;
+	}
+
+	if (last_write_made_hole) {
+		if (ftruncate(dest_fd, total_n_read) < 0) {
+			message(MESS_ERROR, "error ftruncate %s: %s\n",
+			saveLog, strerror(errno));
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
+			int flags)
+{
+    int fdcurr = -1, fdsave = -1;
+    void *prevCtx;
+
+    message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
+
+    if (!debug) {
+	if ((fdcurr = open(currLog, ((flags & LOG_FLAG_COPY) ? O_RDONLY : O_RDWR) | O_NOFOLLOW)) < 0) {
+	    message(MESS_ERROR, "error opening %s: %s\n", currLog,
+		    strerror(errno));
+	    return 1;
+	}
+
+	if (setSecCtx(fdcurr, currLog, &prevCtx) != 0) {
+	    /* error msg already printed */
+	    close(fdcurr);
+	    return 1;
+	}
+#ifdef WITH_ACL
+	if ((prev_acl = acl_get_fd(fdcurr)) == NULL) {
+		if (!ACL_NOT_WELL_SUPPORTED(errno)) {
+			message(MESS_ERROR, "getting file ACL %s: %s\n",
+				currLog, strerror(errno));
+			restoreSecCtx(&prevCtx);
+			close(fdcurr);
+			return 1;
+		}
+	}
+#endif /* WITH_ACL */
+	fdsave =
+	    createOutputFile(saveLog, O_WRONLY | O_CREAT, sb, prev_acl, 0);
+	restoreSecCtx(&prevCtx);
+#ifdef WITH_ACL
+	if (prev_acl) {
+		acl_free(prev_acl);
+		prev_acl = NULL;
+	}
+#endif
+	if (fdsave < 0) {
+	    close(fdcurr);
+	    return 1;
+	}
+
+	if (sparse_copy(fdcurr, fdsave, sb, saveLog, currLog) != 1) {
+		close(fdcurr);
+		close(fdsave);
+		message(MESS_ERROR, "error copying %s to %s: %s\n", currLog,
+				saveLog, strerror(errno));
+		unlink(saveLog);
+		return 1;
+	}
+    }
+
+    if (flags & LOG_FLAG_COPYTRUNCATE) {
+	message(MESS_DEBUG, "truncating %s\n", currLog);
+
+	if (!debug) {
+	    fsync(fdsave);
+	    if (ftruncate(fdcurr, 0)) {
+		message(MESS_ERROR, "error truncating %s: %s\n", currLog,
+			strerror(errno));
+		close(fdcurr);
+		close(fdsave);
+		return 1;
+	    }
+	}
+    } else
+	message(MESS_DEBUG, "Not truncating %s\n", currLog);
+
+    if (fdcurr >= 0) {
+	close(fdcurr);
+    }
+    if (fdsave >= 0) {
+	close(fdsave);
+    }
+    return 0;
+}
+
+int findNeedRotating(struct logInfo *log, int logNum, int force)
+{
+    struct stat sb;
+    struct logState *state = NULL;
+    struct tm now = *localtime(&nowSecs);
+
+    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
+
+	/* Check if parent directory of this log has safe permissions */
+	if ((log->flags & LOG_FLAG_SU) == 0 && getuid() == 0) {
+		char *ld = ourDirName(log->files[logNum]);
+		if (stat(ld, &sb)) {
+			/* If parent directory doesn't exist, it's not real error
+			  (unless nomissingok is specified)
+			  and rotation is not needed */
+			if (errno != ENOENT || (errno == ENOENT && (log->flags & LOG_FLAG_MISSINGOK) == 0)) {
+				message(MESS_ERROR, "stat of %s failed: %s\n", ld,
+					strerror(errno));
+				free(ld);
+				return 1;
+			}
+			free(ld);
+			return 0;
+		}
+		/* Don't rotate in directories writable by others or group which is not "root"  */
+		if ((sb.st_gid != 0 && sb.st_mode & S_IWGRP) || sb.st_mode & S_IWOTH) {
+			message(MESS_ERROR, "skipping \"%s\" because parent directory has insecure permissions"
+								" (It's world writable or writable by group which is not \"root\")"
+								" Set \"su\" directive in config file to tell logrotate which user/group"
+								" should be used for rotation.\n"
+								,log->files[logNum]);
+			free(ld);
+			return 1;
+		}
+		free(ld);
+	}
+
+    if (lstat(log->files[logNum], &sb)) {
+	if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
+	    message(MESS_DEBUG, "  log %s does not exist -- skipping\n",
+		    log->files[logNum]);
+	    return 0;
+	}
+	message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
+		strerror(errno));
+	return 1;
+    }
+
+	state = findState(log->files[logNum]);
+	state->doRotate = 0;
+	state->sb = sb;
+	state->isUsed = 1;
+
+	if ((sb.st_mode & S_IFMT) == S_IFLNK) {
+	    message(MESS_DEBUG, "  log %s is symbolic link. Rotation of symbolic"
+			" links is not allowed to avoid security issues -- skipping.\n",
+		    log->files[logNum]);
+		return 0;
+	}
+
+    message(MESS_DEBUG, "  Now: %d-%02d-%02d %02d:%02d\n", 1900 + now.tm_year,
+	    1 + now.tm_mon, now.tm_mday,
+	    now.tm_hour, now.tm_min);
+
+    message(MESS_DEBUG, "  Last rotated at %d-%02d-%02d %02d:%02d\n", 1900 + state->lastRotated.tm_year,
+	    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+	    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+
+    if (force) {
+	/* user forced rotation of logs from command line */
+	state->doRotate = 1;   
+    }
+    else if (log->maxsize && sb.st_size > log->maxsize) {
+        state->doRotate = 1;
+    }
+    else if (log->criterium == ROT_SIZE) {
+	state->doRotate = (sb.st_size >= log->threshhold);
+	if (!state->doRotate) {
+	message(MESS_DEBUG, "  log does not need rotating "
+		"(log size is below the 'size' threshold)\n");
+	}
+    } else if (mktime(&state->lastRotated) - mktime(&now) > (25 * 3600)) {
+        /* 25 hours allows for DST changes as well as geographical moves */
+	message(MESS_ERROR,
+		"log %s last rotated in the future -- rotation forced\n",
+		log->files[logNum]);
+	state->doRotate = 1;
+    } else if (state->lastRotated.tm_year != now.tm_year ||
+	       state->lastRotated.tm_mon != now.tm_mon ||
+	       state->lastRotated.tm_mday != now.tm_mday ||
+	       state->lastRotated.tm_hour != now.tm_hour) {
+	switch (log->criterium) {
+	case ROT_WEEKLY:
+	    /* rotate if:
+	       1) the current weekday is before the weekday of the
+	       last rotation
+	       2) more then a week has passed since the last
+	       rotation */
+	    state->doRotate = ((now.tm_wday < state->lastRotated.tm_wday)
+			       ||
+			       ((mktime(&now) -
+				 mktime(&state->lastRotated)) >
+				(7 * 24 * 3600)));
+	    if (!state->doRotate) {
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "(log has been rotated at %d-%d-%d %d:%d, "
+		    "that is not week ago yet)\n", 1900 + state->lastRotated.tm_year,
+		    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+		    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+	    }
+	    break;
+	case ROT_HOURLY:
+	    state->doRotate = ((now.tm_hour != state->lastRotated.tm_hour) ||
+			    (now.tm_mday != state->lastRotated.tm_mday) ||
+			    (now.tm_mon != state->lastRotated.tm_mon) ||
+			    (now.tm_year != state->lastRotated.tm_year));
+	    if (!state->doRotate) {
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "(log has been rotated at %d-%d-%d %d:%d, "
+		    "that is not hour ago yet)\n", 1900 + state->lastRotated.tm_year,
+		    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+		    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+	    }
+	    break;
+	case ROT_DAYS:
+	    /* FIXME: only days=1 is implemented!! */
+	    state->doRotate = ((now.tm_mday != state->lastRotated.tm_mday) ||
+			    (now.tm_mon != state->lastRotated.tm_mon) ||
+			    (now.tm_year != state->lastRotated.tm_year));
+	    if (!state->doRotate) {
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "(log has been rotated at %d-%d-%d %d:%d, "
+		    "that is not day ago yet)\n", 1900 + state->lastRotated.tm_year,
+		    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+		    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+	    }
+	    break;
+	case ROT_MONTHLY:
+	    /* rotate if the logs haven't been rotated this month or
+	       this year */
+	    state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
+			    (now.tm_year != state->lastRotated.tm_year));
+	    if (!state->doRotate) {
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "(log has been rotated at %d-%d-%d %d:%d, "
+		    "that is not month ago yet)\n", 1900 + state->lastRotated.tm_year,
+		    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+		    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+	    }
+	    break;
+	case ROT_YEARLY:
+	    /* rotate if the logs haven't been rotated this year */
+	    state->doRotate = (now.tm_year != state->lastRotated.tm_year);
+	    if (!state->doRotate) {
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "(log has been rotated at %d-%d-%d %d:%d, "
+		    "that is not year ago yet)\n", 1900 + state->lastRotated.tm_year,
+		    1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
+		    state->lastRotated.tm_hour, state->lastRotated.tm_min);
+	    }
+	    break;
+	default:
+	    /* ack! */
+	    state->doRotate = 0;
+	    break;
+	}
+	if (log->minsize && sb.st_size < log->minsize) {
+	    state->doRotate = 0;
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "('minsize' directive is used and the log "
+		    "size is smaller than the minsize value)\n");
+	}
+	if (log->rotateMinAge && log->rotateMinAge >= (nowSecs - sb.st_mtime) / DAY_SECONDS) {
+	    state->doRotate = 0;
+	    message(MESS_DEBUG, "  log does not need rotating "
+		    "('minage' directive is used and the log "
+		    "age is smaller than the minage days)\n");
+	}
+    }
+    else if (!state->doRotate) {
+	message(MESS_DEBUG, "  log does not need rotating "
+		"(log has been already rotated)\n");
+    }
+
+    /* The notifempty flag overrides the normal criteria */
+    if (state->doRotate && !(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size) {
+	state->doRotate = 0;
+	message(MESS_DEBUG, "  log does not need rotating "
+		"(log is empty)\n");
+    }
+
+    if (state->doRotate) {
+	message(MESS_DEBUG, "  log needs rotating\n");
+    }
+
+    return 0;
+}
+
+int prerotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
+		       struct logNames *rotNames)
+{
+    struct tm now = *localtime(&nowSecs);
+    char *oldName, *newName = NULL;
+    char *compext = "";
+    char *fileext = "";
+    int hasErrors = 0;
+    int i, j;
+    char *glob_pattern;
+    glob_t globResult;
+    int rc;
+    int rotateCount = log->rotateCount ? log->rotateCount : 1;
+    int logStart = (log->logStart == -1) ? 1 : log->logStart;
+#define DATEEXT_LEN 64
+#define PATTERN_LEN (DATEEXT_LEN * 2)
+    char dext_str[DATEEXT_LEN];
+    char dformat[DATEEXT_LEN] = "";
+    char dext_pattern[PATTERN_LEN];
+    char *dext;
+
+    if (!state->doRotate)
+	return 0;
+
+    /* Logs with rotateCounts of 0 are rotated once, then removed. This
+       lets scripts run properly, and everything gets mailed properly. */
+
+    message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
+	    log->files[logNum], log->rotateCount);
+
+    if (log->flags & LOG_FLAG_COMPRESS)
+	compext = log->compress_ext;
+
+    state->lastRotated = now;
+
+    if (log->oldDir) {
+	if (log->oldDir[0] != '/') {
+	    char *ld = ourDirName(log->files[logNum]);
+	    rotNames->dirName =
+		malloc(strlen(ld) + strlen(log->oldDir) + 2);
+	    sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
+	    free(ld);
+	} else
+	    rotNames->dirName = strdup(log->oldDir);
+    } else
+	rotNames->dirName = ourDirName(log->files[logNum]);
+
+    rotNames->baseName = strdup(ourBaseName(log->files[logNum]));
+
+    if (log->addextension) {
+	size_t baseLen = strlen(rotNames->baseName);
+	size_t extLen = strlen(log->addextension);
+	if (baseLen >= extLen &&
+		strncmp(&(rotNames->baseName[baseLen - extLen]),
+		    log->addextension, extLen) == 0) {
+	    char *tempstr;
+
+	    tempstr = calloc(baseLen - extLen + 1, sizeof(char));
+	    strncat(tempstr, rotNames->baseName, baseLen - extLen);
+	    free(rotNames->baseName);
+	    rotNames->baseName = tempstr;
+	}
+	fileext = log->addextension;
+    }
+
+    if (log->extension &&
+	strncmp(&
+		(rotNames->
+		 baseName[strlen(rotNames->baseName) -
+			  strlen(log->extension)]), log->extension,
+		strlen(log->extension)) == 0) {
+	char *tempstr;
+
+	fileext = log->extension;
+	tempstr =
+	    calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
+		   sizeof(char));
+	strncat(tempstr, rotNames->baseName,
+		strlen(rotNames->baseName) - strlen(log->extension));
+	free(rotNames->baseName);
+	rotNames->baseName = tempstr;
+    }
+	
+    /* Adjust "now" if we want yesterday's date */
+    if (log->flags & LOG_FLAG_DATEYESTERDAY) {
+        now.tm_hour = 12; /* set hour to noon to work around DST issues */
+        now.tm_mday = now.tm_mday - 1;
+        mktime(&now);
+    }
+
+	/* Allow only %Y %d %m and create valid strftime format string
+	 * Construct the glob pattern corresponding to the date format */
+	dext_str[0] = '\0';
+	if (log->dateformat) {
+		i = j = 0;
+		memset(dext_pattern, 0, sizeof(dext_pattern));
+		dext = log->dateformat;
+		while (*dext == ' ')
+			dext++;
+		while ((*dext != '\0') && (!hasErrors)) {
+			/* Will there be a space for a char and '\0'? */
+			if (j >= (sizeof(dext_pattern) - 1)) {
+				message(MESS_ERROR, "Date format %s is too long\n",
+						log->dateformat);
+				hasErrors = 1;
+				break;
+			}
+			if (*dext == '%') {
+				switch (*(dext + 1)) {
+					case 'Y':
+						strncat(dext_pattern, "[0-9][0-9]",
+								sizeof(dext_pattern) - strlen(dext_pattern) - 1);
+						j += 10; /* strlen("[0-9][0-9]") */
+					case 'm':
+					case 'd':
+					case 'H':
+					case 'M':
+					case 'S':
+					case 'V':
+						strncat(dext_pattern, "[0-9][0-9]",
+								sizeof(dext_pattern) - strlen(dext_pattern) - 1);
+						j += 10;
+						if (j >= (sizeof(dext_pattern) - 1)) {
+							message(MESS_ERROR, "Date format %s is too long\n",
+									log->dateformat);
+							hasErrors = 1;
+							break;
+						}
+						dformat[i++] = *(dext++);
+						dformat[i] = *dext;
+						break;
+					case 's':
+						/* End of year 2293 this pattern does not work. */
+						strncat(dext_pattern,
+								"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
+								sizeof(dext_pattern) - strlen(dext_pattern) - 1);
+						j += 50;
+						if (j >= (sizeof(dext_pattern) - 1)) {
+							message(MESS_ERROR, "Date format %s is too long\n",
+									log->dateformat);
+							hasErrors = 1;
+							break;
+						}
+						dformat[i++] = *(dext++);
+						dformat[i] = *dext;
+						break;
+					default:
+						dformat[i++] = *dext;
+						dformat[i] = '%';
+						dext_pattern[j++] = *dext;
+						break;
+				}
+			} else {
+				dformat[i] = *dext;
+				dext_pattern[j++] = *dext;
+			}
+			++i;
+			++dext;
+		}
+		dformat[i] = '\0';
+		message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
+		strftime(dext_str, sizeof(dext_str), dformat, &now);
+	} else {
+		if (log->criterium == ROT_HOURLY) {
+			/* hourly adds another two digits */
+			strftime(dext_str, sizeof(dext_str), "-%Y%m%d%H", &now);
+			strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
+					sizeof(dext_pattern));
+		} else {
+			/* The default dateformat and glob pattern */
+			strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
+			strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
+					sizeof(dext_pattern));
+		}
+		dext_pattern[PATTERN_LEN - 1] = '\0';
+	}
+	message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
+	message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
+
+    if (setSecCtxByName(log->files[logNum], (void **) &prev_context) != 0) {
+	/* error msg already printed */
+	return 1;
+    }
+
+    /* First compress the previous log when necessary */
+    if (log->flags & LOG_FLAG_COMPRESS &&
+	log->flags & LOG_FLAG_DELAYCOMPRESS) {
+	if (log->flags & LOG_FLAG_DATEEXT) {
+		/* glob for uncompressed files with our pattern */
+		if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
+					rotNames->baseName, dext_pattern, fileext) < 0) {
+			message(MESS_FATAL, "could not allocate glob pattern memory\n");
+		}
+	    rc = glob(glob_pattern, 0, globerr, &globResult);
+	    if (!rc && globResult.gl_pathc > 0) {
+		sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat);
+		for (i = 0; i < globResult.gl_pathc && !hasErrors; i++) {
+		    struct stat sbprev;
+
+			if (asprintf(&oldName, "%s", (globResult.gl_pathv)[i]) < 0) {
+				message(MESS_FATAL, "could not allocate glob result memory\n");
+			}
+			if (stat(oldName, &sbprev)) {
+			message(MESS_DEBUG,
+				"previous log %s does not exist\n",
+				oldName);
+		    } else {
+			hasErrors = compressLogFile(oldName, log, &sbprev);
+		    }
+		    free(oldName);
+		}
+	    } else {
+		message(MESS_DEBUG,
+			"glob finding logs to compress failed\n");
+		/* fallback to old behaviour */
+		if (asprintf(&oldName, "%s/%s.%d%s", rotNames->dirName,
+			rotNames->baseName, logStart, fileext) < 0) {
+				message(MESS_FATAL, "could not allocate oldName memory\n");
+			}
+		free(oldName);
+	    }
+	    globfree(&globResult);
+	    free(glob_pattern);
+	} else {
+	    struct stat sbprev;
+	    if (asprintf(&oldName, "%s/%s.%d%s", rotNames->dirName,
+		    rotNames->baseName, logStart, fileext) < 0) {
+			message(MESS_FATAL, "could not allocate oldName memory\n");
+	    }
+	    if (stat(oldName, &sbprev)) {
+		message(MESS_DEBUG, "previous log %s does not exist\n",
+			oldName);
+	    } else {
+		hasErrors = compressLogFile(oldName, log, &sbprev);
+	    }
+	    free(oldName);
+	}
+    }
+
+    /* adding 2 due to / and \0 being added by snprintf */
+    rotNames->firstRotated =
+	malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
+	       strlen(fileext) + strlen(compext) + DATEEXT_LEN + 2 );
+
+    if (log->flags & LOG_FLAG_DATEEXT) {
+	/* glob for compressed files with our pattern
+	 * and compress ext */
+	if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
+				rotNames->baseName, dext_pattern, fileext, compext) < 0) {
+		message(MESS_ERROR, "could not allocate glob pattern memory\n");
+	}
+	rc = glob(glob_pattern, 0, globerr, &globResult);
+	if (!rc) {
+	    /* search for files to drop, if we find one remember it,
+	     * if we find another one mail and remove the first and
+	     * remember the second and so on */
+	    struct stat fst_buf;
+	    int mail_out = -1;
+	    /* remove the first (n - rotateCount) matches
+	     * no real rotation needed, since the files have
+	     * the date in their name */
+		sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat);
+	    for (i = 0; i < globResult.gl_pathc; i++) {
+		if (!stat((globResult.gl_pathv)[i], &fst_buf)) {
+		    if ((i <= ((int) globResult.gl_pathc - rotateCount))
+			|| ((log->rotateAge > 0)
+			    &&
+			    (((nowSecs - fst_buf.st_mtime) / DAY_SECONDS)
+			     > log->rotateAge))) {
+			if (mail_out != -1) {
+			    char *mailFilename =
+				(globResult.gl_pathv)[mail_out];
+			    if (!hasErrors && log->logAddress)
+				hasErrors =
+				    mailLogWrapper(mailFilename,
+						   mailCommand, logNum,
+						   log);
+			    if (!hasErrors) {
+				message(MESS_DEBUG, "removing %s\n", mailFilename);
+				hasErrors = removeLogFile(mailFilename, log);
+				}
+			}
+			mail_out = i;
+		    }
+		}
+	    }
+	    if (mail_out != -1) {
+		/* oldName is oldest Backup found (for unlink later) */
+		if (asprintf(&oldName, "%s", (globResult.gl_pathv)[mail_out]) < 0) {
+		    message(MESS_FATAL, "could not allocate mailout memory\n");
+		}
+		rotNames->disposeName = malloc(strlen(oldName)+1);
+		strcpy(rotNames->disposeName, oldName);
+		free(oldName);
+	    } else {
+		free(rotNames->disposeName);
+		rotNames->disposeName = NULL;
+	    }
+	} else {
+	    message(MESS_DEBUG, "glob finding old rotated logs failed\n");
+	    free(rotNames->disposeName);
+	    rotNames->disposeName = NULL;
+	}
+	/* firstRotated is most recently created/compressed rotated log */
+	sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
+		rotNames->dirName, rotNames->baseName, dext_str, fileext,
+		(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
+	globfree(&globResult);
+	free(glob_pattern);
+    } else {
+	if (log->rotateAge) {
+	    struct stat fst_buf;
+	    for (i = 1; i <= rotateCount + 1; i++) {
+		if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
+			rotNames->baseName, i, fileext, compext) < 0) {
+		    message(MESS_FATAL, "could not allocate mailFilename memory\n");
+		}
+		if (!stat(oldName, &fst_buf)
+		    && (((nowSecs - fst_buf.st_mtime) / DAY_SECONDS)
+			> log->rotateAge)) {
+		    char *mailFilename = oldName;
+		    if (!hasErrors && log->logAddress)
+			hasErrors =
+			    mailLogWrapper(mailFilename, mailCommand,
+					   logNum, log);
+		    if (!hasErrors)
+			hasErrors = removeLogFile(mailFilename, log);
+		}
+		free(oldName);
+	    }
+	}
+
+	if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
+		rotNames->baseName, logStart + rotateCount, fileext,
+		compext) < 0) {
+	    message(MESS_FATAL, "could not allocate disposeName memory\n");
+	}
+	newName = strdup(oldName);
+
+	rotNames->disposeName = strdup(oldName);
+
+	sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
+		rotNames->baseName, logStart, fileext,
+		(log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
+
+	for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
+		free(newName);
+		newName = oldName;
+		if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
+		    rotNames->baseName, i, fileext, compext) < 0) {
+		    message(MESS_FATAL, "could not allocate oldName memory\n");
+		}
+
+	    message(MESS_DEBUG,
+		    "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
+		    oldName, newName, rotateCount, logStart, i);
+
+	    if (!debug && rename(oldName, newName)) {
+		if (errno == ENOENT) {
+		    message(MESS_DEBUG, "old log %s does not exist\n",
+			    oldName);
+		} else {
+		    message(MESS_ERROR, "error renaming %s to %s: %s\n",
+			    oldName, newName, strerror(errno));
+		    hasErrors = 1;
+		}
+	    }
+	    if (hasErrors || i - 1 < 0)
+		    free(oldName);
+	    
+	}
+	free(newName);
+    }				/* !LOG_FLAG_DATEEXT */
+
+	if (log->flags & LOG_FLAG_DATEEXT) {
+		char *destFile;
+		struct stat fst_buf;
+
+		if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
+					rotNames->baseName, dext_str, fileext) < 0) {
+			message(MESS_FATAL, "could not allocate finalName memory\n");
+		}
+		if (asprintf(&destFile, "%s%s", rotNames->finalName, compext) < 0) {
+			message(MESS_FATAL, "could not allocate destFile memory\n");
+		}
+		if (!stat(destFile, &fst_buf)) {
+			message(MESS_ERROR,
+					"destination %s already exists, skipping rotation\n",
+					rotNames->firstRotated);
+			hasErrors = 1;
+		}
+		free(destFile);
+	} else {
+		/* note: the gzip extension is *not* used here! */
+		if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
+					rotNames->baseName, logStart, fileext) < 0) {
+			message(MESS_ERROR, "could not allocate finalName memory\n");
+		}
+	}
+
+    /* if the last rotation doesn't exist, that's okay */
+    if (rotNames->disposeName && access(rotNames->disposeName, F_OK)) {
+	message(MESS_DEBUG,
+		"log %s doesn't exist -- won't try to dispose of it\n",
+		rotNames->disposeName);
+	free(rotNames->disposeName);
+	rotNames->disposeName = NULL;
+    }
+
+    return hasErrors;
+}
+
+int rotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
+		    struct logNames *rotNames)
+{
+    int hasErrors = 0;
+    struct stat sb;
+    int fd;
+    void *savedContext = NULL;
+    char *tmpFilename = NULL;
+
+    if (!state->doRotate)
+	return 0;
+
+    if (!hasErrors) {
+
+	if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
+	    if (setSecCtxByName(log->files[logNum], &savedContext) != 0) {
+		/* error msg already printed */
+		return 1;
+	    }
+#ifdef WITH_ACL
+		if ((prev_acl = acl_get_file(log->files[logNum], ACL_TYPE_ACCESS)) == NULL) {
+			if (!ACL_NOT_WELL_SUPPORTED(errno)) {
+				message(MESS_ERROR, "getting file ACL %s: %s\n",
+					log->files[logNum], strerror(errno));
+				hasErrors = 1;
+			}
+		}
+#endif /* WITH_ACL */
+		if (log->flags & LOG_FLAG_TMPFILENAME) {
+			if (asprintf(&tmpFilename, "%s%s", log->files[logNum], ".tmp") < 0) {
+				message(MESS_FATAL, "could not allocate tmpFilename memory\n");
+				restoreSecCtx(&savedContext);
+				return 1;
+			}
+
+			message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
+				tmpFilename);
+			if (!debug && !hasErrors && rename(log->files[logNum], tmpFilename)) {
+			message(MESS_ERROR, "failed to rename %s to %s: %s\n",
+				log->files[logNum], tmpFilename,
+				strerror(errno));
+				hasErrors = 1;
+			}
+		}
+		else {
+			message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
+				rotNames->finalName);
+			if (!debug && !hasErrors &&
+			rename(log->files[logNum], rotNames->finalName)) {
+				message(MESS_ERROR, "failed to rename %s to %s: %s\n",
+					log->files[logNum], tmpFilename,
+					strerror(errno));
+					hasErrors = 1;
+			}
+	    }
+
+	    if (!log->rotateCount) {
+		rotNames->disposeName =
+		    realloc(rotNames->disposeName,
+			    strlen(rotNames->dirName) +
+			    strlen(rotNames->baseName) +
+			    strlen(log->files[logNum]) + 10);
+		sprintf(rotNames->disposeName, "%s%s", rotNames->finalName,
+			(log->compress_ext
+			 && (log->flags & LOG_FLAG_COMPRESS)) ?
+			log->compress_ext : "");
+		message(MESS_DEBUG, "disposeName will be %s\n",
+			rotNames->disposeName);
+	    }
+	}
+
+	if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
+	    !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
+	    if (log->createUid == NO_UID)
+		sb.st_uid = state->sb.st_uid;
+	    else
+		sb.st_uid = log->createUid;
+
+	    if (log->createGid == NO_GID)
+		sb.st_gid = state->sb.st_gid;
+	    else
+		sb.st_gid = log->createGid;
+
+	    int have_create_mode = 0;
+	    if (log->createMode == NO_MODE)
+		sb.st_mode = state->sb.st_mode & 0777;
+	    else {
+		sb.st_mode = log->createMode;
+		have_create_mode = 1;
+	    }
+
+	    message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
+		    "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
+		    (int) sb.st_uid, (int) sb.st_gid);
+
+	    if (!debug) {
+			if (!hasErrors) {
+			fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
+						  &sb, prev_acl, have_create_mode);
+#ifdef WITH_ACL
+			if (prev_acl) {
+				acl_free(prev_acl);
+				prev_acl = NULL;
+			}
+#endif
+			if (fd < 0)
+				hasErrors = 1;
+			else {
+				close(fd);
+			}
+			}
+	    }
+	}
+
+	restoreSecCtx(&savedContext);
+
+	if (!hasErrors
+	    && log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY)
+		&& !(log->flags & LOG_FLAG_TMPFILENAME)) {
+	    hasErrors =
+		copyTruncate(log->files[logNum], rotNames->finalName,
+			     &state->sb, log->flags);
+	}
+
+#ifdef WITH_ACL
+	if (prev_acl) {
+		acl_free(prev_acl);
+		prev_acl = NULL;
+	}
+#endif /* WITH_ACL */
+		
+    }
+    return hasErrors;
+}
+
+int postrotateSingleLog(struct logInfo *log, int logNum, struct logState *state,
+			struct logNames *rotNames)
+{
+    int hasErrors = 0;
+
+    if (!state->doRotate) {
+	return 0;
+    }
+
+    if (!hasErrors && log->flags & LOG_FLAG_TMPFILENAME) {
+		char *tmpFilename = NULL;
+		if (asprintf(&tmpFilename, "%s%s", log->files[logNum], ".tmp") < 0) {
+			message(MESS_FATAL, "could not allocate tmpFilename memory\n");
+			return 1;
+		}
+	    hasErrors =
+		copyTruncate(tmpFilename, rotNames->finalName,
+			     &state->sb, LOG_FLAG_COPY);
+		message(MESS_DEBUG, "removing tmp log %s \n", tmpFilename);
+		if (!debug && !hasErrors) {
+			unlink(tmpFilename);
+		}
+	}
+
+    if (!hasErrors && (log->flags & LOG_FLAG_COMPRESS) &&
+	!(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
+	hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
+    }
+
+    if (!hasErrors && log->logAddress) {
+	char *mailFilename;
+
+	if (log->flags & LOG_FLAG_MAILFIRST)
+	    mailFilename = rotNames->firstRotated;
+	else
+	    mailFilename = rotNames->disposeName;
+
+	if (mailFilename)
+	    hasErrors =
+		mailLogWrapper(mailFilename, mailCommand, logNum, log);
+    }
+
+    if (!hasErrors && rotNames->disposeName)
+	hasErrors = removeLogFile(rotNames->disposeName, log);
+
+    restoreSecCtx((void **) &prev_context);
+    return hasErrors;
+}
+
+int rotateLogSet(struct logInfo *log, int force)
+{
+    int i, j;
+    int hasErrors = 0;
+    int logHasErrors[log->numFiles];
+    int numRotated = 0;
+    struct logState **state;
+    struct logNames **rotNames;
+
+
+    message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
+    if (force) {
+        message(MESS_DEBUG, "forced from command line ");
+    }
+    else {
+        switch (log->criterium) {
+        case ROT_HOURLY:
+        message(MESS_DEBUG, "hourly ");
+        break;
+        case ROT_DAYS:
+        message(MESS_DEBUG, "after %llu days ", log->threshhold);
+        break;
+        case ROT_WEEKLY:
+        message(MESS_DEBUG, "weekly ");
+        break;
+        case ROT_MONTHLY:
+        message(MESS_DEBUG, "monthly ");
+        break;
+        case ROT_YEARLY:
+        message(MESS_DEBUG, "yearly ");
+        break;
+        case ROT_SIZE:
+        message(MESS_DEBUG, "%llu bytes ", log->threshhold);
+        break;
+        }
+    }
+
+    if (log->rotateCount)
+	message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
+    else
+	message(MESS_DEBUG, "(no old logs will be kept)\n");
+
+    if (log->oldDir)
+	message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
+
+    if (log->flags & LOG_FLAG_IFEMPTY)
+	message(MESS_DEBUG, "empty log files are rotated, ");
+    else
+	message(MESS_DEBUG, "empty log files are not rotated, ");
+
+    if (log->minsize) 
+	message(MESS_DEBUG, "only log files >= %llu bytes are rotated, ",	log->minsize);
+
+    if (log->maxsize) 
+	message(MESS_DEBUG, "log files >= %llu are rotated earlier, ",	log->maxsize);
+
+    if (log->rotateMinAge)
+        message(MESS_DEBUG, "only log files older than %d days are rotated, ", log->rotateMinAge);
+
+    if (log->logAddress) {
+	message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
+    } else {
+	message(MESS_DEBUG, "old logs are removed\n");
+    }
+
+	if (log->numFiles == 0) {
+		message(MESS_DEBUG, "No logs found. Rotation not needed.\n");
+		return 0;
+	}
+
+	if (log->flags & LOG_FLAG_SU) {
+		if (switch_user(log->suUid, log->suGid) != 0) {
+			return 1;
+		}
+	}
+
+    for (i = 0; i < log->numFiles; i++) {
+	logHasErrors[i] = findNeedRotating(log, i, force);
+	hasErrors |= logHasErrors[i];
+
+	/* sure is a lot of findStating going on .. */
+	if ((findState(log->files[i]))->doRotate)
+	    numRotated++;
+    }
+
+    if (log->first) {
+	if (!numRotated) {
+	    message(MESS_DEBUG, "not running first action script, "
+		    "since no logs will be rotated\n");
+	} else {
+	    message(MESS_DEBUG, "running first action script\n");
+	    if (runScript(log, log->pattern, log->first)) {
+		message(MESS_ERROR, "error running first action script "
+			"for %s\n", log->pattern);
+		hasErrors = 1;
+		if (log->flags & LOG_FLAG_SU) {
+			if (switch_user_back() != 0) {
+				return 1;
+			}
+		}
+		/* finish early, firstaction failed, affects all logs in set */
+		return hasErrors;
+	    }
+	}
+    }
+
+    state = malloc(log->numFiles * sizeof(struct logState *));
+    rotNames = malloc(log->numFiles * sizeof(struct logNames *));
+
+    for (j = 0;
+	 (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
+	 || ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
+
+	for (i = j;
+	     ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
+	     || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
+	    state[i] = findState(log->files[i]);
+
+	    rotNames[i] = malloc(sizeof(struct logNames));
+	    memset(rotNames[i], 0, sizeof(struct logNames));
+
+	    logHasErrors[i] |=
+		prerotateSingleLog(log, i, state[i], rotNames[i]);
+	    hasErrors |= logHasErrors[i];
+	}
+
+	if (log->pre
+		&& (!(
+			((logHasErrors[j] || !state[j]->doRotate) && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
+			|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS))
+		))
+	) {
+	    if (!numRotated) {
+		message(MESS_DEBUG, "not running prerotate script, "
+			"since no logs will be rotated\n");
+	    } else {
+		message(MESS_DEBUG, "running prerotate script\n");
+		if (runScript(log, log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->pre)) {
+		    if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
+			message(MESS_ERROR,
+				"error running shared prerotate script "
+				"for '%s'\n", log->pattern);
+		    else {
+			message(MESS_ERROR,
+				"error running non-shared prerotate script "
+				"for %s of '%s'\n", log->files[j], log->pattern);
+		    }
+		    logHasErrors[j] = 1;
+		    hasErrors = 1;
+		}
+	    }
+	}
+
+	for (i = j;
+	     ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
+	     || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
+	    if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
+		   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
+		logHasErrors[i] |=
+		    rotateSingleLog(log, i, state[i], rotNames[i]);
+		hasErrors |= logHasErrors[i];
+	    }
+	}
+
+	if (log->post
+		&& (!(
+			((logHasErrors[j] || !state[j]->doRotate) && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
+			|| (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS))
+		))
+	) {
+	    if (!numRotated) {
+		message(MESS_DEBUG, "not running postrotate script, "
+			"since no logs were rotated\n");
+	    } else {
+		message(MESS_DEBUG, "running postrotate script\n");
+		if (runScript(log, log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], log->post)) {
+		    if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
+			message(MESS_ERROR,
+				"error running shared postrotate script "
+				"for '%s'\n", log->pattern);
+		    else {
+			message(MESS_ERROR,
+				"error running non-shared postrotate script "
+				"for %s of '%s'\n", log->files[j], log->pattern);
+		    }
+		    logHasErrors[j] = 1;
+		    hasErrors = 1;
+		}
+	    }
+	}
+
+	for (i = j;
+	     ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
+	     || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
+	    if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
+		   || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
+		logHasErrors[i] |=
+		    postrotateSingleLog(log, i, state[i], rotNames[i]);
+		hasErrors |= logHasErrors[i];
+	    }
+	}
+
+    }
+
+    for (i = 0; i < log->numFiles; i++) {
+	free(rotNames[i]->firstRotated);
+	free(rotNames[i]->disposeName);
+	free(rotNames[i]->finalName);
+	free(rotNames[i]->dirName);
+	free(rotNames[i]->baseName);
+	free(rotNames[i]);
+    }
+    free(rotNames);
+    free(state);
+
+    if (log->last) {
+	if (!numRotated) {
+	    message(MESS_DEBUG, "not running last action script, "
+		    "since no logs will be rotated\n");
+	} else {
+	    message(MESS_DEBUG, "running last action script\n");
+	    if (runScript(log, log->pattern, log->last)) {
+		message(MESS_ERROR, "error running last action script "
+			"for %s\n", log->pattern);
+		hasErrors = 1;
+	    }
+	}
+    }
+
+	if (log->flags & LOG_FLAG_SU) {
+		if (switch_user_back() != 0) {
+			return 1;
+		}
+	}
+
+    return hasErrors;
+}
+
+static int writeState(char *stateFilename)
+{
+	struct logState *p;
+	FILE *f;
+	char *chptr;
+	int i;
+	int error = 0;
+	int bytes = 0;
+	int fdcurr;
+	int fdsave;
+	struct stat sb;
+	char *tmpFilename = NULL;
+	struct tm now = *localtime(&nowSecs);
+	time_t now_time, last_time;
+	void *prevCtx;
+
+	tmpFilename = malloc(strlen(stateFilename) + 5 );
+	if (tmpFilename == NULL) {
+		message(MESS_ERROR, "could not allocate memory for "
+			"tmp state filename\n");
+		return 1;
+	}
+	strcpy(tmpFilename, stateFilename);
+	strcat(tmpFilename, ".tmp");
+	/* Remove possible tmp state file from previous run */
+	unlink(tmpFilename);
+
+
+	if ((fdcurr = open(stateFilename, O_RDONLY)) < 0) {
+	    message(MESS_ERROR, "error opening %s: %s\n", stateFilename,
+		    strerror(errno));
+		free(tmpFilename);
+	    return 1;
+	}
+
+	if (setSecCtx(fdcurr, stateFilename, &prevCtx) != 0) {
+	    /* error msg already printed */
+	    close(fdcurr);
+	    return 1;
+	}
+
+#ifdef WITH_ACL
+	if ((prev_acl = acl_get_fd(fdcurr)) == NULL) {
+		if (!ACL_NOT_WELL_SUPPORTED(errno)) {
+			message(MESS_ERROR, "getting file ACL %s: %s\n",
+				stateFilename, strerror(errno));
+			restoreSecCtx(&prevCtx);
+			close(fdcurr);
+			return 1;
+		}
+	}
+#endif
+
+	close(fdcurr);
+	stat(stateFilename, &sb);
+
+	fdsave = createOutputFile(tmpFilename, O_RDWR | O_CREAT | O_TRUNC, &sb, prev_acl, 0);
+#ifdef WITH_ACL
+	if (prev_acl) {
+		acl_free(prev_acl);
+		prev_acl = NULL;
+	}
+#endif
+	restoreSecCtx(&prevCtx);
+
+	if (fdsave < 0) {
+	    free(tmpFilename);
+	    return 1;
+	}
+
+	f = fdopen(fdsave, "w");
+	if (!f) {
+		message(MESS_ERROR, "error creating temp state file %s: %s\n",
+			tmpFilename, strerror(errno));
+		free(tmpFilename);
+		return 1;
+	}
+
+	bytes =  fprintf(f, "logrotate state -- version 2\n");
+	if (bytes < 0)
+		error = bytes;
+
+#define SECONDS_IN_YEAR 31556926
+
+	for (i = 0; i < hashSize && error == 0; i++) {
+		for (p = states[i]->head.lh_first; p != NULL && error == 0;
+				p = p->list.le_next) {
+
+			/* Skip states which are not used for more than a year. */
+			now_time = mktime(&now);
+			last_time = mktime(&p->lastRotated);
+			if (!p->isUsed && difftime(now_time, last_time) > SECONDS_IN_YEAR) {
+				message(MESS_DEBUG, "Removing %s from state file, "
+					"because it does not exist and has not been rotated for one year\n",
+					p->fn);
+				continue;
+			}
+
+			error = fputc('"', f) == EOF;
+			for (chptr = p->fn; *chptr && error == 0; chptr++) {
+				switch (*chptr) {
+				case '"':
+				case '\\':
+					error = fputc('\\', f) == EOF;
+					break;
+				case '\n':
+					error = fputc('\\', f) == EOF;
+					if (error == 0) {
+						error = fputc('n', f) == EOF;
+					}
+					continue;
+				}
+				if (error == 0 && fputc(*chptr, f) == EOF) {
+					error = 1;
+				}
+			}
+
+			if (error == 0 && fputc('"', f) == EOF)
+				error = 1;
+
+			if (error == 0) {
+				bytes = fprintf(f, " %d-%d-%d-%d:%d:%d\n",
+					p->lastRotated.tm_year + 1900,
+					p->lastRotated.tm_mon + 1,
+					p->lastRotated.tm_mday,
+					p->lastRotated.tm_hour,
+					p->lastRotated.tm_min,
+					p->lastRotated.tm_sec);
+				if (bytes < 0)
+					error = bytes;
+			}
+		}
+	}
+
+	if (error == 0)
+		error = fsync(fdsave);
+
+	if (error == 0)
+		error = fclose(f);
+	else
+		fclose(f);
+
+	if (error == 0) {
+		if (rename(tmpFilename, stateFilename)) {
+			unlink(tmpFilename);
+			error = 1;
+			message(MESS_ERROR, "error renaming temp state file %s\n",
+				tmpFilename);
+		}
+	}
+	else {
+		unlink(tmpFilename);
+		if (errno)
+			message(MESS_ERROR, "error creating temp state file %s: %s\n",
+			tmpFilename, strerror(errno));
+		else
+			message(MESS_ERROR, "error creating temp state file %s%s\n",
+				tmpFilename, error == ENOMEM ?
+				": Insufficient storage space is available." : "" );
+	}
+	free(tmpFilename);
+	return error;
+}
+
+static int readState(char *stateFilename)
+{
+    FILE *f;
+    char buf[STATEFILE_BUFFER_SIZE];
+    char *filename;
+    const char **argv;
+    int argc;
+    int year, month, day, hour, minute, second;
+    int i;
+    int line = 0;
+    int error;
+    struct logState *st;
+    time_t lr_time;
+    struct stat f_stat;
+    int rc = 0;
+
+    message(MESS_DEBUG, "Reading state from file: %s\n", stateFilename);
+
+    error = stat(stateFilename, &f_stat);
+    if (error) {
+	/* treat non-statable file as an empty file */
+	f_stat.st_size = 0;
+	if (errno != ENOENT) {
+	    message(MESS_ERROR, "error stat()ing state file %s: %s\n",
+		    stateFilename, strerror(errno));
+
+	    /* do not return until the hash table is allocated */
+	    rc = 1;
+	}
+    }
+
+    if (!debug && (f_stat.st_size == 0)) {
+	/* create the file before continuing to ensure we have write
+	   access to the file */
+	f = fopen(stateFilename, "w");
+	if (f) {
+	    fprintf(f, "logrotate state -- version 2\n");
+	    fclose(f);
+	} else {
+	    message(MESS_ERROR, "error creating state file %s: %s\n",
+		    stateFilename, strerror(errno));
+
+	    /* do not return until the hash table is allocated */
+	    rc = 1;
+	}
+    }
+
+    /* Try to estimate how many state entries we have in the state file.
+     * We expect single entry to have around 80 characters (Of course this is
+     * just an estimation). During the testing I've found out that 200 entries
+     * per single hash entry gives good mem/performance ratio. */
+    if (allocateHash(f_stat.st_size / 80 / 200))
+	return 1;
+
+    if (rc || (f_stat.st_size == 0))
+	/* error already occurred, or we have no state file to read from */
+	return rc;
+
+    f = fopen(stateFilename, "r");
+    if (!f) {
+	message(MESS_ERROR, "error opening state file %s: %s\n",
+		stateFilename, strerror(errno));
+	return 1;
+    }
+
+    if (!fgets(buf, sizeof(buf) - 1, f)) {
+	message(MESS_ERROR, "error reading top line of %s\n",
+		stateFilename);
+	fclose(f);
+	return 1;
+    }
+
+    if (strcmp(buf, "logrotate state -- version 1\n") &&
+	strcmp(buf, "logrotate state -- version 2\n")) {
+	fclose(f);
+	message(MESS_ERROR, "bad top line in state file %s\n",
+		stateFilename);
+	return 1;
+    }
+
+    line++;
+
+    while (fgets(buf, sizeof(buf) - 1, f)) {
+	argv = NULL;
+	line++;
+	i = strlen(buf);
+	if (buf[i - 1] != '\n') {
+	    message(MESS_ERROR, "line %d too long in state file %s\n",
+		    line, stateFilename);
+	    fclose(f);
+	    return 1;
+	}
+
+	buf[i - 1] = '\0';
+
+	if (i == 1)
+	    continue;
+
+	year = month = day = hour = minute = second = 0;
+	if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
+	    (sscanf(argv[1], "%d-%d-%d-%d:%d:%d", &year, &month, &day, &hour, &minute, &second) < 3)) {
+	    message(MESS_ERROR, "bad line %d in state file %s\n",
+		    line, stateFilename);
+		free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	/* Hack to hide earlier bug */
+	if ((year != 1900) && (year < 1970 || year > 2100)) {
+	    message(MESS_ERROR,
+		    "bad year %d for file %s in state file %s\n", year,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	if (month < 1 || month > 12) {
+	    message(MESS_ERROR,
+		    "bad month %d for file %s in state file %s\n", month,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	/* 0 to hide earlier bug */
+	if (day < 0 || day > 31) {
+	    message(MESS_ERROR,
+		    "bad day %d for file %s in state file %s\n", day,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	if (hour < 0 || hour > 23) {
+	    message(MESS_ERROR,
+		    "bad hour %d for file %s in state file %s\n", hour,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	if (minute < 0 || minute > 59) {
+	    message(MESS_ERROR,
+		    "bad minute %d for file %s in state file %s\n", minute,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	if (second < 0 || second > 59) {
+	    message(MESS_ERROR,
+		    "bad second %d for file %s in state file %s\n", second,
+		    argv[0], stateFilename);
+	    free(argv);
+	    fclose(f);
+	    return 1;
+	}
+
+	year -= 1900, month -= 1;
+
+	filename = strdup(argv[0]);
+	unescape(filename);
+	
+	if ((st = findState(filename)) == NULL) {
+		fclose(f);
+		return 1;
+	}
+
+	memset(&st->lastRotated, 0, sizeof(st->lastRotated));
+	st->lastRotated.tm_year = year;
+	st->lastRotated.tm_mon = month;
+	st->lastRotated.tm_mday = day;
+	st->lastRotated.tm_hour = hour;
+	st->lastRotated.tm_min = minute;
+	st->lastRotated.tm_sec = second;
+	st->lastRotated.tm_isdst = -1;
+
+	/* fill in the rest of the st->lastRotated fields */
+	lr_time = mktime(&st->lastRotated);
+	st->lastRotated = *localtime(&lr_time);
+
+	free(argv);
+	free(filename);
+    }
+
+    fclose(f);
+    return 0;
+}
+
+int main(int argc, const char **argv)
+{
+    int force = 0;
+    char *stateFile = STATEFILE;
+    char *logFile = NULL;
+    FILE *logFd = 0;
+    int rc = 0;
+    int arg;
+    const char **files;
+    poptContext optCon;
+	struct logInfo *log;
+
+    struct poptOption options[] = {
+	{"debug", 'd', 0, 0, 'd',
+	 "Don't do anything, just test (implies -v)"},
+	{"force", 'f', 0, &force, 0, "Force file rotation"},
+	{"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
+	 "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
+	 "command"},
+	{"state", 's', POPT_ARG_STRING, &stateFile, 0,
+	 "Path of state file",
+	 "statefile"},
+	{"verbose", 'v', 0, 0, 'v', "Display messages during rotation"},
+	{"log", 'l', POPT_ARG_STRING, &logFile, 'l', "Log file or 'syslog' to log to syslog"},
+	{"version", '\0', POPT_ARG_NONE, NULL, 'V', "Display version information"},
+	POPT_AUTOHELP {0, 0, 0, 0, 0}
+    };
+
+    logSetLevel(MESS_NORMAL);
+    setlocale (LC_ALL, "");
+
+    optCon = poptGetContext("logrotate", argc, argv, options, 0);
+    poptReadDefaultConfig(optCon, 1);
+    poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
+
+    while ((arg = poptGetNextOpt(optCon)) >= 0) {
+	switch (arg) {
+	case 'd':
+	    debug = 1;
+	    /* fallthrough */
+	case 'v':
+	    logSetLevel(MESS_DEBUG);
+	    break;
+	case 'l':
+		if (strcmp(logFile, "syslog") == 0) {
+			logToSyslog(1);
+		}
+		else {
+			logFd = fopen(logFile, "w");
+			if (!logFd) {
+				message(MESS_ERROR, "error opening log file %s: %s\n",
+					logFile, strerror(errno));
+				break;
+			}
+			logSetMessageFile(logFd);
+		}
+		break;
+	case 'V':
+	    fprintf(stderr, "logrotate %s\n", VERSION);
+	    poptFreeContext(optCon);
+	    exit(0);
+	}
+    }
+
+    if (arg < -1) {
+	fprintf(stderr, "logrotate: bad argument %s: %s\n",
+		poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+		poptStrerror(rc));
+	poptFreeContext(optCon);
+	return 2;
+    }
+
+    files = poptGetArgs((poptContext) optCon);
+    if (!files) {
+	fprintf(stderr, "logrotate " VERSION
+		" - Copyright (C) 1995-2001 Red Hat, Inc.\n");
+	fprintf(stderr,
+		"This may be freely redistributed under the terms of "
+		"the GNU Public License\n\n");
+	poptPrintUsage(optCon, stderr, 0);
+	poptFreeContext(optCon);
+	exit(1);
+    }
+#ifdef WITH_SELINUX
+    selinux_enabled = (is_selinux_enabled() > 0);
+    selinux_enforce = security_getenforce();
+#endif
+
+	TAILQ_INIT(&logs);
+
+	if (readAllConfigPaths(files)) {
+	poptFreeContext(optCon);
+	exit(1);
+    }
+
+    poptFreeContext(optCon);
+    nowSecs = time(NULL);
+
+    if (readState(stateFile))
+	rc = 1;
+
+    message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
+
+	for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
+		rc |= rotateLogSet(log, force);
+
+	if (!debug)
+		rc |= writeState(stateFile);
+
+	return (rc != 0);
+}
diff --git a/logrotate/logrotate.conf b/logrotate/logrotate.conf
new file mode 100644
index 0000000..f0d8cb6
--- /dev/null
+++ b/logrotate/logrotate.conf
@@ -0,0 +1,12 @@
+compress
+
+/data/misc/logd/messages {
+rotate 10
+size 1M
+notifempty
+missingok
+copytruncate
+lastaction
+    /system/bin/log_sender
+endscript
+}
diff --git a/logrotate/logrotate.conf.5 b/logrotate/logrotate.conf.5
new file mode 100644
index 0000000..7460664
--- /dev/null
+++ b/logrotate/logrotate.conf.5
@@ -0,0 +1 @@
+.so man8/logrotate.8
diff --git a/logrotate/logrotate.h b/logrotate/logrotate.h
new file mode 100644
index 0000000..81c4911
--- /dev/null
+++ b/logrotate/logrotate.h
@@ -0,0 +1,89 @@
+#ifndef H_LOGROTATE
+#define H_LOGROTATE
+
+#include <sys/types.h>
+#include "queue.h"
+#include <glob.h>
+
+#include "config.h"
+
+#define LOG_FLAG_COMPRESS	(1 << 0)
+#define LOG_FLAG_CREATE		(1 << 1)
+#define LOG_FLAG_IFEMPTY	(1 << 2)
+#define LOG_FLAG_DELAYCOMPRESS	(1 << 3)
+#define LOG_FLAG_COPYTRUNCATE	(1 << 4)
+#define LOG_FLAG_MISSINGOK	(1 << 5)
+#define LOG_FLAG_MAILFIRST	(1 << 6)
+#define LOG_FLAG_SHAREDSCRIPTS	(1 << 7)
+#define LOG_FLAG_COPY		(1 << 8)
+#define LOG_FLAG_DATEEXT	(1 << 9)
+#define LOG_FLAG_SHRED		(1 << 10)
+#define LOG_FLAG_SU			(1 << 11)
+#define LOG_FLAG_DATEYESTERDAY	(1 << 12)
+#define LOG_FLAG_OLDDIRCREATE	(1 << 13)
+#define LOG_FLAG_TMPFILENAME	(1 << 14)
+
+#define NO_MODE ((mode_t) -1)
+#define NO_UID  ((uid_t) -1)
+#define NO_GID  ((gid_t) -1)
+
+#define NO_FORCE_ROTATE 0
+#define FORCE_ROTATE    1
+
+#ifdef HAVE_LIBSELINUX
+#define WITH_SELINUX 1
+#endif
+
+#ifdef HAVE_LIBACL
+#define WITH_ACL 1
+#endif
+
+struct logInfo {
+    char *pattern;
+    char **files;
+    int numFiles;
+    char *oldDir;
+    enum { ROT_HOURLY, ROT_DAYS, ROT_WEEKLY, ROT_MONTHLY, ROT_YEARLY, ROT_SIZE
+            } criterium;
+    unsigned long long threshhold;
+    unsigned long long maxsize;
+    unsigned long long minsize;
+    int rotateCount;
+    int rotateMinAge;
+    int rotateAge;
+    int logStart;
+    char *pre, *post, *first, *last, *preremove;
+    char *logAddress;
+    char *extension;
+    char *addextension;
+    char *compress_prog;
+    char *uncompress_prog;
+    char *compress_ext;
+	char *dateformat;		/* specify format for strftime (for dateext) */
+    int flags;
+	int shred_cycles;		/* if !=0, pass -n shred_cycles to GNU shred */
+    mode_t createMode;		/* if any/all of these are -1, we use the */
+    uid_t createUid;		/* attributes from the log file just rotated */
+    gid_t createGid;
+    uid_t suUid;			/* switch user to this uid and group to this gid */
+    gid_t suGid;
+    mode_t olddirMode;
+    uid_t olddirUid;
+    uid_t olddirGid;
+    /* these are at the end so they end up nil */
+    const char **compress_options_list;
+    int compress_options_count;
+    TAILQ_ENTRY(logInfo) list;
+};
+
+TAILQ_HEAD(logInfoHead, logInfo) logs;
+
+extern int numLogs;
+extern int debug;
+
+int readAllConfigPaths(const char **paths);
+#if !defined(asprintf) && !defined(_FORTIFY_SOURCE)
+int asprintf(char **string_ptr, const char *format, ...);
+#endif
+
+#endif
diff --git a/logrotate/logrotate.rc b/logrotate/logrotate.rc
new file mode 100644
index 0000000..e7d7b3e
--- /dev/null
+++ b/logrotate/logrotate.rc
@@ -0,0 +1,14 @@
+on post-fs-data
+    mkdir /data/misc/logrotate 0770 log log
+    mkdir /data/misc/logrotate/lock 0770 log log
+    mkdir /data/misc/logrotate/run 0770 log log
+    mkdir /data/misc/logrotate/tmp 0770 log log
+
+# run each 1 min with 30 sec timeout for sending logs (logrotate will be killed after timeout)
+service logrotate /system/bin/periodic_scheduler 60 30 logrotate \
+    /data/misc/logrotate/cron-lite/ \
+    /system/bin/logrotate -v /system/etc/logrotate.conf
+    class late_start
+    user log
+    group log system inet
+    seclabel u:r:logrotate:s0
diff --git a/logrotate/logrotate.spec.in b/logrotate/logrotate.spec.in
new file mode 100644
index 0000000..aef7806
--- /dev/null
+++ b/logrotate/logrotate.spec.in
@@ -0,0 +1,56 @@
+Summary: Rotates, compresses, removes and mails system log files
+Name: logrotate
+Version: @VERSION@
+Release: 1%{?dist}
+License: GPLv2+
+Group: System Environment/Base
+Source: https://github.com/logrotate/logrotate/releases/download/%{version}/logrotate-%{version}.tar.gz
+Requires: coreutils >= 5.92 libsepol libselinux popt
+BuildRequires: libselinux-devel popt-devel
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+%{!?_licensedir:%global license %%doc}
+
+%description
+The logrotate utility is designed to simplify the administration of
+log files on a system which generates a lot of log files.  Logrotate
+allows for the automatic rotation compression, removal and mailing of
+log files.  Logrotate can be set to handle a log file daily, weekly,
+monthly or when the log file gets to a certain size.  Normally,
+logrotate runs as a daily cron job.
+
+Install the logrotate package if you need a utility to deal with the
+log files on your system.
+
+%prep
+%setup -q
+
+%build
+%configure \
+  --with-selinux
+make %{?_smp_mflags} RPM_OPT_FLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir}
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cron.daily
+mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/lib
+
+install -p -m 644 examples/logrotate-default $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.conf
+install -p -m 755 examples/logrotate.cron $RPM_BUILD_ROOT/%{_sysconfdir}/cron.daily/logrotate
+touch $RPM_BUILD_ROOT/%{_localstatedir}/lib/logrotate.status
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%license COPYING
+%attr(0755, root, root) %{_sbindir}/logrotate
+%attr(0644, root, root) %{_mandir}/man8/logrotate.8*
+%attr(0644, root, root) %{_mandir}/man5/logrotate.conf.5*
+%attr(0755, root, root) %{_sysconfdir}/cron.daily/logrotate
+%attr(0644, root, root) %config(noreplace) %{_sysconfdir}/logrotate.conf
+%attr(0755, root, root) %dir %{_sysconfdir}/logrotate.d
+%attr(0644, root, root) %verify(not size md5 mtime) %config(noreplace) %{_localstatedir}/lib/logrotate.status
diff --git a/logrotate/queue.h b/logrotate/queue.h
new file mode 100644
index 0000000..daf4553
--- /dev/null
+++ b/logrotate/queue.h
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list.  Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction.  Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define	LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	(head)->lh_first = NULL;					\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = ((head)->lh_first);				\
+		(var);							\
+		(var) = ((var)->field.le_next))
+
+/*
+ * List access methods.
+ */
+#define	LIST_EMPTY(head)		((head)->lh_first == NULL)
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+
+/*
+ * Singly-linked List definitions.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) do {						\
+	(head)->slh_first = NULL;					\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = (head)->slh_first;		\
+		while(curelm->field.sle_next != (elm))			\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)					\
+struct name {								\
+	struct type *stqh_first;	/* first element */			\
+	struct type **stqh_last;	/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_INIT(head) do {						\
+	(head)->stqh_first = NULL;					\
+	(head)->stqh_last = &(head)->stqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.stqe_next = (head)->stqh_first) == NULL)	\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(head)->stqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.stqe_next = NULL;					\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &(elm)->field.stqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+		(head)->stqh_last = &(elm)->field.stqe_next;		\
+	(listelm)->field.stqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+		(head)->stqh_last = &(head)->stqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if ((head)->stqh_first == (elm)) {				\
+		STAILQ_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->stqh_first;		\
+		while (curelm->field.stqe_next != (elm))			\
+			curelm = curelm->field.stqe_next;		\
+		if ((curelm->field.stqe_next =				\
+			curelm->field.stqe_next->field.stqe_next) == NULL) \
+			    (head)->stqh_last = &(curelm)->field.stqe_next; \
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->stqh_first);				\
+		(var);							\
+		(var) = ((var)->field.stqe_next))
+
+#define	STAILQ_CONCAT(head1, head2) do {				\
+	if (!STAILQ_EMPTY((head2))) {					\
+		*(head1)->stqh_last = (head2)->stqh_first;		\
+		(head1)->stqh_last = (head2)->stqh_last;		\
+		STAILQ_INIT((head2));					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+
+/*
+ * Simple queue definitions.
+ */
+#define	SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define	SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define	SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_REMOVE(head, elm, type, field) do {			\
+	if ((head)->sqh_first == (elm)) {				\
+		SIMPLEQ_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->sqh_first;		\
+		while (curelm->field.sqe_next != (elm))			\
+			curelm = curelm->field.sqe_next;		\
+		if ((curelm->field.sqe_next =				\
+			curelm->field.sqe_next->field.sqe_next) == NULL) \
+			    (head)->sqh_last = &(curelm)->field.sqe_next; \
+	}								\
+} while (/*CONSTCOND*/0)
+
+#define	SIMPLEQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->sqh_first);				\
+		(var);							\
+		(var) = ((var)->field.sqe_next))
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_EMPTY(head)		((head)->sqh_first == NULL)
+#define	SIMPLEQ_FIRST(head)		((head)->sqh_first)
+#define	SIMPLEQ_NEXT(elm, field)	((elm)->field.sqe_next)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define	_TAILQ_HEAD(name, type, qual)					\
+struct name {								\
+	qual type *tqh_first;		/* first element */		\
+	qual type *qual *tqh_last;	/* addr of last next element */	\
+}
+#define TAILQ_HEAD(name, type)	_TAILQ_HEAD(name, struct type,)
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define	_TAILQ_ENTRY(type, qual)					\
+struct {								\
+	qual type *tqe_next;		/* next element */		\
+	qual type *qual *tqe_prev;	/* address of previous next element */\
+}
+#define TAILQ_ENTRY(type)	_TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+} while (/*CONSTCOND*/0)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = ((head)->tqh_first);				\
+		(var);							\
+		(var) = ((var)->field.tqe_next))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));	\
+		(var);							\
+		(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define	TAILQ_CONCAT(head1, head2, field) do {				\
+	if (!TAILQ_EMPTY(head2)) {					\
+		*(head1)->tqh_last = (head2)->tqh_first;		\
+		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
+		(head1)->tqh_last = (head2)->tqh_last;			\
+		TAILQ_INIT((head2));					\
+	}								\
+} while (/*CONSTCOND*/0)
+
+/*
+ * Tail queue access methods.
+ */
+#define	TAILQ_EMPTY(head)		((head)->tqh_first == NULL)
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+
+#define	TAILQ_LAST(head, headname) \
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define	TAILQ_PREV(elm, headname, field) \
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Circular queue definitions.
+ */
+#define	CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define	CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ (void *)&head, (void *)&head }
+
+#define	CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+} while (/*CONSTCOND*/0)
+
+#define	CIRCLEQ_FOREACH(var, head, field)				\
+	for ((var) = ((head)->cqh_first);				\
+		(var) != (const void *)(head);				\
+		(var) = ((var)->field.cqe_next))
+
+#define	CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for ((var) = ((head)->cqh_last);				\
+		(var) != (const void *)(head);				\
+		(var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define	CIRCLEQ_EMPTY(head)		((head)->cqh_first == (void *)(head))
+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field)				\
+	(((elm)->field.cqe_next == (void *)(head))			\
+	    ? ((head)->cqh_first)					\
+	    : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field)				\
+	(((elm)->field.cqe_prev == (void *)(head))			\
+	    ? ((head)->cqh_last)					\
+	    : (elm->field.cqe_prev))
+
+#endif	/* sys/queue.h */
diff --git a/logrotate/test/Makefile.am b/logrotate/test/Makefile.am
new file mode 100644
index 0000000..3842451
--- /dev/null
+++ b/logrotate/test/Makefile.am
@@ -0,0 +1,17 @@
+EXTRA_DIST = \
+	compress \
+	compress-error \
+	mailer \
+	test \
+	$(top_srcdir)/test/test-config.*.in
+
+DISTCLEANFILES = \
+	test.ACL \
+	test.SELINUX \
+	test.example
+
+TESTS_ENVIRONMENT = \
+	test $(top_srcdir) = $(top_builddir) || ln -fs $(top_srcdir)/test/* . ; \
+	export LOGROTATE=$(top_builddir)/logrotate ;
+
+TESTS = test
diff --git a/logrotate/test/compress b/logrotate/test/compress
new file mode 100755
index 0000000..0eb626d
--- /dev/null
+++ b/logrotate/test/compress
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+echo "gzip $*" > compress-args
+env > compress-env
+gzip $*
\ No newline at end of file
diff --git a/logrotate/test/compress-error b/logrotate/test/compress-error
new file mode 100755
index 0000000..d0d09d9
--- /dev/null
+++ b/logrotate/test/compress-error
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+echo "compression error" 1>&2
+gzip $*
\ No newline at end of file
diff --git a/logrotate/test/mailer b/logrotate/test/mailer
new file mode 100755
index 0000000..73a72fa
--- /dev/null
+++ b/logrotate/test/mailer
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+echo "$*" > mail-out
+cat >> mail-out
diff --git a/logrotate/test/test b/logrotate/test/test
new file mode 100755
index 0000000..1ac7a2e
--- /dev/null
+++ b/logrotate/test/test
@@ -0,0 +1,1867 @@
+#!/bin/bash
+
+ACL_TESTS=1
+SELINUX_TESTS=0
+SYSLOG_TESTS=0
+M="-m ./mailer"
+S="-s state"
+RLR="$LOGROTATE $M $S"
+
+# -- ACL - BEGIN --------------------------------
+# We test if the ACLs tests should be done or not
+echo 1 > test.x
+setfacl -m u:nobody:rwx test.x 2>/dev/null
+if [ $? != 0 ]; then
+	ACL_TESTS=0
+	echo "setfacl failed on this system. ACL tests will not be executed."
+fi
+rm -f test.x
+if [ $ACL_TESTS = 1 ]; then
+  # It seems we can run the ACL tests, but was logrotate compiled WITH_ACL=yes ?
+  # See the Makefile, "pretest" part, for more information
+  if [ -f ./test.ACL ]; then
+    ACL_TESTS=`cat ./test.ACL`
+    if [ $ACL_TESTS = 0 ]; then
+      echo "logrotate was NOT compiled with 'WITH_ACL=yes'. ACL tests will not be executed."
+    fi
+  fi
+fi
+# -- ACL - END ----------------------------------
+
+# -- SELINUX - BEGIN --------------------------------
+# We test if the ACLs tests should be done or not
+if type "selinuxenabled" >/dev/null 2>&1 && selinuxenabled; then
+	SELINUX_TESTS=1
+else
+	echo "SELinux disabled. SELinux tests will not be executed."
+fi
+
+if [ $SELINUX_TESTS = 1 ]; then
+  # It seems we can run the ACL tests, but was logrotate compiled WITH_ACL=yes ?
+  # See the Makefile, "pretest" part, for more information
+  if [ -f ./test.SELINUX ]; then
+    SELINUX_TESTS=`cat ./test.SELINUX`
+    if [ $SELINUX_TESTS = 0 ]; then
+      echo "logrotate was NOT compiled with 'WITH_SELINUX=yes'. SELINUX tests will not be executed."
+    fi
+  fi
+fi
+
+if [ $SELINUX_TESTS = 1 ]; then
+  # if logrotate_tmp_t, we can't continue with SELinux tests...
+  touch .selinuxtest
+  chcon --type=logrotate_tmp_t .selinuxtest 2>/dev/null
+  if [ $? != 0 ]; then
+    SELINUX_TESTS=0
+    echo "SELinux context 'logrotate_tmp_t' does not exist. SELinux tests will not be executed."
+  fi
+  rm -f .selinuxtest
+fi
+# -- SELINUX - END ----------------------------------
+
+# -- SYSLOG - BEGIN
+logger syslog_test 2>/dev/null
+if [ $? = 0 ]; then
+	journalctl -n 50 2>/dev/null | grep syslog_test 2>/dev/null >/dev/null
+	if [ $? = 0 ]; then
+		SYSLOG_TESTS=1
+	fi
+fi
+# -- SYSLOG - END
+
+cleanup() {
+    rm -f test*.log* anothertest*.log* state test-config. scriptout mail-out compress-args compress-env different*.log*
+    rm -f $(ls | egrep '^test-config.[0-9]+$')
+
+    [ -n "$1" ] && echo "Running test $1"
+    return 0
+}
+
+genconfig() {
+    input=test-config.$1.in
+    output=test-config.$1
+    user=$(id -u -n)
+    group=$(id -g -n)
+    sed "s,&DIR&,$PWD,g" < $input | sed "s,&USER&,$user,g" | sed "s,&GROUP&,$group,g" > $output
+    config_crc=$(md5sum $output)
+}
+
+createlog() {
+    num=$1
+    file=$2
+    cl_compressed=$3
+
+    case $num in
+	0)
+	    what=zero
+	    ;;
+	1)
+	    what=first
+	    ;;
+	2)
+	    what=second
+	    ;;
+	3)
+	    what=third
+	    ;;
+	4)
+	    what=fourth
+	    ;;
+	5)
+	    what=fifth
+	    ;;
+	6)
+	    what=sixth
+	    ;;
+	7)
+	    what=seventh
+	    ;;
+	8)
+	    what=eight
+	    ;;
+	9)
+	    what=ninth
+	    ;;
+	*)
+	    exit 1
+	    ;;
+    esac
+
+    echo $what > $file
+    [ -n "$cl_compressed" ] && gzip -9 $file
+}
+
+createlogs() {
+    base=$1
+    numlogs=$2
+    cls_compressed=$3
+
+    rm -f ${base}*
+
+    num=0
+    while [ $num != $numlogs ]; do
+	if [ $num = 0 ]; then
+	    createlog 0 $base
+	else
+	    createlog $num ${base}.$num $cls_compressed
+	fi
+
+	num=`expr $num + 1`
+    done
+}
+
+checkmail() {
+    (echo -s $PWD/$1 user@myhost.org; echo $2) | diff -u - mail-out
+    if [ $? != 0 ]; then
+        exit 5
+    fi
+}
+
+checkoutput() {
+    while read line; do
+	set $line
+	file=$1
+	co_compressed=$2
+	if [ "$#" -le 2 ]; then
+		shift $#
+	else
+		shift 2
+	fi
+
+	fileother=`echo $line | awk '{print $1}'`
+	expected=`echo $line | cut -s -d\  -f3-`
+
+	if [ $file != $fileother ]; then
+	    echo "unexpected file $file'" >&2
+	    exit 2
+	fi
+
+	if [ ! -f $file ]; then
+	    echo "file $file does not exist"
+	fi
+
+	if [ -n "$co_compressed" ] && [ "$co_compressed" != 0 ]; then
+		contents=`gunzip -c $file`
+	else
+		contents=`cat $file | tr -d '\000'`
+	fi
+	if [ "$contents" != "$expected" ]; then
+	    echo "file $file does not contain expected results (compressed $co_compressed, args $*)" >&2
+	    echo contains: \'$contents\'
+	    echo expected: \'$expected\'
+	    exit 2
+	fi
+	echo "$config_crc" | md5sum -c - 2>&1 > /dev/null
+	if [ $? != 0 ]; then
+		echo "config file $output has been altered: MD5 sum mismatch"
+		exit 3
+	fi
+    done
+}
+
+preptest() {
+    base=$1
+    confignum=$2
+    numlogs=$3
+    pt_compressed=$4
+
+    rm -f $base*
+    rm -f state
+
+    genconfig $confignum
+    createlogs $base $numlogs $pt_compressed
+}
+
+# we don't want any stuff left from previous runs
+cleanup 1
+
+# ------------------------------- Test 1 -------------------------------------
+# Without a log file, no rotations should occur
+preptest test.log 1 2
+$RLR test-config.1 
+
+checkoutput <<EOF
+test.log 0 zero
+test.log.1 0 first
+EOF
+
+# Put in place a state file that will force a rotation
+cat > state <<EOF
+logrotate state -- version 1
+"$PWD/test.log" 2000-1-1
+EOF
+
+# Now force the rotation
+$RLR test-config.1
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test.log.2 0 first
+EOF
+
+# rerun it to make sure nothing happens
+$RLR test-config.1 
+
+checkoutput <<EOF
+test.log
+test.log.1 0 zero
+test.log.2 0 first
+EOF
+
+cleanup 2
+
+# ------------------------------- Test 2 -------------------------------------
+preptest test.log 2 3
+$RLR test-config.2 --force
+
+checkoutput <<EOF
+test.log.1 0 zero
+test.log.2 0 first
+EOF
+
+checkmail test.log.3 second
+
+if [ -f test.log ]; then
+    echo "erroneously created test.log"
+fi
+
+cleanup 3
+
+# ------------------------------- Test 3 -------------------------------------
+
+preptest test.log 3 1
+$RLR test-config.3 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+scriptout 0 foo
+EOF
+
+cleanup
+
+preptest test.log 3 1
+preptest test2.log 3 1
+$RLR test-config.3 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0
+test2.log.1 0 zero
+scriptout 0 foo foo
+EOF
+
+cleanup 4
+
+# ------------------------------- Test 4 -------------------------------------
+preptest test.log 4 1
+preptest test2.log 4 1
+$RLR test-config.4 --force 
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0
+test2.log.1 0 zero
+scriptout 0 foo
+EOF
+
+cleanup 5
+
+# ------------------------------- Test 5 -------------------------------------
+preptest test.log 5 1
+preptest anothertest.log 5 1
+$RLR test-config.5 --force 
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+anothertest.log 0
+anothertest.log.1 0 zero
+scriptout 0 foo
+EOF
+
+cleanup 6
+
+# ------------------------------- Test 6 -------------------------------------
+preptest test.log 6 1
+preptest anothertest.log 6 1
+if [ $SELINUX_TESTS = 1 ]; then
+	chcon --type=logrotate_tmp_t test.log
+else
+	echo "Skipping SELinux part of test 6"
+fi
+$RLR test-config.6 --force
+
+if [ $SELINUX_TESTS = 1 ]; then
+	ls -Z test.log.0|grep logrotate_tmp_t >/dev/null
+	if [ $? != 0 ]; then
+		echo "test.log.0 should have selinux context logrotate_tmp_t."
+		exit 3
+	fi
+
+	ls -Z anothertest.log.0|grep logrotate_tmp_t >/dev/null
+	if [ $? = 0 ]; then
+		echo "anothertest.log.0 should not have selinux context logrotate_tmp_t."
+		exit 3
+	fi
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.0 0 zero
+anothertest.log 0
+anothertest.log.0 0 zero
+scriptout 0 foo
+EOF
+
+cleanup 7
+
+# ------------------------------- Test 7 -------------------------------------
+preptest test.log 7 1
+preptest anothertest.log 7 1
+
+$RLR test-config.7 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.6 0 zero
+anothertest.log 0
+anothertest.log.6 0 zero
+scriptout 0 foo
+EOF
+
+cleanup 8
+
+# ------------------------------- Test 8 -------------------------------------
+preptest test.log 8 1 1
+$RLR test-config.8 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1.gz 1 zero
+scriptout 0 foo
+EOF
+
+checkmail test.log zero
+
+cleanup 9
+
+# ------------------------------- Test 9 -------------------------------------
+preptest test.log 9 1 1
+$RLR test-config.9 --force
+
+checkoutput <<EOF
+test.log 0
+scriptout 0 foo
+EOF
+
+checkmail test.log zero
+
+cleanup 10
+
+# ------------------------------- Test 10 ------------------------------------
+preptest test.log 10 1
+
+if [ $SELINUX_TESTS = 1 ]; then
+	chcon --type=logrotate_tmp_t test.log
+else
+	echo "Skipping SELinux part of test 10"
+fi
+
+$RLR test-config.10 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+echo "newfile" > test.log
+
+$RLR test-config.10 --force
+
+if [ $SELINUX_TESTS = 1 ]; then
+	ls -Z test.log.2.gz|grep logrotate_tmp_t >/dev/null
+	if [ $? != 0 ]; then
+		echo "test.log.2.gz should have selinux context logrotate_tmp_t."
+		ls -Z test.log.2.gz
+		exit 3
+	fi
+
+	ls -Z test.log.1|grep logrotate_tmp_t >/dev/null
+	if [ $? != 0 ]; then
+		echo "test.log.1 should have selinux context logrotate_tmp_t."
+		ls -Z test.log.1
+		exit 3
+	fi
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 newfile
+test.log.2.gz 1 zero
+EOF
+
+checkmail test.log.1 newfile
+
+cleanup 11
+
+# ------------------------------- Test 11 ------------------------------------
+preptest test.log 11 2 1
+$RLR test-config.11 --force
+
+checkoutput <<EOF
+test.log 0
+scriptout 0 foo
+EOF
+
+checkmail test.log.2.gz first
+
+# check rotation into a directory given as a relative pathname
+cleanup 12
+
+# ------------------------------- Test 12 ------------------------------------
+preptest test.log 12 1 0
+rm -rf testdir
+mkdir testdir
+$RLR test-config.12 --force
+
+checkoutput <<EOF
+test.log 0
+testdir/test.log.1 0 zero
+EOF
+
+rm -rf testdir
+
+# check rotation into a directory given as an absolute  pathname
+cleanup 13
+
+# ------------------------------- Test 13 ------------------------------------
+preptest test.log 13 1 0
+rm -rf testdir
+$RLR test-config.13 --force
+
+ls -l|grep testdir|grep "drwx------." 2>/dev/null >/dev/null
+if [ $? != 0 ]; then
+	echo "testdir should have mode 2700, but it has:"
+	ls -l|grep testdir
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+testdir/test.log.1 0 zero
+EOF
+
+rm -rf testdir
+
+# sanity rotation check using dateext and dateformat
+cleanup 14
+
+# ------------------------------- Test 14 ------------------------------------
+preptest test.log 14 1 0
+
+$RLR test-config.14 --force
+
+DATESTRING=$(/bin/date +%Y-%m-%d)
+
+checkoutput <<EOF
+test.log 0
+test.log.$DATESTRING 0 zero
+EOF
+
+rm -rf testdir
+
+# shred test
+cleanup 15
+
+# ------------------------------- Test 15 ------------------------------------
+preptest test.log 15 1 0
+$RLR test-config.15 --force
+
+# this rotation should use shred
+$RLR test-config.15 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0
+EOF
+
+cleanup 16
+
+# ------------------------------- Test 16 ------------------------------------
+preptest test.log 16 1 0
+# log with 1 byte should not be rotated
+echo "a" > test.log
+$RLR test-config.16
+
+if [ -f test.log.1 ]; then
+	echo "file $file does exist!"
+	exit 2
+fi
+
+# log with 4 bytes should be rotated
+echo "zero" > test.log
+$RLR test-config.16
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 17
+
+# ------------------------------- Test 17 ------------------------------------
+preptest test.log 17 1 0
+# log with 1 byte should not be rotated
+$RLR test-config.17 -l logrotate.log 2>error.log
+
+grep "unexpected } (missing previous '{')" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm error.log
+
+grep "reading config file test-config.17" logrotate.log >/dev/null
+if [ $? != 0 ]; then
+	echo "There is no log output in logrotate.log"
+	exit 3
+fi
+
+rm -f logrotate.log
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+cleanup 18
+
+# ------------------------------- Test 18 ------------------------------------
+preptest test.log 18 1
+$RLR test-config.18 -l syslog --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1.gz 1 zero
+EOF
+
+(echo "gzip -f -9") | diff -u - compress-args
+egrep -q '^LOGROTATE_COMPRESSED_FILENAME=.+/test/test.log.1$' compress-env
+if [ $? != 0 ]; then
+      echo "LOGROTATE_COMPRESSED_FILENAME environment variable not found."
+      cat compress-env++      exit 3
+fi
+
+if [ $SYSLOG_TESTS = 1 ]; then
+	journalctl -n 50 2>/dev/null|grep $PWD/test.log.1 2>/dev/null >/dev/null
+	if [ $? != 0 ]; then
+		echo "syslog message not found"
+		exit 1
+	fi
+fi
+
+cleanup 19
+
+# ------------------------------- Test 19 ------------------------------------
+preptest test.log 19 1
+$RLR test-config.19 --force 2>error.log
+if [ $? = 0 ]; then
+	echo "Logrotate exited with 0 exit code, but it should not"
+fi
+
+grep "error running non-shared postrotate script for" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+cleanup 20
+
+# ------------------------------- Test 20 ------------------------------------
+preptest test.log 20 1
+$RLR test-config.20 --force 2>error.log
+
+if [ $? = 0 ]; then
+	echo "Logrotate exited with 0 exit code, but it should not"
+fi
+
+grep "error running shared postrotate script for" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+cleanup 21
+
+# ------------------------------- Test 21 ------------------------------------
+# different base name, so it should not find the file
+preptest differenttest.log 21 1
+$RLR test-config.21 --force 2>error.log
+
+if [ $? != 0 ]; then
+	echo "Logrotate exited with non-zero exit code, but it should not"
+fi
+
+cat error.log
+
+# grep "error running shared postrotate script for" error.log >/dev/null
+# if [ $? != 0 ]; then
+# 	echo "No error printed, but there should be one."
+# 	exit 3
+# fi
+
+cleanup 22
+
+# ------------------------------- Test 22 ------------------------------------
+# different base name, so it should not find the file
+preptest differenttest.log 22 1
+$RLR test-config.22 --force 2>error.log
+
+if [ $? = 0 ]; then
+	echo "Logrotate exited with zero exit code, but it should not"
+fi
+
+grep "error: stat of" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+cleanup 23
+
+# ------------------------------- Test 23 ------------------------------------
+# symlinks - symlinks rotation is not allowed for security reasons.
+preptest test.log.original 23 1
+ln -s test.log.original test.log
+$RLR test-config.23 --force 2>error.log 
+
+checkoutput <<EOF
+test.log 0 zero
+test.log.original 0 zero
+EOF
+
+rm -f test.log 2>/dev/null || true
+
+cleanup 24
+
+# ------------------------------- Test 24 ------------------------------------
+# symlinks 2 - now copytruncate is used, but symlinks rotation is not allowed for
+# security reasons.
+# since logrotate-3.8.2, we don't support symlinks rotation officially.
+preptest test.log.original 24 1
+ln -s test.log.original test.log
+$RLR test-config.24 --force 2>error.log
+
+checkoutput <<EOF
+test.log 0 zero
+test.log.original 0 zero
+EOF
+
+rm -f test.log 2>/dev/null || true
+
+cleanup 25
+
+# ------------------------------- Test 25 ------------------------------------
+# If there is no '{' character after log files definition, error should be printed
+# and config file should be skipped
+
+preptest test.log 25 1 0
+# log with 1 byte should not be rotated
+$RLR test-config.25 2>error.log
+
+grep "missing '{' after log files definition" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm error.log
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+cleanup 26
+
+# ------------------------------- Test 26 ------------------------------------
+# If there is error in config file, log should not be rotated and original log
+# should be untouched
+
+preptest test.log 26 1 0
+# log with 1 byte should not be rotated
+$RLR test-config.26 2>error.log
+
+grep "unknown option" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm error.log
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 27
+
+# ------------------------------- Test 27 ------------------------------------
+# logrotate fails to find the correct file to mail, when using "mailfirst" in
+# combination with "delaycompress" and "dateext" option.
+preptest test.log 27 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+
+$RLR test-config.27 --force
+checkoutput <<EOF
+test.log 0
+test.log-$DATESTRING 0 zero
+EOF
+
+checkmail test.log-$DATESTRING zero
+
+
+cleanup 28
+
+# ------------------------------- Test 28 ------------------------------------
+# { on new line
+
+preptest test.log 28 1 0
+# log with 1 byte should not be rotated
+$RLR test-config.28
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 29
+
+# ------------------------------- Test 29 ------------------------------------
+# { } on the same line
+
+preptest test.log 29 1 0
+# log with 1 byte should not be rotated
+$RLR test-config.29 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 30
+
+# ------------------------------- Test 30 ------------------------------------
+# the file with the same date already exists, so it should not be overwritten
+# and log should not be rotated
+preptest test.log 30 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+echo "one" > test.log-$DATESTRING
+
+$RLR test-config.30 --force
+checkoutput <<EOF
+test.log 0 zero
+test.log-$DATESTRING 0 one
+EOF
+
+cleanup 31
+
+# ------------------------------- Test 31 ------------------------------------
+# Test mode in create option
+preptest test.log 31 1 0
+
+$RLR test-config.31 --force
+
+stat -c %f test.log|grep 8180 >/dev/null
+if [ $? != 0 ]; then
+	echo "Bad mode of test.log, should be 0600"
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+if [ $ACL_TESTS = 0 ]; then
+  echo "Skipping test 32: no ACL support"
+else
+
+cleanup 32
+
+# ------------------------------- Test 32 ------------------------------------
+# Without mode in 'create' directive, ACLs should be respected.
+# Also check that chmod is respected when setting ACLs
+preptest test.log 32 1 0
+
+chmod 600 test.log
+setfacl -m u:nobody:rwx test.log
+
+$RLR test-config.32 --force
+getfacl test.log|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log must have user:nobody:rwx ACL"
+	getfacl test.log
+	exit 3
+fi
+
+getfacl test.log|grep "group::---" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log must have group::--- ACL"
+	getfacl test.log
+	exit 3
+fi
+
+getfacl test.log.1|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log.1 must have user:nobody:rwx ACL"
+	getfacl test.log.1
+	exit 3
+fi
+
+getfacl test.log.1|grep "group::---" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log.1 must have group::--- ACL"
+	getfacl test.log.1
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+fi
+
+if [ $ACL_TESTS = 0 ]; then
+  echo "Skipping test 33: no ACL support"
+else
+
+cleanup 33
+
+# ------------------------------- Test 33 ------------------------------------
+# With mode in 'create' directive, ACLs are overwriten by chmod
+preptest test.log 33 1 0
+
+
+setfacl -m u:nobody:rwx test.log
+$RLR test-config.33 --force
+
+getfacl test.log|grep "user:nobody:rwx" >/dev/null
+if [ $? = 0 ]; then
+	echo "test.log must not contain user:nobody:rwx"
+	exit 3
+fi
+
+getfacl test.log.1|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log.1 must contain user:nobody:rwx"
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+fi
+
+cleanup 34
+
+# ------------------------------- Test 34 ------------------------------------
+# We support changing user/mode without setting mode in create directive now
+# We can't change user/group as normal user, so this test uses debug mode and
+# checks the logrotate -d output.
+preptest test.log 34 1 0
+
+$RLR test-config.34 -d -f 2>&1|grep "uid = 0 gid = 0" > /dev/null
+
+if [ $? != 0 ]; then
+	echo "logrotate output must contain 'uid = 0 gid = 0'"
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+if [ $ACL_TESTS = 0 ]; then
+  echo "Skipping test 35: no ACL support"
+else
+
+cleanup 35
+
+# ------------------------------- Test 35 ------------------------------------
+# Test 'create' directive without mode but with user/group with ACLs. ACLs should
+# be respected.
+preptest test.log 35 1 0
+
+setfacl -m u:nobody:rwx test.log
+$RLR test-config.35 --force
+
+getfacl test.log|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log must contain user:nobody:rwx"
+	getfacl test.log
+	exit 3
+fi
+
+getfacl test.log.1|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "test.log.1 must contain user:nobody:rwx"
+	getfacl test.log.1
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+fi
+
+cleanup 36
+
+# ------------------------------- Test 36 ------------------------------------
+# size 1x - 'x' is unknown unit, config should be skipped
+preptest test.log 36 1 0
+
+$RLR test-config.36 --force 2>error.log
+
+grep "unknown unit" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error 'unknown unit' printed, but there should be one."
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+cleanup 37
+
+# ------------------------------- Test 37 ------------------------------------
+# skip config with firstaction script
+preptest test.log 37 1 0
+
+$RLR test-config.37 --force 2>error.log
+
+grep "skipping" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error 'skipping' printed, but there should be one."
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+scriptout 0 second
+EOF
+
+cleanup 38
+
+# ------------------------------- Test 38 ------------------------------------
+# preremove script
+preptest test.log 38 1
+preptest test2.log 38 1
+$RLR test-config.38 --force 
+
+# Check both possible orders
+grep "test2.log.1test.log.1" scriptout >/dev/null
+if [ $? != 0 ]; then
+	grep "test.log.1test2.log.1" scriptout >/dev/null
+	if [ $? != 0 ]; then
+		echo "ERROR: scriptout should contain 'test2.log.1test.log.1' or 'test.log.1test2.log.1'"
+		exit 3
+	fi
+fi
+
+rm -f scriptout
+
+checkoutput <<EOF
+test.log 0
+test2.log 0
+EOF
+
+cleanup 39
+
+# ------------------------------- Test 39 ------------------------------------
+# preremove script error - do not remove log file
+preptest test.log 39 1
+preptest test2.log 39 1
+$RLR test-config.39 --force 2>error.log
+
+grep "error running preremove script" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error 'error running preremove script' printed, but there should be one."
+	exit 3
+fi
+
+# Check both possible orders
+grep "test2.log.1" scriptout >/dev/null
+if [ $? != 0 ]; then
+	grep "test.log.1" scriptout >/dev/null
+	if [ $? != 0 ]; then
+		echo "ERROR: scriptout should contain 'test2.log.1' or 'test.log.1'"
+		exit 3
+	fi
+fi
+
+rm -f scriptout
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0
+test2.log.1 0 zero
+EOF
+
+cleanup 40
+
+# ------------------------------- Test 40 ------------------------------------
+# test tabooext parsing and implementation, config.v and config.x should not be
+# loaded.
+preptest test.log 40 1
+mkdir -p testingdir
+echo 1 > ./testingdir/config.v
+echo 2 > ./testingdir/config.x
+
+$RLR test-config.40 --force
+
+rm -rf testingdir
+
+cleanup 41
+
+# ------------------------------- Test 41 ------------------------------------
+# Test that prerotate and postrotate scripts are called only when the log files
+# are actually rotated
+preptest test.log 41 1
+echo x > test2.log
+
+$RLR test-config.41
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0 x
+scriptout 0 test.log;test.log;
+EOF
+
+cleanup 42
+
+# ------------------------------- Test 42 ------------------------------------
+# Test that script is called only once when sharedscripts is defined
+preptest test.log 42 1
+echo number2 > test2.log
+
+$RLR test-config.42
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0
+test2.log.1 0 number2
+scriptout 0 "test*.log ;test*.log ;"
+EOF
+
+cleanup 43
+
+# ------------------------------- Test 43 ------------------------------------
+# Test that prerotate and postrotate scripts are called twice for two files
+# when sharedscripts defined
+preptest test.log 43 1
+echo number2 > test2.log
+
+$RLR test-config.43
+
+# Check both possible orders
+grep "test2.log;test2.log;test.log;test.log;" scriptout >/dev/null
+if [ $? != 0 ]; then
+	grep "test.log;test.log;test2.log;test2.log;" scriptout >/dev/null
+	if [ $? != 0 ]; then
+		echo "ERROR: scriptout should contain 'test2.log;test2.log;test.log;test.log;' or 'test.log;test.log;test2.log;test2.log;'"
+		exit 3
+	fi
+fi
+
+rm -f scriptout
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test2.log 0
+test2.log.1 0 number2
+EOF
+
+cleanup 44
+
+# ------------------------------- Test 44 ------------------------------------
+# Test that prerotate and postrotate scripts are called once when nosharedscripts
+# is defined and one rotation fails
+preptest test.log 44 1
+
+$RLR test-config.44 2>error.log
+
+grep "error: stat of" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm -f error.log
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+scriptout 0 "test.log;test.log;"
+EOF
+
+cleanup 45
+
+# ------------------------------- Test 45 ------------------------------------
+# Test that prerotate and postrotate scripts are not called when sharedscripts
+# is defined and one rotation fails
+preptest test.log 45 1
+
+touch scriptout
+$RLR test-config.45 2>error.log
+
+grep "error: stat of" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm -f error.log
+
+checkoutput <<EOF
+test.log 0 zero
+scriptout 0 
+EOF
+
+cleanup 46
+
+# ------------------------------- Test 46 ------------------------------------
+# the state file is truncated and obviously corrupt
+preptest test.log 46 1
+
+cat > state << EOF
+logrotate state -- version 1
+"$PWD/test.log" 2000-1-1
+"$PWD/test2.l
+EOF
+
+DATESTRING=$(/bin/date +%Y%m%d)
+$RLR test-config.46 2>error.log
+
+grep "error: bad line 3 in state file state" error.log >/dev/null
+if [ $? != 0 ]; then
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm -f error.log
+
+checkoutput <<EOF
+test.log 0
+EOF
+
+
+if [ $SELINUX_TESTS = 1 ]; then
+	chcon --type=logrotate_tmp_t test.log
+else
+	echo "Skipping SELinux part of test 46"
+fi
+
+cleanup 47
+
+if [ $SELINUX_TESTS = 1 ]; then
+
+# ------------------------------- Test 47 ------------------------------------
+# test that newly created state file has the same SELinux context as the
+# previous one
+preptest test.log 47 1
+
+cat > state << EOF
+logrotate state -- version 2
+EOF
+
+chcon --type=logrotate_tmp_t state
+
+$RLR test-config.47
+
+ls -Z state|grep logrotate_tmp_t >/dev/null
+if [ $? != 0 ]; then
+	echo "state file should have selinux context logrotate_tmp_t."
+	exit 3
+fi
+
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+else
+	echo "Skipping SELinux test 47"
+fi
+
+cleanup 48
+
+if [ $ACL_TESTS = 0 ]; then
+  echo "Skipping test 48: no ACL support"
+else
+
+# ------------------------------- Test 48 ------------------------------------
+# Test that state file keeps the set ACLs
+preptest test.log 48 1 0
+
+cat > state << EOF
+logrotate state -- version 2
+EOF
+
+setfacl -m u:nobody:rwx state
+
+$RLR test-config.48
+
+getfacl state|grep "user:nobody:rwx" >/dev/null
+if [ $? != 0 ]; then
+	echo "state file must have acls user:nobody:rwx"
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+fi
+
+cleanup 49
+
+# ------------------------------- Test 49 ------------------------------------
+# Test that state files without hours/minutes/seconds still works properly
+preptest test.log 49 1 0
+
+cat > state << EOF
+logrotate state -- version 2
+"test.log" 2012-8-19
+EOF
+
+$RLR test-config.49
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 50
+
+# ------------------------------- Test 50 ------------------------------------
+# test that hourly rotation works properly
+preptest test.log 50 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d%H)
+NOW=$(/bin/date "+%Y-%-m-%-d-%-H" 2>/dev/null)
+HOURAGO=$(/bin/date "+%Y-%-m-%-d-%-H" --date "1 hour ago" 2>/dev/null)
+GNUDATE=$?
+
+# --force to trigger rotation
+$RLR test-config.50 --force
+checkoutput <<EOF
+test.log 0
+test.log-$DATESTRING 0 zero
+EOF
+
+# It should not rotate this hour again
+echo second > test.log
+rm -f test.log-$DATESTRING
+$RLR test-config.50
+checkoutput <<EOF
+test.log 0 second
+EOF
+
+if [ -f test.log.1 ]; then
+    echo "file $file does exist!"
+    exit 2
+fi
+
+if [ $GNUDATE = 0 ]; then
+# Simulate previous rotation by editing state file. This should overwrite
+# our previously rotated log
+sed -i "s,$NOW,$HOURAGO,g" state
+$RLR test-config.50
+checkoutput <<EOF
+test.log 0
+test.log-$DATESTRING 0 second
+EOF
+else
+echo "Does not have GNU Date, skipping part of this test"
+fi
+
+cleanup 51
+
+# ------------------------------- Test 51 ------------------------------------
+# regression in 3.8.4, logrotate crashes with sharedscripts when 0 logs rotated
+preptest test.log 51 1 0
+
+# It's memory corruption and without something in state file, it won't crash
+# reliably. It would be better to run valgrind here and check the errors, but
+# I don't want the test-suite to depend on valgrind...
+cat > state << EOF
+logrotate state -- version 2
+"/var/log/httpd/backend_error_log" 2013-6-16
+"/var/log/tokyotyrant/*.log" 2011-5-30
+"/var/log/mailman/digest" 2011-5-30
+"/var/log/piranha/piranha-gui-access" 2011-5-30
+"/var/log/boincerr.log" 2011-5-30
+"/var/log/btmp" 2013-7-9
+"/var/log/httpd/a_log" 2011-11-15
+"/var/log/cups/*_log" 2012-7-19
+"/var/log/rabbitmq/*.log" 2011-5-30
+"/var/log/func/func.log" 2011-11-17
+"/var/log/wtmp" 2013-7-9
+"/var/log/glusterfs/*glusterd.vol.log" 2011-11-17
+"/var/log/imapd.log" 2011-5-30
+"/var/log/cobbler/cobbler.log" 2011-11-6
+"/var/log/httpd/ssl_access_log" 2013-3-27
+"/var/log/mrepo.log" 2011-5-30
+EOF
+
+$RLR test-config.51
+
+if [ $? != 0 ]; then
+	echo "logrotate ended with non-zero exit code (probably crashed)"
+	exit 3
+fi
+
+cleanup 52
+
+# ------------------------------- Test 52 ------------------------------------
+# sharedscripts are not run if the first log file does not exist
+preptest test.log 52 1 0
+
+$RLR test-config.52
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+scriptout 0 foo
+EOF
+
+cleanup 53
+
+# ------------------------------- Test 53 ------------------------------------
+# test if --force works
+preptest test.log 53 1 0
+
+$RLR test-config.53 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 54
+
+# ------------------------------- Test 54 ------------------------------------
+# removing last log file when using %Y-%m-%d
+rm -f *test.log*
+preptest test.log 54 1 0
+
+DATE=""
+for i in $(seq 1 60)
+do
+    DATE=$(/bin/date "+%Y-%m-%d" --date "$i day ago" 2>/dev/null)   
+    echo "x" > test.log-$DATE
+done
+
+$RLR test-config.54 --force
+
+if [ -e test.log-$DATE ]; then
+    echo "File test.log-$DATE should not exist (it should be deleted)"
+    exit 3
+fi
+
+rm -f *test.log*
+
+cleanup 55
+
+# ------------------------------- Test 55 ------------------------------------
+# removing last log file when using %s and hourly
+rm -f *test.log*
+preptest test.log 55 1 0
+
+DATE=""
+for i in $(seq 1 60)
+do
+    DATE=$(/bin/date "+%s" --date "$i hour ago" 2>/dev/null)   
+    echo "x" > test.log-$DATE.gz
+done
+
+$RLR test-config.55 --force
+
+if [ -e test.log-$DATE.gz ]; then
+    echo "File test.log-$DATE.gz should not exist (it should be deleted)"
+    exit 3
+fi
+
+rm -f *test.log*
+
+cleanup 56
+
+# ------------------------------- Test 56 ------------------------------------
+# removing last log file when using %d-%m-%Y
+rm -f *test.log*
+preptest test.log 56 1 0
+
+DATE=""
+for i in $(seq 1 60)
+do
+    DATE=$(/bin/date "+%d-%m-%Y" --date "$i day ago" 2>/dev/null)   
+    echo "x" > test.log-$DATE
+done
+
+$RLR test-config.56 --force
+
+if [ -e test.log-$DATE ]; then
+    echo "File test.log-$DATE should not exist (it should be deleted)"
+    exit 3
+fi
+
+rm -f *test.log*
+
+cleanup 57
+
+# ------------------------------- Test 57 ------------------------------------
+# When compressing program prints something to stderr, we should prepend it
+# with the log name.
+preptest test.log 57 1
+$RLR test-config.57 --force 2>error.log
+
+grep "error: Compressing" error.log >/dev/null
+if [ $? != 0 ]; then
+	cat error.log
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+grep "compression error" error.log >/dev/null
+if [ $? != 0 ]; then
+	cat error.log
+	echo "No error printed, but there should be one."
+	exit 3
+fi
+
+rm -f error.log
+
+checkoutput <<EOF
+test.log 0
+test.log.1.gz 1 zero
+EOF
+
+cleanup 58
+
+# ------------------------------- Test 58 ------------------------------------
+# Test renamecopy
+preptest test.log 58 1 0
+$RLR test-config.58 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 59
+
+# ------------------------------- Test 59 ------------------------------------
+# Test renamecopy in debug mode, nothing should happen
+preptest test.log 58 1 0
+touch test.log.1
+touch test.log.2
+$RLR test-config.58 --force -d 2>/dev/null
+
+checkoutput <<EOF
+test.log 0 zero
+EOF
+
+rm -f test.log.1
+rm -f test.log.2
+
+cleanup 60
+
+# ------------------------------- Test 60 ------------------------------------
+# Test we log debug output using -l option when passed.
+preptest test.log 61 1 0
+
+$RLR test-config.61 --force -l ./logrotate.log
+
+DATESTRING=$(/bin/date +%Y-%m-%d-%H)
+
+grep "reading config file test-config.61" logrotate.log >/dev/null
+if [ $? != 0 ]; then
+	echo "There is no log output in logrotate.log"
+	exit 3
+fi
+
+rm -f logrotate.log
+
+checkoutput <<EOF
+test.log 0
+test.log.$DATESTRING 0 zero
+EOF
+
+cleanup 61
+
+# ------------------------------- Test 61 ------------------------------------
+preptest test.log 61 1 0
+
+$RLR test-config.61 --force
+
+DATESTRING=$(/bin/date +%Y-%m-%d-%H)
+
+checkoutput <<EOF
+test.log 0
+test.log.$DATESTRING 0 zero
+EOF
+
+cleanup 62
+
+# ------------------------------- Test 62 ------------------------------------
+# Rotate sparse file
+preptest test.log 24 1 0
+
+printf zero > test.log
+truncate -s 10M test.log
+echo x >> test.log
+
+cp test.log test.example
+
+SIZE_SPARSE_OLD=$(du test.log|awk '{print $1}')
+SIZE_OLD=$(du --apparent-size test.log|awk '{print $1}')
+$RLR test-config.24 --force
+SIZE_NEW=$(du --apparent-size test.log.1|awk '{print $1}')
+SIZE_SPARSE_NEW=$(du test.log.1|awk '{print $1}')
+
+if [ $SIZE_OLD != $SIZE_NEW ]; then
+	echo "Bad apparent size of sparse logs"
+	echo "test.log: $SIZE_OLD"
+	echo "test.log.1: $SIZE_NEW"
+	exit 3
+fi
+
+if [ $SIZE_SPARSE_OLD -gt 100 ] || [ $SIZE_SPARSE_NEW -gt 100 ]; then
+	echo "Bad size of sparse logs"
+	echo "test.log: $SIZE_SPARSE_OLD"
+	echo "test.log.1: $SIZE_SPARSE_NEW"
+	exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zerox
+EOF
+
+cleanup 63
+
+# ------------------------------- Test 63 ------------------------------------
+# Rotate sparse file, no data should be lost when hole is in the end of file
+preptest test.log 24 1 0
+
+printf zero > test.log
+truncate -s 10M test.log
+
+cp test.log test.example
+
+SIZE_SPARSE_OLD=$(du test.log|awk '{print $1}')
+SIZE_OLD=$(du --apparent-size test.log|awk '{print $1}')
+$RLR test-config.24 --force
+SIZE_NEW=$(du --apparent-size test.log.1|awk '{print $1}')
+SIZE_SPARSE_NEW=$(du test.log.1|awk '{print $1}')
+
+if [ $SIZE_OLD != $SIZE_NEW ]; then
+    echo "Bad apparent size of sparse logs"
+    echo "test.log: $SIZE_OLD"
+    echo "test.log.1: $SIZE_NEW"
+    exit 3
+fi
+
+if [ $SIZE_SPARSE_OLD -gt 100 ] || [ $SIZE_SPARSE_NEW -gt 100 ]; then
+    echo "Bad size of sparse logs"
+    echo "test.log: $SIZE_SPARSE_OLD"
+    echo "test.log.1: $SIZE_SPARSE_NEW"
+    exit 3
+fi
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+EOF
+
+cleanup 64
+
+# ------------------------------- Test 64 ------------------------------------
+# filename in mail's subject with compress directive and maillast directive
+# should be the name of the removed log
+preptest test.log 64 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+
+$RLR test-config.64 --force
+checkoutput <<EOF
+test.log 0
+EOF
+
+checkmail test.log-$DATESTRING.gz zero
+
+cleanup 65
+
+# ------------------------------- Test 65 ------------------------------------
+# filename in mail's subject without compress directive and maillast directive
+# should be the name of the removed log
+preptest test.log 65 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+
+$RLR test-config.65 --force
+checkoutput <<EOF
+test.log 0
+EOF
+
+checkmail test.log-$DATESTRING zero
+
+cleanup 66
+
+# ------------------------------- Test 66 ------------------------------------
+# When using %Y in the dateformat, the old logs are not removed
+preptest test.log 66 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+DAYAGO=$(/bin/date "+%Y-%m-%d" --date "1 day ago" 2>/dev/null)
+
+echo removed > "test.log$DAYAGO"
+
+$RLR test-config.66 --force
+checkoutput <<EOF
+test.log 0
+EOF
+
+if [ -f test.log$DAYAGO ]; then
+	echo "file $file does exist!"
+	exit 2
+fi
+
+cleanup 67
+
+# ------------------------------- Test 67 ------------------------------------
+# firstaction and lastaction scripts should be called if no file is rotated
+preptest test.log 67 1 0
+
+DATESTRING=$(/bin/date +%Y%m%d)
+TODAY=$(/bin/date "+%Y-%m-%d" 2>/dev/null)
+
+echo removed > "test.log$TODAY"
+
+$RLR test-config.67 --force
+
+cat scriptout|grep firstaction >/dev/null
+if [ $? != 0 ]; then
+	echo "scriptout should contain 'firstaction'"
+	exit 3
+fi
+
+cat scriptout|grep lastaction >/dev/null
+if [ $? != 0 ]; then
+	echo "scriptout should contain 'lastaction'"
+	exit 3
+fi
+
+cleanup 68
+
+# ------------------------------- Test 68 ------------------------------------
+# Old state file entries should be removed when not used. Logrotate should
+# not freeze on big state file.
+preptest test.log 68 1 0
+
+cat > state << EOF
+logrotate state -- version 1
+"$PWD/test.log" 2000-1-1
+EOF
+
+for i in $(seq 1 200000)
+do
+   echo "\"$PWD/removed.log$i\" 2000-1-1" >> state
+done
+
+$RLR test-config.68 --force
+
+cat state|grep test.log >/dev/null
+if [ $? != 0 ]; then
+	echo "state file should contain 'test.log'"
+	exit 3
+fi
+
+cat state|grep removed.log >/dev/null
+if [ $? = 0 ]; then
+	echo "state file should not contain 'removed.log'"
+	exit 3
+fi
+
+cleanup 69
+
+# ------------------------------- Test 69 ------------------------------------
+# Test olddir with wildcard in the pattern
+preptest test.log 69 1 0
+rm -rf testdir adir bdir
+mkdir adir
+mkdir bdir
+cp test.log adir
+cp test.log bdir
+$RLR test-config.69 --force -v
+
+checkoutput <<EOF
+adir/test.log 0
+testdir/test.log.1 0 zero
+EOF
+
+rm -rf testdir adir
+rm -rf testdir bdir
+
+cleanup 70
+
+# ------------------------------- Test 70 ------------------------------------
+# No rotation should occur because file is too young
+preptest test.log 70 2
+
+# Put in place a state file that will force a rotation
+cat > state <<EOF
+logrotate state -- version 2
+"$PWD/test.log" 2000-1-1
+EOF
+
+$RLR test-config.70
+
+checkoutput <<EOF
+test.log 0 zero
+test.log.1 0 first
+EOF
+
+cleanup 71
+
+# ------------------------------- Test 71 ------------------------------------
+# Rotation should occur because file is old
+preptest test.log 71 2
+
+# Set log modification time to some date in the past
+touch -t 200001010000 test.log
+
+# Put in place a state file that will force a rotation
+cat > state <<EOF
+logrotate state -- version 2
+"$PWD/test.log" 2000-1-1
+EOF
+
+$RLR test-config.71
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test.log.2 0 first
+EOF
+
+cleanup 72
+
+# ------------------------------- Test 72 ------------------------------------
+preptest test.log 72 2
+
+$RLR test-config.72 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0 zero
+test.log.2.gz 1 first
+EOF
+
+echo 'unexpected' > test.log.1.gz
+
+$RLR test-config.72 --force
+dt="$(date +%Y%m%d%H)"
+
+checkoutput <<EOF
+test.log 0
+test.log.1 0
+test.log.1.gz-$dt.backup 0 unexpected
+test.log.2.gz 1 zero
+test.log.3.gz 1 first
+EOF
+
+cleanup 100
+
+# ------------------------------- Test 100 ------------------------------------
+# check rotation with extension appended to the filename
+preptest test.log 100 1 0
+$RLR test-config.100 --force
+
+checkoutput <<EOF
+test.log 0
+test.log.1.newext 0 zero
+EOF
+
+# check rotation with extension moved after the number
+cleanup 101
+
+preptest test.log 101 1 0
+$RLR test-config.101 --force
+
+checkoutput <<EOF
+test.log 0
+test.1.log 0 zero
+EOF
+
+
+cleanup
+
diff --git a/logrotate/test/test-config.1.in b/logrotate/test/test-config.1.in
new file mode 100644
index 0000000..46651db
--- /dev/null
+++ b/logrotate/test/test-config.1.in
@@ -0,0 +1,9 @@
+create
+
+&DIR&/test.log {
+    daily
+    # note the white space after this line
+    rotate 2 
+    mail user@myhost.org
+    maillast
+}
diff --git a/logrotate/test/test-config.10.in b/logrotate/test/test-config.10.in
new file mode 100644
index 0000000..2bbdf70
--- /dev/null
+++ b/logrotate/test/test-config.10.in
@@ -0,0 +1,11 @@
+create
+
+&DIR&/test.log {
+    daily
+    rotate 3
+    compress
+    delaycompress
+    create
+    mailfirst
+    mail user@myhost.org
+}
diff --git a/logrotate/test/test-config.100.in b/logrotate/test/test-config.100.in
new file mode 100644
index 0000000..da99f89
--- /dev/null
+++ b/logrotate/test/test-config.100.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    monthly
+    rotate 1
+    addextension .newext
+}
diff --git a/logrotate/test/test-config.101.in b/logrotate/test/test-config.101.in
new file mode 100644
index 0000000..c5f8951
--- /dev/null
+++ b/logrotate/test/test-config.101.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    monthly
+    rotate 1
+    addextension .log
+}
diff --git a/logrotate/test/test-config.11.in b/logrotate/test/test-config.11.in
new file mode 100644
index 0000000..1c235ed
--- /dev/null
+++ b/logrotate/test/test-config.11.in
@@ -0,0 +1,17 @@
+create
+
+compress
+
+&DIR&/test.log {
+    monthly
+    rotate 1
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.12.in b/logrotate/test/test-config.12.in
new file mode 100644
index 0000000..b35e139
--- /dev/null
+++ b/logrotate/test/test-config.12.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    monthly
+    rotate 1
+    olddir testdir
+}
diff --git a/logrotate/test/test-config.13.in b/logrotate/test/test-config.13.in
new file mode 100644
index 0000000..dc2efd5
--- /dev/null
+++ b/logrotate/test/test-config.13.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    monthly
+    rotate 1
+    olddir &DIR&/testdir
+    createolddir 700 &USER& &GROUP&
+}
diff --git a/logrotate/test/test-config.14.in b/logrotate/test/test-config.14.in
new file mode 100644
index 0000000..d6b8722
--- /dev/null
+++ b/logrotate/test/test-config.14.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+	dateext
+	dateformat .%Y-%m-%d
+    rotate 1
+}
diff --git a/logrotate/test/test-config.15.in b/logrotate/test/test-config.15.in
new file mode 100644
index 0000000..13b43b0
--- /dev/null
+++ b/logrotate/test/test-config.15.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+    shred
+    shredcycles 20
+    rotate 1
+}
diff --git a/logrotate/test/test-config.16.in b/logrotate/test/test-config.16.in
new file mode 100644
index 0000000..cc42fa8
--- /dev/null
+++ b/logrotate/test/test-config.16.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    weekly
+    maxsize 4
+    rotate 1
+}
diff --git a/logrotate/test/test-config.17.in b/logrotate/test/test-config.17.in
new file mode 100644
index 0000000..a33eaa0
--- /dev/null
+++ b/logrotate/test/test-config.17.in
@@ -0,0 +1,8 @@
+create
+
+# missing { is OK, we're testing parsing here
+&DIR&/test.log
+    weekly
+    maxsize 4
+    rotate 1
+}
diff --git a/logrotate/test/test-config.18.in b/logrotate/test/test-config.18.in
new file mode 100644
index 0000000..ee2fc3f
--- /dev/null
+++ b/logrotate/test/test-config.18.in
@@ -0,0 +1,10 @@
+create
+
+# tests more compressoptions
+&DIR&/test.log {
+    compress
+    compresscmd ./compress
+    compressoptions -f -9
+    weekly
+    rotate 1
+}
diff --git a/logrotate/test/test-config.19.in b/logrotate/test/test-config.19.in
new file mode 100644
index 0000000..5f5fe03
--- /dev/null
+++ b/logrotate/test/test-config.19.in
@@ -0,0 +1,9 @@
+create
+
+&DIR&/test*.log {
+    daily
+    rotate 1
+    postrotate
+	exit 1
+    endscript
+}
diff --git a/logrotate/test/test-config.2.in b/logrotate/test/test-config.2.in
new file mode 100644
index 0000000..fb5500f
--- /dev/null
+++ b/logrotate/test/test-config.2.in
@@ -0,0 +1,6 @@
+"&DIR&/test.log" {
+    monthly
+    rotate 2
+    mail user@myhost.org
+    maillast
+}
diff --git a/logrotate/test/test-config.20.in b/logrotate/test/test-config.20.in
new file mode 100644
index 0000000..d2a17b4
--- /dev/null
+++ b/logrotate/test/test-config.20.in
@@ -0,0 +1,10 @@
+create
+
+&DIR&/test*.log {
+    daily
+    rotate 1
+    sharedscripts
+    postrotate
+	exit 1
+    endscript
+}
diff --git a/logrotate/test/test-config.21.in b/logrotate/test/test-config.21.in
new file mode 100644
index 0000000..7bca5c2
--- /dev/null
+++ b/logrotate/test/test-config.21.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test*.log {
+    daily
+    rotate 1
+    missingok
+}
diff --git a/logrotate/test/test-config.22.in b/logrotate/test/test-config.22.in
new file mode 100644
index 0000000..d98ac86
--- /dev/null
+++ b/logrotate/test/test-config.22.in
@@ -0,0 +1,6 @@
+create
+
+&DIR&/test*.log {
+    daily
+    rotate 1
+}
diff --git a/logrotate/test/test-config.23.in b/logrotate/test/test-config.23.in
new file mode 100644
index 0000000..d98ac86
--- /dev/null
+++ b/logrotate/test/test-config.23.in
@@ -0,0 +1,6 @@
+create
+
+&DIR&/test*.log {
+    daily
+    rotate 1
+}
diff --git a/logrotate/test/test-config.24.in b/logrotate/test/test-config.24.in
new file mode 100644
index 0000000..35cfcd3
--- /dev/null
+++ b/logrotate/test/test-config.24.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test*.log {
+    daily
+    copytruncate
+    rotate 1
+}
diff --git a/logrotate/test/test-config.25.in b/logrotate/test/test-config.25.in
new file mode 100644
index 0000000..1e5b077
--- /dev/null
+++ b/logrotate/test/test-config.25.in
@@ -0,0 +1,7 @@
+create
+
+# missing { is OK, we're testing parsing here
+&DIR&/test.log
+    weekly
+    maxsize 4
+    rotate 1
diff --git a/logrotate/test/test-config.26.in b/logrotate/test/test-config.26.in
new file mode 100644
index 0000000..3c820fb
--- /dev/null
+++ b/logrotate/test/test-config.26.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    waeekly
+    maxsize 4
+    rotate 1
+}
\ No newline at end of file
diff --git a/logrotate/test/test-config.27.in b/logrotate/test/test-config.27.in
new file mode 100644
index 0000000..eac0232
--- /dev/null
+++ b/logrotate/test/test-config.27.in
@@ -0,0 +1,13 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+	compress
+	dateext
+	ifempty
+	delaycompress
+
+	mailfirst
+	mail user@myhost.org
+}
diff --git a/logrotate/test/test-config.28.in b/logrotate/test/test-config.28.in
new file mode 100644
index 0000000..96479d6
--- /dev/null
+++ b/logrotate/test/test-config.28.in
@@ -0,0 +1,9 @@
+create
+
+#testing { on new line
+&DIR&/test.log
+{
+    weekly
+    maxsize 4
+    rotate 1
+}
diff --git a/logrotate/test/test-config.29.in b/logrotate/test/test-config.29.in
new file mode 100644
index 0000000..b3123d6
--- /dev/null
+++ b/logrotate/test/test-config.29.in
@@ -0,0 +1,6 @@
+create
+rotate 1
+daily
+
+#testing { } on the same line
+&DIR&/test.log { }
diff --git a/logrotate/test/test-config.3.in b/logrotate/test/test-config.3.in
new file mode 100644
index 0000000..eb081f9
--- /dev/null
+++ b/logrotate/test/test-config.3.in
@@ -0,0 +1,14 @@
+create
+
+&DIR&/test*.log {
+    monthly
+    rotate 1
+    mail user@myhost.org
+    maillast
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.30.in b/logrotate/test/test-config.30.in
new file mode 100644
index 0000000..154fb56
--- /dev/null
+++ b/logrotate/test/test-config.30.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+	dateext
+}
diff --git a/logrotate/test/test-config.31.in b/logrotate/test/test-config.31.in
new file mode 100644
index 0000000..c1f0e22
--- /dev/null
+++ b/logrotate/test/test-config.31.in
@@ -0,0 +1,6 @@
+create 0600 &USER& &GROUP&
+
+&DIR&/test.log {
+	daily
+	rotate 999
+}
diff --git a/logrotate/test/test-config.32.in b/logrotate/test/test-config.32.in
new file mode 100644
index 0000000..1b8d7be
--- /dev/null
+++ b/logrotate/test/test-config.32.in
@@ -0,0 +1,6 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+}
diff --git a/logrotate/test/test-config.33.in b/logrotate/test/test-config.33.in
new file mode 100644
index 0000000..dba4234
--- /dev/null
+++ b/logrotate/test/test-config.33.in
@@ -0,0 +1,6 @@
+create 0600
+
+&DIR&/test.log {
+	daily
+	rotate 999
+}
diff --git a/logrotate/test/test-config.34.in b/logrotate/test/test-config.34.in
new file mode 100644
index 0000000..cae6959
--- /dev/null
+++ b/logrotate/test/test-config.34.in
@@ -0,0 +1,6 @@
+create root root
+
+&DIR&/test.log {
+	daily
+	rotate 1
+}
diff --git a/logrotate/test/test-config.35.in b/logrotate/test/test-config.35.in
new file mode 100644
index 0000000..8f34312
--- /dev/null
+++ b/logrotate/test/test-config.35.in
@@ -0,0 +1,6 @@
+create &USER& &GROUP&
+
+&DIR&/test.log {
+	daily
+	rotate 1
+}
diff --git a/logrotate/test/test-config.36.in b/logrotate/test/test-config.36.in
new file mode 100644
index 0000000..a53271f
--- /dev/null
+++ b/logrotate/test/test-config.36.in
@@ -0,0 +1,7 @@
+# bad unit 'x'
+
+&DIR&/test.log {
+	daily
+	size 300x
+	rotate 1
+}
diff --git a/logrotate/test/test-config.37.in b/logrotate/test/test-config.37.in
new file mode 100644
index 0000000..99befbf
--- /dev/null
+++ b/logrotate/test/test-config.37.in
@@ -0,0 +1,23 @@
+&DIR&/test.log {
+	size 300x
+	daily
+    firstaction
+		touch scriptout
+		# put some } here, so we check if parser won't break on it.
+}
+		echo $(cat scriptout) foo > foo }
+		mv foo scriptout
+	endscript
+	rotate 1
+}
+
+&DIR&/test.log {
+	create
+	daily
+    firstaction
+		touch scriptout
+		echo $(cat scriptout) second > foo
+		mv foo scriptout
+	endscript
+	rotate 1
+}
diff --git a/logrotate/test/test-config.38.in b/logrotate/test/test-config.38.in
new file mode 100644
index 0000000..806799f
--- /dev/null
+++ b/logrotate/test/test-config.38.in
@@ -0,0 +1,14 @@
+create
+
+&DIR&/test*.log {
+    monthly
+    rotate 0
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    preremove
+	touch scriptout
+	printf '%s' "$(basename $*)" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.39.in b/logrotate/test/test-config.39.in
new file mode 100644
index 0000000..8e84927
--- /dev/null
+++ b/logrotate/test/test-config.39.in
@@ -0,0 +1,15 @@
+create
+
+&DIR&/test*.log {
+    monthly
+    rotate 0
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    preremove
+	touch scriptout
+	printf '%s' "$(basename $*)" >> scriptout
+	this should trigger an error
+    endscript
+}
diff --git a/logrotate/test/test-config.4.in b/logrotate/test/test-config.4.in
new file mode 100644
index 0000000..a06fa83
--- /dev/null
+++ b/logrotate/test/test-config.4.in
@@ -0,0 +1,15 @@
+create
+
+&DIR&/test*.log {
+    monthly
+    rotate 1
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.40.in b/logrotate/test/test-config.40.in
new file mode 100644
index 0000000..29779c0
--- /dev/null
+++ b/logrotate/test/test-config.40.in
@@ -0,0 +1,5 @@
+create
+tabooext .v,.sample,.unused,~
+tabooext + .x, .y, .z
+
+include &DIR&/testingdir
diff --git a/logrotate/test/test-config.41.in b/logrotate/test/test-config.41.in
new file mode 100644
index 0000000..59dd395
--- /dev/null
+++ b/logrotate/test/test-config.41.in
@@ -0,0 +1,17 @@
+create
+
+&DIR&/test*.log {
+    size 5
+    rotate 1
+    nosharedscripts
+    prerotate
+		touch scriptout
+		printf '%s' "$(basename $1)" >> scriptout
+		printf ";" >> scriptout
+    endscript
+    postrotate
+		touch scriptout
+		printf '%s' "$(basename $1)" >> scriptout
+		printf ";" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.42.in b/logrotate/test/test-config.42.in
new file mode 100644
index 0000000..a28ee08
--- /dev/null
+++ b/logrotate/test/test-config.42.in
@@ -0,0 +1,27 @@
+create
+
+&DIR&/test*.log {
+    size 5
+    rotate 1
+    sharedscripts
+    prerotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		printf "\"" >> scriptout
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >> scriptout
+			printf ";" >> scriptout
+		done
+    endscript
+    postrotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >>  scriptout
+			printf ";" >> scriptout
+		done
+		printf "\"" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.43.in b/logrotate/test/test-config.43.in
new file mode 100644
index 0000000..d8eca2f
--- /dev/null
+++ b/logrotate/test/test-config.43.in
@@ -0,0 +1,17 @@
+create
+
+&DIR&/test*.log {
+    size 5
+    rotate 1
+    nosharedscripts
+    prerotate
+		touch scriptout
+		printf '%s' "$(basename $1)" >> scriptout
+		printf ";" >> scriptout
+    endscript
+    postrotate
+		touch scriptout
+		printf '%s' "$(basename $1)" >>  scriptout
+		printf ";" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.44.in b/logrotate/test/test-config.44.in
new file mode 100644
index 0000000..6a6cdf0
--- /dev/null
+++ b/logrotate/test/test-config.44.in
@@ -0,0 +1,27 @@
+create
+
+&DIR&/test.log &DIR&/test2.log {
+    size 5
+    rotate 1
+    nosharedscripts
+    prerotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		printf "\"" >> scriptout 
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >>  scriptout
+			printf ";" >> scriptout
+		done
+    endscript
+    postrotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >>  scriptout
+			printf ";" >> scriptout
+		done
+		printf "\"" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.45.in b/logrotate/test/test-config.45.in
new file mode 100644
index 0000000..284f1bf
--- /dev/null
+++ b/logrotate/test/test-config.45.in
@@ -0,0 +1,27 @@
+create
+
+&DIR&/test.log &DIR&/test2.log {
+    size 5
+    rotate 1
+    sharedscripts
+    prerotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		printf "\"" >> scriptout
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >>  scriptout
+			printf ";" >> scriptout
+		done
+    endscript
+    postrotate
+		touch scriptout
+		IFS=$(printf "\n\b")
+		for f in "$1"
+		do
+			printf '%s' "$(basename $f)" >>  scriptout
+			printf ";" >> scriptout
+		done
+		printf "\"" >> scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.46.in b/logrotate/test/test-config.46.in
new file mode 100644
index 0000000..154fb56
--- /dev/null
+++ b/logrotate/test/test-config.46.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+	dateext
+}
diff --git a/logrotate/test/test-config.47.in b/logrotate/test/test-config.47.in
new file mode 100644
index 0000000..154fb56
--- /dev/null
+++ b/logrotate/test/test-config.47.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+	dateext
+}
diff --git a/logrotate/test/test-config.48.in b/logrotate/test/test-config.48.in
new file mode 100644
index 0000000..b7d3f00
--- /dev/null
+++ b/logrotate/test/test-config.48.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 999
+	size 2
+}
diff --git a/logrotate/test/test-config.49.in b/logrotate/test/test-config.49.in
new file mode 100644
index 0000000..dca1b63
--- /dev/null
+++ b/logrotate/test/test-config.49.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	daily
+	rotate 4
+	size 2
+}
diff --git a/logrotate/test/test-config.5.in b/logrotate/test/test-config.5.in
new file mode 100644
index 0000000..dfc7890
--- /dev/null
+++ b/logrotate/test/test-config.5.in
@@ -0,0 +1,15 @@
+create
+
+&DIR&/test.log &DIR&/anothertest.log {
+    monthly
+    rotate 1
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.50.in b/logrotate/test/test-config.50.in
new file mode 100644
index 0000000..38bdd48
--- /dev/null
+++ b/logrotate/test/test-config.50.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	hourly
+	dateext
+	rotate 4
+}
diff --git a/logrotate/test/test-config.51.in b/logrotate/test/test-config.51.in
new file mode 100644
index 0000000..3182525
--- /dev/null
+++ b/logrotate/test/test-config.51.in
@@ -0,0 +1,8 @@
+/var/log/this_dir_does_not_exist/*log {
+    size 1
+    missingok
+    sharedscripts
+    prerotate
+    /usr/bin/some-program
+    endscript
+}
diff --git a/logrotate/test/test-config.52.in b/logrotate/test/test-config.52.in
new file mode 100644
index 0000000..56beced
--- /dev/null
+++ b/logrotate/test/test-config.52.in
@@ -0,0 +1,13 @@
+create
+
+/var/log/does_not_exist.log &DIR&/test.log {
+	rotate 14
+	size 2
+	missingok
+	sharedscripts
+	postrotate
+		touch scriptout
+		echo $(cat scriptout) foo > foo
+		mv foo scriptout
+	endscript
+}
\ No newline at end of file
diff --git a/logrotate/test/test-config.53.in b/logrotate/test/test-config.53.in
new file mode 100644
index 0000000..871fcff
--- /dev/null
+++ b/logrotate/test/test-config.53.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+	rotate 14
+	size 4096
+	missingok
+}
\ No newline at end of file
diff --git a/logrotate/test/test-config.54.in b/logrotate/test/test-config.54.in
new file mode 100644
index 0000000..c946af1
--- /dev/null
+++ b/logrotate/test/test-config.54.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+	dateext
+	dateformat -%Y-%m-%d
+    rotate 60
+}
diff --git a/logrotate/test/test-config.55.in b/logrotate/test/test-config.55.in
new file mode 100644
index 0000000..8b10ad1
--- /dev/null
+++ b/logrotate/test/test-config.55.in
@@ -0,0 +1,21 @@
+create
+
+# continue and throw no error message when log file is not present
+missingok
+
+# truncate the original log file in place after creating a copy
+copytruncate
+
+# compress the file
+compress
+
+# do only rotate when not empty
+notifempty
+
+&DIR&/test.log {
+    hourly
+	dateext
+	dateformat -%s
+    rotate 60
+    nosharedscripts
+}
diff --git a/logrotate/test/test-config.56.in b/logrotate/test/test-config.56.in
new file mode 100644
index 0000000..adaf2a5
--- /dev/null
+++ b/logrotate/test/test-config.56.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+	dateext
+	dateformat -%d-%m-%Y
+    rotate 60
+}
diff --git a/logrotate/test/test-config.57.in b/logrotate/test/test-config.57.in
new file mode 100644
index 0000000..e9018b3
--- /dev/null
+++ b/logrotate/test/test-config.57.in
@@ -0,0 +1,10 @@
+create
+
+# tests more compressoptions
+&DIR&/test.log {
+    compress
+    compresscmd ./compress-error
+    compressoptions -f -9
+    weekly
+    rotate 1
+}
diff --git a/logrotate/test/test-config.58.in b/logrotate/test/test-config.58.in
new file mode 100644
index 0000000..34906da
--- /dev/null
+++ b/logrotate/test/test-config.58.in
@@ -0,0 +1,7 @@
+create
+
+&DIR&/test.log {
+    renamecopy
+    weekly
+    rotate 1
+}
diff --git a/logrotate/test/test-config.6.in b/logrotate/test/test-config.6.in
new file mode 100644
index 0000000..0d3e956
--- /dev/null
+++ b/logrotate/test/test-config.6.in
@@ -0,0 +1,16 @@
+create
+
+&DIR&/test.log &DIR&/anothertest.log {
+    monthly
+    rotate 1
+    start 0
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.61.in b/logrotate/test/test-config.61.in
new file mode 100644
index 0000000..423241f
--- /dev/null
+++ b/logrotate/test/test-config.61.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+    dateext
+    dateformat .%Y-%m-%d-%H
+    rotate 1
+}
diff --git a/logrotate/test/test-config.64.in b/logrotate/test/test-config.64.in
new file mode 100644
index 0000000..c817734
--- /dev/null
+++ b/logrotate/test/test-config.64.in
@@ -0,0 +1,13 @@
+create
+
+&DIR&/test.log {
+    daily
+    dateext
+    dateformat -%Y%m%d
+    rotate 0
+    compress
+    nosharedscripts
+    dateext
+    mail user@myhost.org
+    maillast
+}
diff --git a/logrotate/test/test-config.65.in b/logrotate/test/test-config.65.in
new file mode 100644
index 0000000..19007f5
--- /dev/null
+++ b/logrotate/test/test-config.65.in
@@ -0,0 +1,12 @@
+create
+
+&DIR&/test.log {
+    daily
+    dateext
+    dateformat -%Y%m%d
+    rotate 0
+    nosharedscripts
+    dateext
+    mail user@myhost.org
+    maillast
+}
diff --git a/logrotate/test/test-config.66.in b/logrotate/test/test-config.66.in
new file mode 100644
index 0000000..8c32687
--- /dev/null
+++ b/logrotate/test/test-config.66.in
@@ -0,0 +1,10 @@
+create
+
+&DIR&/test.log {
+    daily
+    dateext
+    dateformat %Y-%m-%d
+    rotate 1
+    nosharedscripts
+    dateext
+}
diff --git a/logrotate/test/test-config.67.in b/logrotate/test/test-config.67.in
new file mode 100644
index 0000000..69b9fff
--- /dev/null
+++ b/logrotate/test/test-config.67.in
@@ -0,0 +1,16 @@
+create
+
+&DIR&/test.log {
+    daily
+    dateext
+    dateformat %Y-%m-%d
+    rotate 1
+
+	firstaction
+		echo "firstaction" > scriptout
+	endscript
+
+	lastaction
+		echo "lastaction" >> scriptout
+	endscript
+}
diff --git a/logrotate/test/test-config.68.in b/logrotate/test/test-config.68.in
new file mode 100644
index 0000000..e8e1c79
--- /dev/null
+++ b/logrotate/test/test-config.68.in
@@ -0,0 +1,6 @@
+create
+
+&DIR&/test.log {
+    daily
+    rotate 1
+}
diff --git a/logrotate/test/test-config.69.in b/logrotate/test/test-config.69.in
new file mode 100644
index 0000000..9752e0a
--- /dev/null
+++ b/logrotate/test/test-config.69.in
@@ -0,0 +1,10 @@
+create
+
+&DIR&/*/test.log
+&DIR&/*/test.lo3 {
+    monthly
+    rotate 1
+    olddir &DIR&/testdir
+    createolddir 700 &USER& &GROUP&
+    missingok
+}
diff --git a/logrotate/test/test-config.7.in b/logrotate/test/test-config.7.in
new file mode 100644
index 0000000..d565da2
--- /dev/null
+++ b/logrotate/test/test-config.7.in
@@ -0,0 +1,16 @@
+create
+
+&DIR&/test.log &DIR&/anothertest.log {
+    monthly
+    rotate 3
+    start 6
+    mail user@myhost.org
+    maillast
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.70.in b/logrotate/test/test-config.70.in
new file mode 100644
index 0000000..0047d73
--- /dev/null
+++ b/logrotate/test/test-config.70.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+    rotate 3
+    missingok
+    minage 5
+}
diff --git a/logrotate/test/test-config.71.in b/logrotate/test/test-config.71.in
new file mode 100644
index 0000000..0047d73
--- /dev/null
+++ b/logrotate/test/test-config.71.in
@@ -0,0 +1,8 @@
+create
+
+&DIR&/test.log {
+    daily
+    rotate 3
+    missingok
+    minage 5
+}
diff --git a/logrotate/test/test-config.72.in b/logrotate/test/test-config.72.in
new file mode 100644
index 0000000..9fe50a2
--- /dev/null
+++ b/logrotate/test/test-config.72.in
@@ -0,0 +1,7 @@
+&DIR&/test.log {
+    daily
+    rotate 3
+    compress
+    delaycompress
+    create
+}
diff --git a/logrotate/test/test-config.8.in b/logrotate/test/test-config.8.in
new file mode 100644
index 0000000..994be5f
--- /dev/null
+++ b/logrotate/test/test-config.8.in
@@ -0,0 +1,17 @@
+create
+
+compress
+
+&DIR&/test.log {
+    monthly
+    rotate 3
+    mail user@myhost.org
+    mailfirst
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/test/test-config.9.in b/logrotate/test/test-config.9.in
new file mode 100644
index 0000000..0d26bd9
--- /dev/null
+++ b/logrotate/test/test-config.9.in
@@ -0,0 +1,17 @@
+create
+
+compress
+
+&DIR&/test.log {
+    monthly
+    rotate 0
+    mail user@myhost.org
+    mailfirst
+    sharedscripts
+
+    postrotate
+	touch scriptout
+	echo $(cat scriptout) foo > foo
+	mv foo scriptout
+    endscript
+}
diff --git a/logrotate/upload-release.sh b/logrotate/upload-release.sh
new file mode 100755
index 0000000..e227bcc
--- /dev/null
+++ b/logrotate/upload-release.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+set -x
+SELF="$0"
+TAG="$1"
+TOKEN="$2"
+
+NAME="logrotate"
+NV="${NAME}-${TAG}"
+
+usage() {
+    printf "Usage: %s TAG TOKEN\n" "$SELF" >&2
+    exit 1
+}
+
+die() {
+    printf "%s: error: %s\n" "$SELF" >&2
+    exit 1
+}
+
+# check arguments
+test "$TAG" = "$(git describe --tags "$TAG")" || usage
+test -n "$TOKEN" || usage
+
+# check that .tar.gz is prepared
+TAR_GZ="${NV}.tar.gz"
+test -e "$TAR_GZ" || die "$TAR_GZ does not exist"
+
+# create .tar.xz from .tar.gz
+TAR_XZ="${NV}.tar.xz"
+gzip -cd "$TAR_GZ" | xz -c > "$TAR_XZ" || die "failed to write $TAR_XZ"
+
+# file to store response from GitHub API
+JSON="./${NV}-github-relase.js"
+
+# create a new release on GitHub
+curl "https://api.github.com/repos/${NAME}/${NAME}/releases" \
+    -o "$JSON" --fail --verbose \
+    --header "Authorization: token $TOKEN" \
+    --data '{
+    "tag_name": "'"$TAG"'",
+    "target_commitish": "master",
+    "name": "'"$NV"'",
+    "draft": false,
+    "prerelease": false
+}' || exit $?
+
+# parse upload URL from the response
+UPLOAD_URL="$(grep '^ *"upload_url": "' "$JSON" \
+    | sed -e 's/^ *"upload_url": "//' -e 's/{.*}.*$//')"
+grep '^https://uploads.github.com/.*/assets$' <<< "$UPLOAD_URL" || exit $?
+
+# upload both .tar.gz and .tar.xz
+for comp in gzip xz; do
+    file="${NV}.tar.${comp:0:2}"
+    curl "${UPLOAD_URL}?name=${file}" \
+        -T "$file" --fail --verbose \
+        --header "Authorization: token $TOKEN" \
+        --header "Content-Type: application/x-${comp}"
+done