diff options
author | Joey Hess <joeyh@debian.org> | 2013-07-10 16:21:41 -0400 |
---|---|---|
committer | Joey Hess <joeyh@debian.org> | 2013-07-10 16:21:41 -0400 |
commit | 48ca6c6dab953dc0915cda78ac6c3c5bd4bd95ca (patch) | |
tree | d616e72a2afd9b29ec085ef5b57d925bfec6c4e6 | |
download | moreutils-48ca6c6dab953dc0915cda78ac6c3c5bd4bd95ca.tar.gz |
moreutils (0.49) unstable; urgency=low
* ts: Fix timezone used with -i
* errno, isutf8: Fix zero-termination of option list, which could lead
to getopt crash. Closes: #715867
(Thanks, Mayhem Team)
# imported from the archive
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Makefile | 60 | ||||
-rw-r--r-- | README | 27 | ||||
-rwxr-xr-x | check-isutf8 | 46 | ||||
-rwxr-xr-x | chronic | 63 | ||||
-rwxr-xr-x | combine | 167 | ||||
-rw-r--r-- | debian/changelog | 450 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 35 | ||||
-rw-r--r-- | debian/copyright | 78 | ||||
-rw-r--r-- | debian/docs | 1 | ||||
-rwxr-xr-x | debian/rules | 12 | ||||
-rw-r--r-- | errno.c | 228 | ||||
-rw-r--r-- | errno.docbook | 136 | ||||
-rw-r--r-- | ifdata.c | 546 | ||||
-rw-r--r-- | ifdata.docbook | 321 | ||||
-rw-r--r-- | ifne.c | 127 | ||||
-rw-r--r-- | ifne.docbook | 91 | ||||
-rw-r--r-- | isutf8.c | 280 | ||||
-rw-r--r-- | isutf8.docbook | 125 | ||||
-rw-r--r-- | lckdo.c | 233 | ||||
-rw-r--r-- | lckdo.docbook | 157 | ||||
-rw-r--r-- | mispipe.c | 179 | ||||
-rw-r--r-- | mispipe.docbook | 93 | ||||
-rw-r--r-- | parallel.c | 243 | ||||
-rw-r--r-- | parallel.docbook | 162 | ||||
-rw-r--r-- | pee.c | 61 | ||||
-rw-r--r-- | pee.docbook | 89 | ||||
-rw-r--r-- | physmem.c | 301 | ||||
-rw-r--r-- | sponge.c | 373 | ||||
-rw-r--r-- | sponge.docbook | 80 | ||||
-rwxr-xr-x | ts | 154 | ||||
-rwxr-xr-x | vidir | 232 | ||||
-rwxr-xr-x | vipe | 74 | ||||
-rwxr-xr-x | zrun | 105 |
35 files changed, 5670 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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) <year> <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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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) year 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/Makefile b/Makefile new file mode 100644 index 0000000..be379ab --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +BINS=isutf8 ifdata ifne pee sponge mispipe lckdo parallel errno +PERLSCRIPTS=vidir vipe ts combine zrun chronic +MANS=sponge.1 vidir.1 vipe.1 isutf8.1 ts.1 combine.1 ifdata.1 ifne.1 pee.1 zrun.1 chronic.1 mispipe.1 lckdo.1 parallel.1 errno.1 +CFLAGS?=-O2 -g -Wall +INSTALL_BIN?=install -s +PREFIX?=/usr + +DOCBOOK2XMAN=docbook2x-man + +all: $(BINS) $(MANS) + +clean: + rm -f $(BINS) $(MANS) dump.c errnos.h errno.o + +install: + mkdir -p $(DESTDIR)$(PREFIX)/bin + $(INSTALL_BIN) $(BINS) $(DESTDIR)$(PREFIX)/bin + install $(PERLSCRIPTS) $(DESTDIR)$(PREFIX)/bin + + mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 + install $(MANS) $(DESTDIR)$(PREFIX)/share/man/man1 + +check: isutf8 + ./check-isutf8 + +isutf8.1: isutf8.docbook + $(DOCBOOK2XMAN) $< + +ifdata.1: ifdata.docbook + $(DOCBOOK2XMAN) $< + +ifne.1: ifne.docbook + $(DOCBOOK2XMAN) $< + +pee.1: pee.docbook + $(DOCBOOK2XMAN) $< + +sponge.1: sponge.docbook + $(DOCBOOK2XMAN) $< + +mispipe.1: mispipe.docbook + $(DOCBOOK2XMAN) $< + +lckdo.1: lckdo.docbook + $(DOCBOOK2XMAN) $< + +parallel.1: parallel.docbook + $(DOCBOOK2XMAN) $< + +errno.o: errnos.h +errnos.h: + echo '#include <errno.h>' > dump.c + $(CC) -E -dD dump.c | awk '/^#define E/ { printf "{\"%s\",%s},\n", $$2, $$2 }' > errnos.h + rm -f dump.c + +errno.1: errno.docbook + $(DOCBOOK2XMAN) $< + +%.1: % + pod2man --center=" " --release="moreutils" $< > $@; @@ -0,0 +1,27 @@ +This is a collection of the unix tools that nobody thought to write +long ago, when unix was young. Currently it consists of these tools: + +chronic: runs a command quietly unless it fails +combine: combine the lines in two files using boolean operations +errno: look up errno names and descriptions +ifdata: get network interface info without parsing ifconfig output +isutf8: check if a file or standard input is utf-8 +ifne: run a command if the standard input is not empty +lckdo: execute a program with a lock held (deprecated) +mispipe: pipe two commands, returning the exit status of the first +parallel: run multiple jobs at once +pee: tee standard input to pipes +sponge: soak up standard input and write to a file +ts: timestamp standard input +vidir: edit a directory in your text editor +vipe: insert a text editor into a pipe +zrun: automatically uncompress arguments to command + +Its web page is here: http://kitenet.net/~joey/code/moreutils/ + +Your suggestions of additional tools to add to this collection are +appreciated. The web page lists some that are under consideration but +have not yet been included, I also welcome feedback on which of these to +include. + +-- Joey Hess <joey@kitenet.net> diff --git a/check-isutf8 b/check-isutf8 new file mode 100755 index 0000000..83a4eed --- /dev/null +++ b/check-isutf8 @@ -0,0 +1,46 @@ +#!/bin/bash +# Bash needed for the character encodings below. +# +# Run checks for ./isutf8. +# +# Lars Wirzenius <liw@iki.fi> + +failed=0 + +check() { + printf "$2" | ./isutf8 -q + ret=$? + if [ $ret != $1 ]; then + echo "Failure:" + echo " input: $2" + echo " expected: $1" + echo " got: $ret" + failed=1 + fi + printf "$2" > check-isutf8.tmp.$$ + ./isutf8 -q check-isutf8.tmp.$$ + ret=$? + rm -f check-isutf8.tmp.$$ + if [ $ret != $1 ]; then + echo "Failure (from file):" + echo " input: $2" + echo " expected: $1" + echo " got: $ret" + failed=1 + fi +} + +check 0 '' +check 0 'a' +check 0 'ab' +check 0 '\xc2\xa9' + +check 1 '\xc2' +check 1 '\xc2\x20' +check 1 '\x20\xc2' +check 1 '\300\200' +check 1 '\xed\xa0\x88\xed\xbd\x85' # UTF-16 surrogates +check 1 '\xef\xbf\xbe' # 0xFFFE +check 1 '\xef\xbf\xbf' # 0xFFFF + +exit $failed @@ -0,0 +1,63 @@ +#!/usr/bin/perl + +=head1 NAME + +chronic - runs a command quietly unless it fails + +=head1 SYNOPSIS + +chronic COMMAND... + +=head1 DESCRIPTION + +chronic runs a command, and arranges for its standard out and standard +error to only be displayed if the command fails (exits nonzero or crashes). +If the command succeeds, any extraneous output will be hidden. + +A common use for chronic is for running a cron job. Rather than +trying to keep the command quiet, and having to deal with mails containing +accidental output when it succeeds, and not verbose enough output when it +fails, you can just run it verbosely always, and use chronic to hide +the successful output. + + 0 1 * * * chronic backup # instead of backup >/dev/null 2>&1 + +=head1 AUTHOR + +Copyright 2010 by Joey Hess <joey@kitenet.net> + +Original concept and "chronic" name by Chuck Houpt. + +Licensed under the GNU GPL version 2 or higher. + +=cut + +use warnings; +use strict; +use IPC::Run qw( start pump finish timeout ); + +if (! @ARGV) { + die "usage: chronic COMMAND...\n"; +} + +my ($out, $err); +my $h = IPC::Run::start \@ARGV, \*STDIN, \$out, \$err; +$h->finish; +my $ret=$h->full_result; + +if ($ret >> 8) { # child failed + showout(); + exit ($ret >> 8); +} +elsif ($ret != 0) { # child killed by signal + showout(); + exit 1; +} +else { + exit 0; +} + +sub showout { + print STDOUT $out; + print STDERR $err; +} @@ -0,0 +1,167 @@ +#!/usr/bin/perl + +=head1 NAME + +combine - combine sets of lines from two files using boolean operations + +=head1 SYNOPSIS + +combine file1 and file2 + +combine file1 not file2 + +combine file1 or file2 + +combine file1 xor file2 + +_ file1 and file2 _ + +_ file1 not file2 _ + +_ file1 or file2 _ + +_ file1 xor file2 _ + +=head1 DESCRIPTION + +B<combine> combines the lines in two files. Depending on the boolean +operation specified, the contents will be combined in different ways: + +=over 4 + +=item and + +Outputs lines that are in file1 if they are also present in file2. + +=item not + +Outputs lines that are in file1 but not in file2. + +=item or + +Outputs lines that are in file1 or file2. + +=item xor + +Outputs lines that are in either file1 or file2, but not in both files. + +=back + +"-" can be specified for either file to read stdin for that file. + +The input files need not be sorted, and the lines are output in the order +they occur in file1 (followed by the order they occur in file2 for the two +"or" operations). Bear in mind that this means that the operations are not +commutative; "a and b" will not necessarily be the same as "b and a". To +obtain commutative behavior sort and uniq the result. + +Note that this program can be installed as "_" to allow for the syntactic +sugar shown in the latter half of the synopsis (similar to the test/[ +command). It is not currently installed as "_" by default, but you can +alias it to that if you like. + +=head1 SEE ALSO + +join(1) + +=head1 AUTHOR + +Copyright 2006 by Joey Hess <joey@kitenet.net> + +Licensed under the GNU GPL. + +=cut + +use warnings; +use strict; + +sub filemap { + my $file=shift; + my $sub=shift; + + open (IN, $file) || die "$file: $!\n"; + while (<IN>) { + chomp; + $sub->(); + } + close IN; +} + +sub hashify { + my $file=shift; + + my %seen; + filemap $file, sub { $seen{$_}++ }; + return \%seen; +} + +sub compare_or { + my ($file1, $file2) = @_; + + filemap $file1, sub { print "$_\n" }; + filemap $file2, sub { print "$_\n" }; +} + +sub compare_xor { + my ($file1, $file2) = @_; + + my (@lines2, %seen2); + filemap $file2, + sub { + push @lines2, $_; + $seen2{$_} = 1; + }; + + # Print all lines in file1 that are not in file2, + # and mark lines that are in both files by setting + # their value in %seen2 to 0. + filemap $file1, + sub { + if (exists $seen2{$_}) { + $seen2{$_} = 0; + } + else { + print "$_\n"; + } + }; + + # Print all lines that are in file2 but not in file1. + # The value of these lines in seen2 is set to 1. + foreach (@lines2) { + print "$_\n" if $seen2{$_}; + } +} + +sub compare_not { + my ($file1, $file2) = @_; + + my $seen=hashify($file2); + filemap $file1, sub { print "$_\n" unless $seen->{$_} }; +} + +sub compare_and { + my ($file1, $file2) = @_; + + my $seen=hashify($file2); + filemap $file1, sub { print "$_\n" if $seen->{$_} }; +} + +if (@ARGV >= 4 && $ARGV[3] eq "_") { + delete $ARGV[3]; +} + +if (@ARGV != 3) { + die "Usage: combine file1 OP file2\n"; +} + +my $file1=shift; +my $op=lc shift; +my $file2=shift; + +if ($::{"compare_$op"}) { + no strict 'refs'; + "compare_$op"->($file1, $file2); +} +else { + die "unknown operation, $op\n"; +} diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7559ffb --- /dev/null +++ b/debian/changelog @@ -0,0 +1,450 @@ +moreutils (0.49) unstable; urgency=low + + * ts: Fix timezone used with -i + * errno, isutf8: Fix zero-termination of option list, which could lead + to getopt crash. Closes: #715867 + (Thanks, Mayhem Team) + + -- Joey Hess <joeyh@debian.org> Wed, 10 Jul 2013 16:21:41 -0400 + +moreutils (0.48) unstable; urgency=low + + * Allow overriding PREFIX and CFLAGS to make the build more flexible + (Thanks, Ben Walton) + * Typo. Closes: #697113 + * ts: -i enables incremental timestamping. + Thanks, Thomas Vander Stichele + * ts: Support single-digit day dates. + Thanks, Peter Lunicks + * sponge: Check fclose to detect certian short reads. Closes: #704453 + + -- Joey Hess <joeyh@debian.org> Sat, 04 May 2013 23:50:03 -0400 + +moreutils (0.47) unstable; urgency=low + + * errno: New utility that looks up errno numbers or names. + + -- Joey Hess <joeyh@debian.org> Mon, 25 Jun 2012 16:31:17 -0400 + +moreutils (0.46) unstable; urgency=low + + * Typo. Closes: #649158 + * combine: Avoid reading files twice, to support data coming from + pipes. Closes: #667960 Thanks, Carsten Hey + + -- Joey Hess <joeyh@debian.org> Mon, 09 Apr 2012 16:19:34 -0400 + +moreutils (0.45) unstable; urgency=low + + * ts: Support %.s for seconds sinch epoch with subsecond resolution. + Closes: #619764 + + -- Joey Hess <joeyh@debian.org> Sun, 19 Jun 2011 15:44:28 -0400 + +moreutils (0.44) unstable; urgency=low + + * pee: Propigate exit status of commands run. + + -- Joey Hess <joeyh@debian.org> Thu, 10 Mar 2011 17:38:14 -0400 + +moreutils (0.43) unstable; urgency=low + + * chronic: New command, runs a command quietly, unless it fails. + * Now depends on IPC::Run, used by chronic. + + -- Joey Hess <joeyh@debian.org> Fri, 29 Oct 2010 15:54:37 -0400 + +moreutils (0.42) unstable; urgency=low + + * sponge: Guarantee that output file is always updated atomically, + by renaming a temp file into place. Closes: #592144 + * sponge: Ensure that output file permissions are always preserved + if it already exists. + * Typo. Closes: #596032 + + -- Joey Hess <joeyh@debian.org> Wed, 06 Oct 2010 20:03:25 -0400 + +moreutils (0.41) unstable; urgency=low + + * ifdata.docbook: Mark interface as required in synopsis. Closes: #588397 + * Add missing AUTHOR section to docbook man pages. + * sponge: Correct bad use of fread that caused a trailing quantity of + soaked data to be silently discarded when a temp file was used + and sponge output to stdout. Closes: #595220 + + -- Joey Hess <joeyh@debian.org> Thu, 02 Sep 2010 16:47:10 -0400 + +moreutils (0.40) unstable; urgency=low + + * lckdo: Now deprecated, since util-linux's flock(1) can do the same + thing. + * parallel: -i will now replace {} inside parameters, before the {} had + to be a separate parameter. + + -- Joey Hess <joeyh@debian.org> Tue, 06 Jul 2010 19:38:14 -0400 + +moreutils (0.39) unstable; urgency=low + + * parallel: Fix exit code handling when commands are specified after -- + * parallel: Make -j 0 do something reasonable (start all jobs at once). + * parallel: Fix to really avoid running new jobs when load is too high. + * parallel: Fix logic error in code handling -l that could make parallel + return a bogus 255 exit code when all jobs succeeded. Closes: #569617 + * parallel: Allow a decimal load value to be specified with -l + * Caps sillyness. Closes: #570815 + * zrun: Add support for .xz files. + + -- Joey Hess <joeyh@debian.org> Tue, 23 Feb 2010 15:51:26 -0500 + +moreutils (0.38) unstable; urgency=low + + * Description improvements. Closes: #549450 + (Thanks, Justin B Rye) + * parallel: Allow running independent commands, + like `parallel -j3 -- ls df "echo hi"` + * ifdata: Add FreeBSD kernel support, although some of the more esoteric + interface options are not currently supported in FreeBSD. + * parallel: Define WEXITED to allow building on FreeBSD kernel. + * Thanks Enrico Tassi for the FreeBSD kernel support, which should + be enough to get moreutils built on Debian kFreeBSD. Closes: #562609 + + -- Joey Hess <joeyh@debian.org> Tue, 09 Feb 2010 15:50:42 -0500 + +moreutils (0.37) unstable; urgency=low + + * parallel: Clarify man page regarding CPUs. Closes: #536597 + * parallel: Add -n option. Thanks, Pierre Habouzit. Closes: #537992 + (As a side effect, fixes a segfault if -- was omitted.) + * parallel.1: Typo fixes. Closes: #538147 + + -- Joey Hess <joeyh@debian.org> Sat, 25 Jul 2009 10:18:40 +0200 + +moreutils (0.36) unstable; urgency=low + + * parallel: New program, contributed by Tollef Fog Heen, + that can run multiple jobs in parallel, optionally checking + load average. + * mispipe: Fix closing of extra pipe FD before starting command + so it is not inherited by daemons. Closes: #533448 + (Thanks, Jeremie Koenig) + + -- Joey Hess <joeyh@debian.org> Fri, 10 Jul 2009 11:01:41 -0400 + +moreutils (0.35) unstable; urgency=low + + * ifdata: Don't assume that all interface names are 6 characters or less, + for instance "wmaster0" is longer. Increase the limit to 20 characters. + Closes: #526654 (Thanks, Alan Pope) + * isutf8: Reject UTF-8-encoded UTF-16 surrogates. Closes: #525301 + (Thanks, Jakub Wilk and liw) + + -- Joey Hess <joeyh@debian.org> Tue, 05 May 2009 15:06:41 -0400 + +moreutils (0.34) unstable; urgency=low + + * vipe: Avoid dying on empty input. Thanks, Anders Kaseorg + Closes: #508491 + + -- Joey Hess <joeyh@debian.org> Thu, 11 Dec 2008 15:11:23 -0500 + +moreutils (0.33) unstable; urgency=low + + * Support installing moreutils into prefixes other than /usr (Evan Broder) + * Fix zrun breakage introduced last version. Closes: #504129 + + -- Joey Hess <joeyh@debian.org> Fri, 31 Oct 2008 17:01:03 -0400 + +moreutils (0.32) unstable; urgency=low + + * zrun: Can be linked to zsomeprog to run the equivilant of zrun someprog. + Closes: #411623 (Stefan Fritsch) + * zrun: Add support for lzma and lzo. (Stefan Fritsch) + * Fix pod error in vidir(1). + + -- Joey Hess <joeyh@debian.org> Sun, 26 Oct 2008 23:43:30 -0400 + +moreutils (0.31) unstable; urgency=low + + * pee.1: Document difference with tee in stdout. + * ts: Support displaying fractional seconds via a "%.S" conversion + specification. Closes: #482789 + + -- Joey Hess <joeyh@debian.org> Sat, 28 Jun 2008 23:19:31 -0400 + +moreutils (0.30) unstable; urgency=low + + * debhelper v7; rules file minimisation + * Use DESTDIR instead of PREFIX. + * Add a DOCBOOK2XMAN setting. (Greg KH) + * ifne: Add -n which makes it run the command if stdin is empty. + * ifne: If no command is specified, print usage information. + + -- Joey Hess <joeyh@debian.org> Wed, 14 May 2008 19:37:42 -0400 + +moreutils (0.29) unstable; urgency=low + + * Add ifne, contributed by Javier Merino. + * sponge, ifne: Ensure that suspending/resuming doesn't + result in partial writes of the data, by using fwrite() + rather than write(). + * sponge: Handle large data sizes by using a temp file rather than by + consuming arbitrary amounts of memory. Patch by Brock Noland. + * ts: Allow both -r and a format to be specified, to parse dates and output + in a specified format. + * ts: Fix bug in timezone regexp. + + -- Joey Hess <joeyh@debian.org> Tue, 15 Apr 2008 15:30:52 -0400 + +moreutils (0.28) unstable; urgency=low + + * Moved to a git repository. + * vidir: Applied patch from Stefan Fritsch (one part of #412176): + - Check for control characters (especially newlines) in filenames + and error out, since this can greatly confuse the editor or vidir. + - If the source of a rename does not exist (and thus the rename will fail + anyway), vidir should not move an existing target file to a tmpfile. + - If a directory is renamed, vidir should take that into account when + renaming files in this directory. + - If a directory name is passed as name/ to vidir, vidir should not + add second slash after the name. + * vidir: Add support for unlinking directories. To recursivly delete + a directory and its contents, pipe find to vidir, and delete the directory + and its contents in the editor. Closes: #412176 + * Add example to man page about recursive modification of directories. + Closes: #390099 + + -- Joey Hess <joeyh@debian.org> Sat, 02 Feb 2008 17:26:23 -0500 + +moreutils (0.27) unstable; urgency=low + + * vidir: Check exit codes of close. Closes: #463739 + + -- Joey Hess <joeyh@debian.org> Sat, 02 Feb 2008 16:42:18 -0500 + +moreutils (0.26) unstable; urgency=low + + * isutf8: Correct inverted exit code when passed a file to check. + Closes: #453306 + + -- Joey Hess <joeyh@debian.org> Wed, 28 Nov 2007 14:19:32 -0500 + +moreutils (0.25) unstable; urgency=low + + * isutf8: Detect and reject overlong UTF-8 sequences. Closes: #440951 + Many thanks to liw for the patch. + + -- Joey Hess <joeyh@debian.org> Mon, 12 Nov 2007 11:58:07 -0500 + +moreutils (0.24) unstable; urgency=low + + * vidir: Force numbers to normalised integers. + * vidir: Abort on unknown item numbers rather than deleting them. + Closes: #442440 + + -- Joey Hess <joeyh@debian.org> Sun, 16 Sep 2007 13:05:54 -0400 + +moreutils (0.23) unstable; urgency=low + + * Add pointer to join from combine's man page. Closes: #435516 + * Don't strip binaries for debian package if built with + DEB_BUILD_OPTIONS=nostrip. Closes: #437577 + * Include Michael Tokarev's lckdo program and replace / conflict with the + current lckdo package. (Robert Edmonds) Closes: #432906 + * lckdo: Don't clear other flags when setting close on exec. + + -- Joey Hess <joeyh@debian.org> Wed, 05 Sep 2007 21:45:25 -0400 + +moreutils (0.22) unstable; urgency=low + + * vidir, zrun: Don't put temp files in the current directory. Closes: #429367 + + -- Joey Hess <joeyh@debian.org> Mon, 25 Jun 2007 16:11:27 -0400 + +moreutils (0.21) unstable; urgency=low + + * Patch from Sergej Pupykin fixing ifdata -pN. + + -- Joey Hess <joeyh@debian.org> Mon, 25 Jun 2007 13:07:21 -0400 + +moreutils (0.20) unstable; urgency=low + + * Typo fixes from Ralf Wildenhues. + * ifdata: Add -bips and -bops options contributed by André Appel, + to print the number of bytes of incoming/outgoing traffic per second. + + -- Joey Hess <joeyh@debian.org> Sat, 23 Dec 2006 15:55:45 -0500 + +moreutils (0.19) unstable; urgency=low + + * vidir: Don't ignore files whose names end in dots. Closes: #398141 + Thanks, Bram Sanders. + + -- Joey Hess <joeyh@debian.org> Sat, 11 Nov 2006 22:02:17 -0500 + +moreutils (0.18) unstable; urgency=low + + * mispipe.docbook: Typo. Closes: #386756 + * spongs: Output to stdout if no file is specified, useful in a pipeline + such as: cvs diff | sponge | patch -R -p0 + Closes: #387501 + + -- Joey Hess <joeyh@debian.org> Thu, 14 Sep 2006 15:13:46 -0400 + +moreutils (0.17) unstable; urgency=low + + * Add missing \n to sponge usage. Closes: #383944 + * Add mispipe, contributed by Nathanael Nerode. Pipes together two commands, + returning the exit status of the first. + + -- Joey Hess <joeyh@debian.org> Thu, 7 Sep 2006 20:49:16 -0400 + +moreutils (0.16) unstable; urgency=low + + * Change the default ts format to include the month and day, for consistency + with syslog format. + * Add -r switch to ts, which makes it convert existing timestamps in + the input into relative times, such as "15m2s ago". Many common date + formats are supported. + + -- Joey Hess <joeyh@debian.org> Sat, 19 Aug 2006 15:27:42 -0400 + +moreutils (0.15) unstable; urgency=low + + * Remove notes about potential tools from README, moved to wiki. + * vidir: Don't abort if it sees an empty or all-whitespace line. + * vidir: If just a filename is removed and the number is left, + treat this the same as removing the whole line, and delete the file, + instead of trying to rename the file to "". + * vidir: Remove the periods after the item numbers. + * vidir: Man page improvements. Closes: #378122 + * combine: Man page improvements, to clarify even more that order does + matter and that the operations are not commutative. Closes: #361123 + * combine: The behavior of "or" was fairly strange if lines were repeated in + a file. Changed behavior to just print all lines from both files, even + if this means printing dups. Not sure I like this behavior either, but + it's consistent with the very useful behaviors of "and" and "not". + * vidir, vipe: Use /usr/bin/editor if it's present, and EDITOR and VISUAL + arn't set, to comply with Debian policy. For portability, fall back to vi + if there's no /usr/bin/editor. Closes: #378623 + + -- Joey Hess <joeyh@debian.org> Tue, 25 Jul 2006 22:26:56 -0400 + +moreutils (0.14) unstable; urgency=low + + * vidir: Also support EDITOR or VISUAL that contains spaces and parameters. + + -- Joey Hess <joeyh@debian.org> Wed, 12 Jul 2006 13:35:49 -0400 + +moreutils (0.13) unstable; urgency=low + + * ifdata: typo + * vipe: Support EDITOR or VISUAL that contains spaces and parameters. + + -- Joey Hess <joeyh@debian.org> Wed, 12 Jul 2006 12:53:44 -0400 + +moreutils (0.12) unstable; urgency=low + + * Really fix typo. Closes: #369485 + * zrun: Add usage message. + * ifdata: Fix bug in argument parsing that could make it segfault. + * combine: Allow operators to be written in any case. + + -- Joey Hess <joeyh@debian.org> Wed, 28 Jun 2006 14:04:49 -0400 + +moreutils (0.11) unstable; urgency=low + + * combine: better synopsis. Closes: #360544 + * ifdata: basically rewritten by Adam Lackorzynski, code size is 40% + smaller, macros removed, return checks added for all lib calls. + Closes: #361728 + * vidir: behave properly if passed a directory name to edit. Closes: #369156 + * check-isutf8: needs to be a bash script, the way it encodes characters + won't work with dash. + * check-isutf8: exit nonzero if any tests fail + + -- Joey Hess <joeyh@debian.org> Sat, 27 May 2006 19:12:50 -0400 + +moreutils (0.10) unstable; urgency=low + + * ifdata: patch from Adam Lackorzynski to translate French error messages + to English and to convert some #defines to enums. + * ifdata: patch from Adam Lackorzynski to avoid printing info for + bogus interface. Closes: #360433 + * Add zrun, contributed by Chung-chieh Shan (you might prefer to alias it to + "z"). + + -- Joey Hess <joeyh@debian.org> Sun, 2 Apr 2006 18:34:01 -0400 + +moreutils (0.9) unstable; urgency=low + + * ifdata: robustness patch from Adam Lackorzynski, in particular deal with + /proc not mounted. Closes: #358930 + * Typo fixes. Closes: #359039, #359040 + * pee: don't send a copy of every line sent to every command to stdout. + Seems best for it not to output anything to stdout on its on at all. + Closes: #359041 + + -- Joey Hess <joeyh@debian.org> Mon, 27 Mar 2006 13:45:45 -0500 + +moreutils (0.8) unstable; urgency=low + + * Back to Mithandir's C sponge, now fixed. + * tss.1, ifdata.1: typo fix. Closes: #358557, #358556 + * ifdata: patch from its author to make it behave properly on big endian + systems. Closes: #358860 + + -- Joey Hess <joeyh@debian.org> Fri, 24 Mar 2006 20:52:21 -0500 + +moreutils (0.7) unstable; urgency=low + + * Add pee (pipe tee) contributed by Miek Gieben. + * ifdata: Patch from KELEMEN Peter to add support for printing hardware + interface. Closes: #357646 + + -- Joey Hess <joeyh@debian.org> Sat, 18 Mar 2006 14:17:08 -0500 + +moreutils (0.6) unstable; urgency=low + + * Revert to perl sponge since the C one corrupts larger files. + Hopefully temporary.. + + -- Joey Hess <joeyh@debian.org> Fri, 10 Mar 2006 15:33:43 -0500 + +moreutils (0.5) unstable; urgency=low + + * Added ifdata, by Benjamin BAYART (originally called ifcfg). + * Made ifdata -Wall clean. + * Made ifdata support -h and print usage on unknown options. + * Cleaned up ifdata's behavior when asked to print info for nonexistant + devices. Still needs improvement. + * Indentation improvements. + * Replaced and(1) and not(1) by combine, based on an idea by Matt Taggart. + + -- Joey Hess <joeyh@debian.org> Tue, 7 Mar 2006 23:02:14 -0500 + +moreutils (0.4) unstable; urgency=low + + * Added versions of and(1) and not(1) that support arbitrary numbers of + input files. + + -- Joey Hess <joeyh@debian.org> Sun, 5 Mar 2006 22:28:13 -0500 + +moreutils (0.3) unstable; urgency=low + + * Switch sponge to a C implementation by mithandir. + * Added a list of prospective tools that I am considering adding to the + README file. + + -- Joey Hess <joeyh@debian.org> Fri, 3 Mar 2006 18:09:46 -0500 + +moreutils (0.2) unstable; urgency=low + + * Build dep on docbook-xml. + + -- Joey Hess <joeyh@debian.org> Sun, 19 Feb 2006 18:40:56 -0500 + +moreutils (0.1) unstable; urgency=low + + * First release. + + -- Joey Hess <joeyh@debian.org> Sun, 12 Feb 2006 17:11:21 -0500 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..aa5f025 --- /dev/null +++ b/debian/control @@ -0,0 +1,35 @@ +Source: moreutils +Section: utils +Priority: optional +Build-Depends: debhelper (>= 7), dpkg-dev (>= 1.9.0), docbook2x, docbook-xml +Maintainer: Joey Hess <joeyh@debian.org> +Standards-Version: 3.9.2 +Vcs-Git: git://git.kitenet.net/moreutils +Homepage: http://kitenet.net/~joey/code/moreutils/ + +Package: moreutils +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, libipc-run-perl +Suggests: libtime-duration-perl, libtimedate-perl +Conflicts: lckdo +Replaces: lckdo +Description: additional Unix utilities + This is a growing collection of the Unix tools that nobody thought + to write long ago, when Unix was young. + . + So far, it includes the following utilities: + - chronic: runs a command quietly unless it fails + - combine: combine the lines in two files using boolean operations + - errno: look up errno names and descriptions + - ifdata: get network interface info without parsing ifconfig output + - ifne: run a program if the standard input is not empty + - isutf8: check if a file or standard input is utf-8 + - lckdo: execute a program with a lock held + - mispipe: pipe two commands, returning the exit status of the first + - parallel: run multiple jobs at once + - pee: tee standard input to pipes + - sponge: soak up standard input and write to a file + - ts: timestamp standard input + - vidir: edit a directory in your text editor + - vipe: insert a text editor into a pipe + - zrun: automatically uncompress arguments to command diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..56e9c1f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,78 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: Contributed by many folks. + +Files: * +Copyright: 2006-2011 Joey Hess <joeyh@debian.org> +License: GPL-2+ + +Files: isutf8.* +Copyright: 2005 Lars Wirzenius +License: GPL-2+ + +Files: sponge.* +Copyright: 2006 Tollef Fog Heen +License: GPL-2 + +Files: ifdata.c +Copyright: 2002 Benjamin BAYART +License: GPL-2+ + +Files: pee.c +Copyright: 2006 Miek Gieben +License: GPL-2+ + +Files: zrun +Copyright: 2006 Chung-chieh Shan +License: GPL-2+ + +Files: mispipe.c +Copyright: 2004 Nathanael Nerode +License: GPL-2+ or Expat + +Files: lckdo.c lckdo.docbook +Copyright: Michael Tokarev +License: other + Public domain + +Files: ifne.c ifne.docbook +Copyright: 2008 Javier Merino +License: GPL-2+ + +Files: parallel.c +Copyright: 2008 Tollef Fog Heen <tfheen@err.no> +License: GPL-2 + +Files: errno.c errno.docbook +Copyright: 2012 Lars Wirzenius +License: GPL-2+ + +Files: physmem.c +Copyright: 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. +License: GPL-2+ + +License: GPL-2 + The full text of the GPL is distributed as COPYING in moreutils's source, + and is distributed in /usr/share/common-licenses/GPL-2 on Debian systems. + +License: GPL-2+ + The full text of the GPL is distributed as COPYING in moreutils's source, + and is distributed in /usr/share/common-licenses/GPL-2 on Debian systems. + +License: Expat + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..cc50779 --- /dev/null +++ b/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +# Prevent the makefile from stripping, in case it's being built in +# unstripped mode. +export INSTALL_BIN=install + +%: + dh $@ + +# Not intended for use by anyone except the author. +announcedir: + @echo ${HOME}/src/joeywiki/code/moreutils/news @@ -0,0 +1,228 @@ +/* + * errno.c -- look up errno names and descriptions + * Copyright 2012 Lars Wirzenius (liw@iki.fi) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#include <ctype.h> +#include <errno.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + + +static struct { + const char *name; + int code; +} errnos[] = { + #include "errnos.h" +}; +static const int num_errnos = sizeof(errnos) / sizeof(errnos[0]); + + +static void +report(const char *name, int code) +{ + printf("%s %d %s\n", name, code, strerror(code)); +} + + +static bool +report_from_name(const char *name) +{ + int i; + for (i = 0; i < num_errnos; ++i) { + if (strcasecmp(errnos[i].name, name) == 0) { + report(errnos[i].name, errnos[i].code); + return true; + } + } + return false; +} + + +static bool +report_from_code(int code) +{ + int i; + for (i = 0; i < num_errnos; ++i) { + if (errnos[i].code == code) { + report(errnos[i].name, code); + return true; + } + } + return false; +} + + +static bool +matches(int code, int num_words, char **words) +{ + const char *text = strerror(code); + int i; + + for (i = 0; i < num_words; ++i) { + if (strcasestr(text, words[i]) == NULL) + return false; + } + return true; +} + + +static void +search(int num_words, char **words) +{ + int i; + + for (i = 0; i < num_errnos; ++i) { + if (matches(errnos[i].code, num_words, words)) + report(errnos[i].name, errnos[i].code); + } +} + + +static void +search_all(int num_words, char **words) +{ + FILE *f; + + /* Static buffers are ugly, but they're simple. If anyone has a + locale name longer than a kilobyte, they will suffer, and they + will complain, and then I will fix this. */ + char line[1024]; + + f = popen("locale -a", "r"); + if (f == NULL) { + fprintf(stderr, "ERROR: Can't execute locale -a: %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); + } + + while (fgets(line, sizeof line, f) != NULL) { + line[strcspn(line, "\n")] = '\0'; + setlocale(LC_ALL, line); + search(num_words, words); + } + + fclose(f); +} + + +static struct option +options[] = { + { "help", 0, NULL, 'h' }, + { "list", 0, NULL, 'l' }, + { "search", 0, NULL, 's' }, + { "search-all-locales", 0, NULL, 'S' }, + { 0, 0, 0, 0 } +}; + + +static void +usage(void) +{ + printf("Usage: errno [-lsS] [--list] [--search] [--search-all-locales] " + "[keyword]\n"); +} + + +int +main(int argc, char **argv) +{ + int i; + int exit_code; + int index = 0; + enum { + lookup_mode, + list_mode, + search_mode, + search_all_mode + } mode = lookup_mode; + + setlocale(LC_ALL, ""); + + for (;;) { + int c = getopt_long(argc, argv, "hlsS", options, &index); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(); + return EXIT_SUCCESS; + + case 'l': + mode = list_mode; + break; + + case 's': + mode = search_mode; + break; + + case 'S': + mode = search_all_mode; + break; + + case '?': + break; + + default: + fprintf(stderr, "getopt returned 0x%02x\n", c); + return EXIT_FAILURE; + } + } + + exit_code = EXIT_SUCCESS; + + switch (mode) { + case lookup_mode: + for (i = optind; i < argc; ++i) { + const char *arg = argv[i]; + if (toupper(arg[0]) == 'E') { + if (!report_from_name(arg)) + exit_code = EXIT_FAILURE; + } else if (isdigit(arg[0])) { + if (!report_from_code(atoi(arg))) + exit_code = EXIT_FAILURE; + } else { + fprintf(stderr, "ERROR: Not understood: %s\n", arg); + exit_code = EXIT_FAILURE; + } + } + break; + + case list_mode: + for (i = 0; i < num_errnos; ++i) + report(errnos[i].name, errnos[i].code); + break; + + case search_mode: + search(argc - optind, argv + optind); + break; + + case search_all_mode: + search_all(argc - optind, argv + optind); + break; + } + + return exit_code; +} diff --git a/errno.docbook b/errno.docbook new file mode 100644 index 0000000..63ae492 --- /dev/null +++ b/errno.docbook @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2012 Lars Wirzenius (liw@iki.fi) + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>liw@liw.fi</email> + </address> + <author> + <firstname>Lars</firstname> + <surname>Wirzenius</surname> + </author> + <date>2012-06-05</date> + </refentryinfo> + + <refmeta> + <refentrytitle>errno</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>errno</refname> + <refpurpose>look up errno names and descriptions</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>errno</command> + <arg choice="req"><replaceable>name-or-code</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>errno</command> + <arg>-ls</arg> + <arg>--list</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>errno</command> + <arg>-s</arg> + <arg>--search</arg> + <arg choice="req"><replaceable>word</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>errno</command> + <arg>-S</arg> + <arg>--search-all-locales</arg> + <arg choice="req"><replaceable>word</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>errno</command> looks up errno macro names, + errno codes, and the corresponding descriptions. For example, + if given <literal>ENOENT</literal> on a Linux system, it + prints out the code 2 and the description "No such file or directory". + If given the code 2, it printes <literal>ENOENT</literal> + and the same description.</para> + + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term><option>-l</option></term> + <term><option>--list</option></term> + <listitem> + <para>List all errno values.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-s</option></term> + <term><option>--search</option></term> + <listitem> + <para>Search for errors whose description contains + all the given words (case-insensitive).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-S</option></term> + <term><option>--search-all-locales</option></term> + <listitem> + <para>Like <option>--search</option>, but searches all + installed locales.</para> + </listitem> + </varlistentry> + </variablelist> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + Lars Wirzenius + <para> + + </para> + </refsect1> + + <refsect1> + <title>SEE ALSO</title> + + <para> + <citerefentry> + <refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum> + </citerefentry> + </para> + + </refsect1> +</refentry> diff --git a/ifdata.c b/ifdata.c new file mode 100644 index 0000000..2de98a0 --- /dev/null +++ b/ifdata.c @@ -0,0 +1,546 @@ +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <netdb.h> +#include <sys/ioctl.h> + +#if defined(__linux__) + #include <linux/sockios.h> + #include <linux/if.h> +#endif + +#if defined(__FreeBSD_kernel__) + #include <net/if.h> +#endif + +#include <netinet/in.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +enum { + DO_EXISTS = 1, + DO_PEXISTS, + DO_PADDRESS, + DO_PMASK, + DO_PMTU, + DO_PCAST, + DO_PALL, + DO_PFLAGS, + DO_SINPACKETS, + DO_SINBYTES, + DO_SINERRORS, + DO_SINDROPS, + DO_SINALL, + DO_SINFIFO, + DO_SINFRAME, + DO_SINCOMPRESSES, + DO_SINMULTICAST, + DO_SOUTALL, + DO_SOUTBYTES, + DO_SOUTPACKETS, + DO_SOUTERRORS, + DO_SOUTDROPS, + DO_SOUTFIFO, + DO_SOUTCOLLS, + DO_SOUTCARRIER, + DO_SOUTMULTICAST, + DO_PNETWORK, + DO_PHWADDRESS, + DO_BIPS, + DO_BOPS +}; + +struct if_stat { + unsigned long long in_packets, in_bytes, in_errors, in_drops; + unsigned long long in_fifo, in_frame, in_compress, in_multicast; + unsigned long long out_bytes, out_packets, out_errors, out_drops; + unsigned long long out_fifo, out_colls, out_carrier, out_multicast; +}; + + +void print_quad_ipv4(in_addr_t i) { + i = ntohl(i); + printf("%d.%d.%d.%d", + (i & 0xff000000) >> 24, + (i & 0x00ff0000) >> 16, + (i & 0x0000ff00) >> 8, + (i & 0x000000ff)); +} + +void print_quad_ipv6(uint16_t *a) { + printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); +} + +void print_quad(struct sockaddr *adr) { + switch (adr->sa_family) { + case AF_INET: + print_quad_ipv4(((struct sockaddr_in*)adr)->sin_addr.s_addr); + break; + case AF_INET6: + print_quad_ipv6(((struct sockaddr_in6*)adr)->sin6_addr.s6_addr16); + break; + default: + printf("NON-IP"); + break; + } +} + +enum print_error_enum { + PRINT_ERROR, + PRINT_NO_ERROR, +}; + +/** + * return 0 success + * 1 error + */ +static int do_socket_ioctl(const char *ifname, const unsigned long int request, + struct ifreq *req, int *ioctl_errno, + const enum print_error_enum print_error) { + int sock, res; + + if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) + return 1; + strncpy(req->ifr_name, ifname, IFNAMSIZ); + req->ifr_name[IFNAMSIZ - 1] = 0; + + if ((res = ioctl(sock, request, req)) == -1) { + if (ioctl_errno) + *ioctl_errno = errno; + if (print_error == PRINT_ERROR) + fprintf(stderr, "ioctl on %s: %s\n", ifname, strerror(errno)); + close(sock); + return 1; + } + + close(sock); + + return 0; +} + +int if_exists(const char *iface) { + struct ifreq r; + return !do_socket_ioctl(iface, SIOCGIFFLAGS, &r, NULL, PRINT_NO_ERROR); +} + +#if defined(__linux__) + +void if_flags(const char *iface) { + struct ifreq r; + unsigned int i; + const struct { + unsigned int flag; + char *name; + } flags[] = { + { IFF_UP, "Up" }, + { IFF_BROADCAST, "Broadcast" }, + { IFF_DEBUG, "Debugging" }, + { IFF_LOOPBACK, "Loopback" }, + { IFF_POINTOPOINT, "Ppp" }, + { IFF_NOTRAILERS, "No-trailers" }, + { IFF_RUNNING, "Running" }, + { IFF_NOARP, "No-arp" }, + { IFF_PROMISC, "Promiscuous" }, + { IFF_ALLMULTI, "All-multicast" }, + { IFF_MASTER, "Load-master" }, + { IFF_SLAVE, "Load-slave" }, + { IFF_MULTICAST, "Multicast" }, + { IFF_PORTSEL, "Port-select" }, + { IFF_AUTOMEDIA, "Auto-detect" }, + { IFF_DYNAMIC, "Dynaddr" }, + { 0xffff0000, "Unknown-flags" }, + }; + + if (do_socket_ioctl(iface, SIOCGIFFLAGS, &r, NULL, PRINT_ERROR)) + return; + + for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) + printf("%s%s%s", (r.ifr_flags & flags[i].flag) ? "On " : "Off ", + flags[i].name, + sizeof(flags) / sizeof(flags[0]) - 1 == i ? "" : "\n"); +} + +void if_hwaddr(const char *iface) { + struct ifreq r; + unsigned char *hwaddr; + + if (do_socket_ioctl(iface, SIOCGIFHWADDR, &r, NULL, PRINT_ERROR)) + return; + + hwaddr = (unsigned char *)r.ifr_hwaddr.sa_data; + printf("%02X:%02X:%02X:%02X:%02X:%02X", + hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); +} + +#endif + +static struct sockaddr *if_addr_value(const char *iface, struct ifreq *r, + unsigned long int request) { + int e; + + if (do_socket_ioctl(iface, request, r, &e, PRINT_NO_ERROR)) { + if (e == EADDRNOTAVAIL) + return &r->ifr_addr; + return NULL; + } + return &r->ifr_addr; +} + +struct sockaddr *if_addr(const char *iface, struct ifreq *r) { + return if_addr_value(iface, r, SIOCGIFADDR); +} + +struct sockaddr *if_mask(const char *iface, struct ifreq *r) { + return if_addr_value(iface, r, SIOCGIFNETMASK); +} + +struct sockaddr *if_bcast(const char *iface, struct ifreq *r) { + return if_addr_value(iface, r, SIOCGIFBRDADDR); +} + +struct sockaddr *if_network(const char *iface) { + struct sockaddr *saddr; + static struct ifreq req; + unsigned int mask; + + if (!(saddr = if_mask(iface, &req))) + return NULL; + + mask = ((struct sockaddr_in*)saddr)->sin_addr.s_addr; + + if (!(saddr = if_addr(iface, &req))) + return NULL; + + ((struct sockaddr_in*)saddr)->sin_addr.s_addr &= mask; + return saddr; +} + +int if_mtu(const char *iface) { + static struct ifreq req; + + if (do_socket_ioctl(iface, SIOCGIFMTU, &req, NULL, PRINT_ERROR)) + return 0; + + return req.ifr_mtu; +} + +#if defined(__linux__) + +static void skipline(FILE *fd) { + int ch; + do { + ch = getc(fd); + } while (ch != '\n' && ch != EOF); +} + +struct if_stat *get_stats(const char *iface) { + FILE *fd; + struct if_stat *ifstat; + char name[10]; + + if (!(ifstat = malloc(sizeof(struct if_stat)))) { + perror("malloc"); + return NULL; + } + + if ((fd = fopen("/proc/net/dev", "r")) == NULL) { + perror("fopen(\"/proc/net/dev\")"); + free(ifstat); + return NULL; + } + + /* Skip header */ + skipline(fd); + skipline(fd); + + do { + int items = fscanf(fd, + " %20[^:]:%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu", + name, + &ifstat->in_bytes, &ifstat->in_packets, + &ifstat->in_errors, &ifstat->in_drops, + &ifstat->in_fifo, &ifstat->in_frame, + &ifstat->in_compress, &ifstat->in_multicast, + &ifstat->out_bytes, &ifstat->out_packets, + &ifstat->out_errors, &ifstat->out_drops, + &ifstat->out_fifo, &ifstat->out_colls, + &ifstat->out_carrier, &ifstat->out_carrier + ); + + if (items == -1) + break; + if (items != 17) { + fprintf(stderr, "Invalid data read, check!\n"); + break; + } + + if (!strncmp(name, iface, sizeof(name))) { + fclose(fd); + return ifstat; + } + } while (!feof(fd)); + + fclose(fd); + free(ifstat); + return NULL; +} + +#endif + +const struct { + char *option; + unsigned int flag; + unsigned int is_stat; + char *description; +} options[] = { + { "-e", DO_EXISTS, 0, "Reports interface existence via return code" }, + { "-p", DO_PALL, 0, "Print out the whole config of iface" }, + { "-pe", DO_PEXISTS, 0, "Print out yes or no according to existence" }, + { "-pa", DO_PADDRESS, 0, "Print out the address" }, + { "-pn", DO_PMASK, 0, "Print netmask" }, + { "-pN", DO_PNETWORK, 0, "Print network address" }, + { "-pb", DO_PCAST, 0, "Print broadcast" }, + { "-pm", DO_PMTU, 0, "Print mtu" }, +#if defined(__linux__) + { "-ph", DO_PHWADDRESS, 0, "Print out the hardware address" }, + { "-pf", DO_PFLAGS, 0, "Print flags" }, + { "-si", DO_SINALL, 1, "Print all statistics on input" }, + { "-sip", DO_SINPACKETS, 1, "Print # of in packets" }, + { "-sib", DO_SINBYTES, 1, "Print # of in bytes" }, + { "-sie", DO_SINERRORS, 1, "Print # of in errors" }, + { "-sid", DO_SINDROPS, 1, "Print # of in drops" }, + { "-sif", DO_SINFIFO, 1, "Print # of in fifo overruns" }, + { "-sic", DO_SINCOMPRESSES, 1, "Print # of in compress" }, + { "-sim", DO_SINMULTICAST, 1, "Print # of in multicast" }, + { "-so", DO_SOUTALL, 1, "Print all statistics on output" }, + { "-sop", DO_SOUTPACKETS, 1, "Print # of out packets" }, + { "-sob", DO_SOUTBYTES, 1, "Print # of out bytes" }, + { "-soe", DO_SOUTERRORS, 1, "Print # of out errors" }, + { "-sod", DO_SOUTDROPS, 1, "Print # of out drops" }, + { "-sof", DO_SOUTFIFO, 1, "Print # of out fifo overruns" }, + { "-sox", DO_SOUTCOLLS, 1, "Print # of out collisions" }, + { "-soc", DO_SOUTCARRIER, 1, "Print # of out carrier loss" }, + { "-som", DO_SOUTMULTICAST, 1, "Print # of out multicast" }, + { "-bips",DO_BIPS, 1, "Print # of incoming bytes per second" }, + { "-bops",DO_BOPS, 1, "Print # of outgoing bytes per second" }, +#endif +}; + +void usage(const char *name) { + unsigned int i; + + fprintf(stderr, "Usage: %s [options] iface\n", name); + for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + fprintf(stderr, " %5s %s\n", + options[i].option, options[i].description); + } +} + +void add_do(int *ndo, int **todo, int act) { + *todo = realloc(*todo, (*ndo+1) * sizeof(int)); + (*todo)[*ndo] = act; + *ndo += 1; +} + +static void print_addr(struct sockaddr *sadr) { + if (!sadr) { + fprintf(stderr, "Error\n"); + exit(1); + } + print_quad(sadr); +} + +struct if_stat *ifstats, *ifstats2 = NULL; + +void please_do(int ndo, int *todo, const char *ifname) { + int i; + static struct ifreq req; + if (!ndo) return; + // printf("I have %d items in my queue.\n",ndo); + for (i=0; i<ndo; i++) { + switch (todo[i]) { + case DO_EXISTS: + exit(!if_exists(ifname)); + case DO_PEXISTS: + printf("%s", if_exists(ifname) ? "yes" : "no"); + break; + case DO_PADDRESS: + print_addr(if_addr(ifname, &req)); + break; +#if defined(__linux__) + case DO_PHWADDRESS: + if_hwaddr(ifname); + break; + case DO_PFLAGS: + if_flags(ifname); + break; +#endif + case DO_PMASK: + print_addr(if_mask(ifname, &req)); + break; + case DO_PCAST: + print_addr(if_bcast(ifname, &req)); + break; + case DO_PMTU: + printf("%d", if_mtu(ifname)); + break; + case DO_PNETWORK: + print_addr(if_network(ifname)); + break; + case DO_PALL: + print_addr(if_addr(ifname, &req)); + printf(" "); + print_addr(if_mask(ifname, &req)); + printf(" "); + print_addr(if_bcast(ifname, &req)); + printf(" "); + printf("%d", if_mtu(ifname)); + break; +#if defined(__linux__) + case DO_SINPACKETS: + printf("%llu",ifstats->in_packets); + break; + case DO_SINBYTES: + printf("%llu",ifstats->in_bytes); + break; + case DO_SINERRORS: + printf("%llu",ifstats->in_errors); + break; + case DO_SINDROPS: + printf("%llu",ifstats->in_drops); + break; + case DO_SINFIFO: + printf("%llu",ifstats->in_fifo); + break; + case DO_SINFRAME: + printf("%llu",ifstats->in_frame); + break; + case DO_SINCOMPRESSES: + printf("%llu",ifstats->in_compress); + break; + case DO_SINMULTICAST: + printf("%llu",ifstats->in_multicast); + break; + case DO_SINALL: + printf("%llu %llu %llu %llu %llu %llu %llu %llu", + ifstats->in_bytes, ifstats->in_packets, + ifstats->in_errors, ifstats->in_drops, + ifstats->in_fifo, ifstats->in_frame, + ifstats->in_compress, ifstats->in_multicast); + break; + case DO_SOUTBYTES: + printf("%llu",ifstats->out_bytes); + break; + case DO_SOUTPACKETS: + printf("%llu",ifstats->out_packets); + break; + case DO_SOUTERRORS: + printf("%llu",ifstats->out_errors); + break; + case DO_SOUTDROPS: + printf("%llu",ifstats->out_drops); + break; + case DO_SOUTFIFO: + printf("%llu",ifstats->out_fifo); + break; + case DO_SOUTCOLLS: + printf("%llu",ifstats->out_colls); + break; + case DO_SOUTCARRIER: + printf("%llu",ifstats->out_carrier); + break; + case DO_SOUTMULTICAST: + printf("%llu",ifstats->out_multicast); + break; + case DO_BIPS: + if (ifstats2 == NULL) { + sleep(1); + ifstats2 = get_stats(ifname); + } + printf("%llu", ifstats2->in_bytes-ifstats->in_bytes); + break; + case DO_BOPS: + if (ifstats2 == NULL) { + sleep(1); + ifstats2 = get_stats(ifname); + } + printf("%llu", ifstats2->out_bytes-ifstats->out_bytes); + break; + case DO_SOUTALL: + printf("%llu %llu %llu %llu %llu %llu %llu %llu", + ifstats->out_bytes, ifstats->out_packets, + ifstats->out_errors, ifstats->out_drops, + ifstats->out_fifo, ifstats->out_colls, + ifstats->out_carrier, ifstats->out_multicast); + break; +#endif + default: + printf("Unknown command: %d", todo[i]); + break; + } + printf("\n"); + } +} + +int main(int argc, char *argv[]) { + int ndo=0; + int *todo=NULL; + char *ifname=NULL; + int narg = 0; + int do_stats = 0; + unsigned int i, found; + + if (argc == 1) { + usage(*argv); + return 1; + } + + while (narg < argc - 1) { + narg++; + + found = 0; + + for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + if (!strcmp(argv[narg], options[i].option)) { + add_do(&ndo, &todo, options[i].flag); + do_stats |= options[i].is_stat; + found = 1; + break; + } + } + + if (found) + continue; + + if (argv[narg][0] == '-') { + usage(*argv); + return 1; + } + else { + ifname = argv[narg]; + break; + } + } + + if (narg + 1 < argc || !ifname) { + usage(*argv); + return 1; + } + +#if defined(__linux__) + if (do_stats && (ifstats = get_stats(ifname)) == NULL) { + fprintf(stderr, "Error getting statistics for %s\n", ifname); + return 1; + } +#endif + + please_do(ndo, todo, ifname); + + return 0; +} diff --git a/ifdata.docbook b/ifdata.docbook new file mode 100644 index 0000000..963943e --- /dev/null +++ b/ifdata.docbook @@ -0,0 +1,321 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2006 Joey Hess + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + + <refentryinfo> + <author> + <firstname>Joey</firstname> + <surname>Hess</surname> + </author> + <date>2006-03-07</date> + </refentryinfo> + + <refmeta> + <refentrytitle>ifdata</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>ifdata</refname> + <refpurpose>get network interface info without + parsing ifconfig output</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>ifdata</command> + <arg>options</arg> + <arg choice="req"><replaceable>iface</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para> + <command>ifdata</command> can be used to check for + the existence of a network interface, or to get + information abut the interface, such as its IP + address. Unlike <command>ifconfig</command> or + <command>ip</command>, <command>ifdata</command> + has simple to parse output that is designed to be + easily used by a shell script. + </para> + + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term><option>-h</option></term> + <listitem> + <para>Print out a help summary.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-e</option></term> + <listitem> + <para>Test to see if the interface exists, + exit nonzero if it does not.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-p</option></term> + <listitem> + <para>Prints out the whole configuration of + the interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pe</option></term> + <listitem> + <para>Prints "yes" or "no" if the interface + exists or not.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pa</option></term> + <listitem> + <para>Prints the IPv4 address of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pn</option></term> + <listitem> + <para>Prints the netmask of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pN</option></term> + <listitem> + <para>Prints the network address of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pb</option></term> + <listitem> + <para>Prints the broadcast address of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pm</option></term> + <listitem> + <para>Prints the MTU of the interface.</para> + </listitem> + </varlistentry> + + </variablelist> + + <para>Following options are Linux only.</para> + + <variablelist> + + <varlistentry> + <term><option>-ph</option></term> + <listitem> + <para>Prints the hardware address of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-pf</option></term> + <listitem> + <para>Prints the flags of the + interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-si</option></term> + <listitem> + <para>Prints out all the input statistics + of the interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sip</option></term> + <listitem> + <para>Prints the number of input packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sib</option></term> + <listitem> + <para>Prints the number of input bytes.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sie</option></term> + <listitem> + <para>Prints the number of input errors.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sid</option></term> + <listitem> + <para>Prints the number of dropped input + packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sif</option></term> + <listitem> + <para>Prints the number of input fifo overruns.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sic</option></term> + <listitem> + <para>Print the number of compressed input + packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sim</option></term> + <listitem> + <para>Prints the number of input + multicast packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-so</option></term> + <listitem> + <para>Prints out all the output statistics + of the interface.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sop</option></term> + <listitem> + <para>Prints the number of output packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sob</option></term> + <listitem> + <para>Prints the number of output bytes.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-soe</option></term> + <listitem> + <para>Prints the number of output errors.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sod</option></term> + <listitem> + <para>Prints the number of dropped + output packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sof</option></term> + <listitem> + <para>Prints the number of output fifo overruns.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-sox</option></term> + <listitem> + <para>Print the number of output collisions.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-soc</option></term> + <listitem> + <para>Prints the number of output carrier + losses.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-som</option></term> + <listitem> + <para>Prints the number of output multicast + packets.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-bips</option></term> + <listitem> + <para>Prints the number of bytes of + incoming traffic measured in one second.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-bops</option></term> + <listitem> + <para>Prints the number of bytes of + outgoing traffic measured in one second.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>AUTHOR</title> + + <para> + Benjamin BAYART + </para> + </refsect1> +</refentry> @@ -0,0 +1,127 @@ +/* + * + * Copyright 2008 Javier Merino <cibervicho@gmail.com> + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <string.h> +#define streq(a, b) (!strcmp((a), (b))) + +static void stdin_to_stream(char *buf, ssize_t r, FILE *outf) { + while (r > 0) { + if (fwrite(buf, r*sizeof(char), 1, outf) < 1) { + fprintf(stderr, "Write error\n"); + exit(EXIT_FAILURE); + } + r = read(0, buf, BUFSIZ*sizeof(char)); + } + if (r == -1) { + perror("read"); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) { + ssize_t r; + int run_if_empty; + char **argv_exec; + int fds[2]; + int child_status; + pid_t child_pid; + char buf[BUFSIZ]; + FILE *outf; + + if ((argc < 2) || ((argc == 2) && streq(argv[1], "-n"))) { + fprintf(stderr, "Usage: ifne [-n] command [args]\n"); + return EXIT_FAILURE; + } + + if (streq(argv[1], "-n")) { + run_if_empty = 1; + argv_exec = &argv[2]; + } else { + run_if_empty = 0; + argv_exec = &argv[1]; + } + + r = read(0, buf, BUFSIZ*sizeof(char)); + + if ((r == 0) && !run_if_empty) + return EXIT_SUCCESS; + else if (r == -1) { + perror("read"); + return EXIT_FAILURE; + } + + if (pipe(fds)) { + perror("pipe"); + return EXIT_FAILURE; + } + + if (r && run_if_empty) { + /* don't run the subcommand if we read something from stdin and -n was set */ + /* But write stdin to stdout so ifne -n can be piped without sucking the stream */ + stdin_to_stream(buf, r, stdout); + return EXIT_SUCCESS; + } + + child_pid = fork(); + if (!child_pid) { + /* child process: rebind stdin and exec the subcommand */ + close(fds[1]); + if (dup2(fds[0], 0)) { + perror("dup2"); + return EXIT_FAILURE; + } + + execvp(*argv_exec, argv_exec); + perror(*argv_exec); + close(fds[0]); + return EXIT_FAILURE; + } else if (child_pid == -1) { + perror("fork"); + return EXIT_FAILURE; + } + + /* Parent: write stdin to fds[1] */ + close(fds[0]); + outf = fdopen(fds[1], "w"); + if (! outf) { + perror("fdopen"); + exit(1); + } + + stdin_to_stream(buf, r, outf); + fclose(outf); + + if (waitpid(child_pid, &child_status, 0) != child_pid) { + perror("waitpid"); + return EXIT_FAILURE; + } + if (WIFEXITED(child_status)) { + return (WEXITSTATUS(child_status)); + } else if (WIFSIGNALED(child_status)) { + raise(WTERMSIG(child_status)); + return EXIT_FAILURE; + } + + return EXIT_FAILURE; +} diff --git a/ifne.docbook b/ifne.docbook new file mode 100644 index 0000000..41fa9ab --- /dev/null +++ b/ifne.docbook @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2008 Javier Merino <cibervicho@gmail.com> + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>cibervicho@gmail.com</email> + </address> + <author> + <firstname>Javier</firstname> + <surname>Merino</surname> + </author> + <date>2008-05-01</date> + </refentryinfo> + + <refmeta> + <refentrytitle>ifne</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>ifne</refname> + <refpurpose>Run command if the standard input is not empty</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>ifne [-n] command</command> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>ifne</command> runs the following command if and only if + the standard input is not empty.</para> + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term><option>-n</option></term> + <listitem> + <para>Reverse operation. Run the command if the standard input is empty.</para> + <para>Note that if the standard input is not empty, it is passed through ifne + in this case.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>EXAMPLE</title> + <cmdsynopsis> + <command>find . -name core | ifne mail -s "Core files found" root</command> + </cmdsynopsis> + </refsect1> + + <refsect1> + <title>AUTHOR</title> + + <para>Copyright 2008 by Javier Merino <cibervicho@gmail.com></para> + <para>Licensed under the GNU GPL</para> + </refsect1> + +</refentry> diff --git a/isutf8.c b/isutf8.c new file mode 100644 index 0000000..9711124 --- /dev/null +++ b/isutf8.c @@ -0,0 +1,280 @@ +/* + * isutf8.c - do the input files look like valid utf-8 byte streams? + * + * Copyright (C) 2005 Lars Wirzenius + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> + + +#define VERSION "1.1" + + +/* + * Code to indicate an invalid UTF8 character. + */ +enum { INVALID_CHAR = 0xffffffff }; + + +/* + * Produce shortest UTF8 encoding of a 31-bit value in 'u', returning it + * in the array 'buf'. Return the number of bytes in the encoded value. + * If the value is too large (more than 32 bits or would take more than + * 'maxbytes' bytes), return -1. + */ +static int encodeutf8(unsigned long u, unsigned char *buf, size_t maxbytes) +{ + static const struct { + int nbytes; + unsigned long max; + } tab[] = { + { 1, 0x0000007F }, + { 2, 0x000007FF }, + { 3, 0x0000FFFF }, + { 4, 0x001FFFFF }, + { 5, 0x03FFFFFF }, + { 6, 0x7FFFFFFF }, + }; + static const int ntab = sizeof(tab) / sizeof(tab[0]); + int i, j; + + if (u > tab[ntab-1].max) + return -1; + + for (i = 0; i < ntab; ++i) { + if (u <= tab[i].max) + break; + } + assert(i < ntab); + + if (tab[i].nbytes > maxbytes) + return -1; + + if (tab[i].nbytes == 1) { /* Special case */ + buf[0] = u; + } else { + for (j = tab[i].nbytes-1; j > 0; --j) { + buf[j] = 0x80 | (u & 0x3f); + u >>= 6; + } + + unsigned char mask = ~(0xFF >> tab[i].nbytes); + buf[0] = mask | u; + } + + return tab[i].nbytes; +} + + +/* + * Return number of ones at the top of a byte. + * + * I'm pretty sure there is a fancy trick to do this without a loop, + * but I'm too tired to figure it out now. --liw + */ +static int high_ones(int c) { + int n; + + for (n = 0; (c & 0x80) == 0x80; c <<= 1) + ++n; + return n; +} + + +/* + * Decode a UTF8 character from an array of bytes. Return character code. + * Upon error, return INVALID_CHAR. + */ +static unsigned long decodeutf8(unsigned char *buf, int nbytes) +{ + unsigned long u; + int i, j; + + if (nbytes <= 0) + return INVALID_CHAR; + + if (nbytes == 1) { + if (buf[0] >= 0x80) + return INVALID_CHAR; + return buf[0]; + } + + i = high_ones(buf[0]); + if (i != nbytes) + return INVALID_CHAR; + u = buf[0] & (0xff >> i); + for (j = 1; j < nbytes; ++j) { + if ((buf[j] & 0xC0) != 0x80) + return INVALID_CHAR; + u = (u << 6) | (buf[j] & 0x3f); + } + + /* Conforming UTF-8 cannot contain codes 0xd800–0xdfff (UTF-16 + surrogates) as well as 0xfffe and 0xffff. */ + if (u >= 0xD800 && u <= 0xDFFF) + return INVALID_CHAR; + if (u == 0xFFFE || u == 0xFFFF) + return INVALID_CHAR; + + return u; +} + + +/* + * Determine if the contents of an open file form a valid UTF8 byte stream. + * Do this by collecting bytes for a character into a buffer and then + * decode the bytes and re-encode them and compare that they are identical + * to the original bytes. If any step fails, return 0 for error. If EOF + * is reached, return 1 for OK. + */ +static int is_utf8_byte_stream(FILE *file, char *filename, int quiet) { + enum { MAX_UTF8_BYTES = 6 }; + unsigned char buf[MAX_UTF8_BYTES]; + unsigned char buf2[MAX_UTF8_BYTES]; + int nbytes, nbytes2; + int c; + unsigned long code; + unsigned long line, col, byteoff; + + nbytes = 0; + line = 1; + col = 1; + byteoff = 0; + + for (;;) { + c = getc(file); + + if (c == EOF || c < 0x80 || (c & 0xC0) != 0x80) { + /* New char starts, deal with previous one. */ + if (nbytes > 0) { + code = decodeutf8(buf, nbytes); + if (code == INVALID_CHAR) + goto error; + nbytes2 = encodeutf8(code, buf2, + MAX_UTF8_BYTES); + if (nbytes != nbytes2 || + memcmp(buf, buf2, nbytes) != 0) + goto error; + ++col; + } + nbytes = 0; + /* If it's UTF8, start collecting again. */ + if (c != EOF && c >= 0x80) + buf[nbytes++] = c; + } else { + /* This is a continuation byte, append to buffer. */ + if (nbytes == MAX_UTF8_BYTES) + goto error; + buf[nbytes++] = c; + } + + if (c == EOF) + break; + else if (c == '\n') { + ++line; + byteoff = 0; + col = 1; + } else + ++byteoff; + } + + if (nbytes != 0) + goto error; + + return 1; + +error: + if (!quiet) { + printf("%s: line %lu, char %lu, byte offset %lu: " + "invalid UTF-8 code\n", filename, line, col, byteoff); + } + return 0; +} + + +static void usage(const char *program_name) { + printf("Usage: %s [-hq] [--help] [--quiet] [file ...]\n", + program_name); + printf("Check whether input files are valid UTF-8.\n"); + printf("This is version %s.\n", VERSION); +} + + +int main(int argc, char **argv) { + int i, ok; + FILE *file; + + int quiet; + struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "quiet", no_argument, &quiet, 1 }, + { 0, 0, 0, 0 } + }; + int opt; + + quiet = 0; + + while ((opt = getopt_long(argc, argv, "hq", options, NULL)) != -1) { + switch (opt) { + case 0: + break; + + case 'h': + usage(argv[0]); + exit(0); + break; + + case 'q': + quiet = 1; + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + + if (optind == argc) + ok = is_utf8_byte_stream(stdin, "stdin", quiet); + else { + ok = 1; + for (i = optind; i < argc; ++i) { + file = fopen(argv[i], "r"); + if (file == NULL) { + fprintf(stderr, "isutf8: %s: error %d: %s\n", + argv[i], errno, + strerror(errno)); + ok = 0; + } else { + if (! is_utf8_byte_stream(file, argv[i], quiet)) + ok = 0; + (void) fclose(file); + } + } + } + + if (ok) + exit(0); + exit(EXIT_FAILURE); +} diff --git a/isutf8.docbook b/isutf8.docbook new file mode 100644 index 0000000..58355a2 --- /dev/null +++ b/isutf8.docbook @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2006 Lars Wirzenius (liw@iki.fi) + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>liw@iki.fi</email> + </address> + <author> + <firstname>Lars</firstname> + <surname>Wirzenius</surname> + </author> + <date>2006-02-19</date> + </refentryinfo> + + <refmeta> + <refentrytitle>isutf8</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>isutf8</refname> + <refpurpose>check whether files are valid UTF-8</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>isutf8</command> + <arg><option>-hq</option></arg> + <arg><option>--help</option></arg> + <arg><option>--quiet</option></arg> + <group choice="opt"> + <arg rep="repeat"><replaceable>file</replaceable></arg> + </group> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>isutf8</command> checks whether files are + syntactically valid UTF-8. Input is either files named on the + command line, or the standard input. Notices about files with + invalid UTF-8 are printed to standard output.</para> + + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term><option>-h</option></term> + <term><option>--help</option></term> + <listitem> + <para>Print out a help summary.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-q</option></term> + <term><option>--quiet</option></term> + <listitem> + <para>Don't print messages telling which files are + invalid UTF-8, merely indicate it with the exit + status.</para> + </listitem> + </varlistentry> + + </variablelist> + + </refsect1> + + <refsect1> + <title>EXIT STATUS</title> + + <para>If the file is valid UTF-8, the exit status is zero. + If the file is not valid UTF-8, or there is some + error, the exit status is non-zero.</para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + Lars Wirzenius + <para> + + </para> + </refsect1> + + <refsect1> + <title>SEE ALSO</title> + + <para> + <citerefentry> + <refentrytitle>utf8</refentrytitle><manvolnum>7</manvolnum> + </citerefentry> + </para> + + </refsect1> +</refentry> @@ -0,0 +1,233 @@ +/* lckdo.c: run a program with a lock held, + * to prevent multiple processes running in parallel. + * Use just like `nice' or `nohup'. + * Written by Michael Tokarev <mjt@tls.msk.ru> + * Public domain. + */ + +#define _GNU_SOURCE +#define _BSD_SOURCE +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sysexits.h> +#include <sys/file.h> +#include <sys/wait.h> +#include <signal.h> + +/* compile with -DUSE_FLOCK to use flock() instead of fcntl() */ + +#if !defined(USE_FLOCK) && !defined(F_SETLKW) +# define USE_FLOCK +#endif + +#ifndef __GNUC__ +# ifndef __attribute__ +# define __attribute__(x) +# endif +#endif + +static char *progname; +static void +__attribute__((format(printf,3,4))) +__attribute__((noreturn)) +error(int errnum, int exitcode, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + if (errnum) + fprintf(stderr, ": %s\n", strerror(errnum)); + else + fputs("\n", stderr); + exit(exitcode); +} + +static const char *lckfile; +static int quiet; + +static void sigalarm(int sig) { + if (quiet) + _exit(EX_TEMPFAIL); + error(0, EX_TEMPFAIL, + "lock file `%s' is already locked (timeout waiting)", lckfile); +} + +int main(int argc, char **argv) { + int fd; + int c; + int create = O_CREAT; + int dofork = 1; + int waittime = 0; + int shared = 0; + int test = 0; + int fdn = -1; +#ifndef USE_FLOCK + struct flock fl; +#endif + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + argv[0] = ++progname; + + if (argc == 1) { + printf( + "%s: execute a program with a lock set.\n" + "Usage: %s [options] lockfile program [arguments]\n" + "where options are:\n" + " -w - if the lock is already held by another process,\n" + " wait for it to complete instead of failing immediately\n" + " -W sec - the same as -w but wait not more than sec seconds\n" + " -e - execute the program directly, no fork/wait\n" + " (keeps extra open file descriptor)\n" + " -E nnn - set the fd# to keep open in -e case (implies -e)\n" + " -n - do not create the lock file if it does not exist\n" + " -q - produce no output if lock is already held\n" + " -s - lock in shared (read) mode\n" + " -x - lock in exclusive (write) mode (default)\n" + " -t - test for lock existence" +#ifndef USE_FLOCK + " (just prints pid if any with -q)\n" +#endif + " (implies -n)\n" + , progname, progname); + return 0; + } + + while ((c = getopt(argc, argv, "+wW:neE:sxtq")) != EOF) { + switch(c) { + case 'w': + if (!waittime) + waittime = -1; + break; + case 'W': + if ((waittime = atoi(optarg)) < 1) + error(0, EX_USAGE, "invalid wait time `%s'", optarg); + break; + case 't': + test = 1; + /* fall thru */ + case 'n': + create = 0; + break; + case 'E': + if ((fdn = atoi(optarg)) < 0 || fdn == 2) + error(0, EX_USAGE, "invalid fd# `%s'", optarg); + /* fall thru */ + case 'e': + dofork = 0; + break; + case 's': + shared = 1; + break; + case 'x': + shared = 0; + break; + case 'q': + quiet = 1; + break; + default: + return EX_USAGE; + } + } + + argc -= optind; argv += optind; + if (!argc || (!test && argc < 2)) + error(0, EX_USAGE, "too few arguments given"); + + lckfile = *argv++; + +#ifdef USE_FLOCK + create |= O_RDONLY; +#else + if (!test) + create |= shared ? O_RDONLY : O_WRONLY; +#endif + fd = open(lckfile, create, 0666); + if (fd < 0) { + if (test && errno == ENOENT) { + if (!quiet) + printf("lockfile `%s' is not locked\n", lckfile); + return 0; + } + error(errno, EX_CANTCREAT, "unable to open `%s'", lckfile); + } + + if (!test && fdn >= 0) { + /* dup it early to comply with stupid POSIX fcntl locking + * semantics */ + if (dup2(fd, fdn) < 0) + error(errno, EX_OSERR, "dup2(%d,%d) failed", fd, fdn); + close(fd); + fd = fdn; + } + + if (test) + waittime = 0; + else if (waittime > 0) { + alarm(waittime); + signal(SIGALRM, sigalarm); + } +#ifdef USE_FLOCK + c = flock(fd, (shared ? LOCK_SH : LOCK_EX) | (waittime ? 0 : LOCK_NB)); + if (test && c < 0 && + (errno == EWOULDBLOCK || errno == EAGAIN || errno == EACCES)) { + if (!quiet) + printf("lockfile `%s' is locked\n", lckfile); + else + printf("locked\n"); + return EX_TEMPFAIL; + } +#else + memset(&fl, 0, sizeof(fl)); + fl.l_type = shared ? F_RDLCK : F_WRLCK; + c = fcntl(fd, test ? F_GETLK : waittime ? F_SETLKW : F_SETLK, &fl); + if (test && c == 0) { + if (fl.l_type == F_UNLCK) { + if (!quiet) + printf("lockfile `%s' is not locked\n", lckfile); + return 0; + } + if (!quiet) + printf("lockfile `%s' is locked by process %d\n", lckfile, fl.l_pid); + else + printf("%d\n", fl.l_pid); + return EX_TEMPFAIL; + } +#endif + if (waittime > 0) + alarm(0); + if (c < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EACCES) + error(errno, EX_OSERR, "unable to lock `%s'", lckfile); + else if (quiet) + return EX_TEMPFAIL; + else + error(0, EX_TEMPFAIL, "lockfile `%s' is already locked", lckfile); + } + + if (dofork) { + pid_t pid; + int flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) + error(errno, EX_OSERR, "fcntl() failed"); + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + pid = fork(); + if (pid < 0) + error(errno, EX_OSERR, "unable to fork"); + else if (pid) { + if (wait(&c) < 0) + error(errno, EX_OSERR, "wait() failed"); + if (WIFSIGNALED(c)) + error(0, EX_SOFTWARE, "%s: %s", *argv, + strsignal(WTERMSIG(c))); + return WEXITSTATUS(c); + } + } + execvp(*argv, argv); + error(errno, EX_OSERR, "unable to execute %s", *argv); +} diff --git a/lckdo.docbook b/lckdo.docbook new file mode 100644 index 0000000..effe84d --- /dev/null +++ b/lckdo.docbook @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Written by Michael Tokarev <mjt@tls.msk.ru> +Public domain. + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>mjt@tls.msk.ru</email> + </address> + <author> + <firstname>Michael</firstname> + <surname>Tokarev</surname> + </author> + <date>2007-08-15</date> + </refentryinfo> + + <refmeta> + <refentrytitle>lckdo</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>lckdo</refname> + <refpurpose>run a program with a lock held</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>lckdo</command> + <arg>options</arg> + <arg choice="req">lockfile</arg> + <arg choice="req">program</arg> + <arg>arguments</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>lckdo</command> runs a program with a lock + held, in order to prevent multiple processes from running in + parallel. Use just like <command>nice</command> or + <command>nohup</command>.</para> + + <para>Now that util-linux contains a similar command + named <command>flock</command>, lckdo is deprecated, + and will be removed from some future version of moreutils. + </para> + + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term><option>-w</option></term> + <listitem> + <para>If the lock is already held by another process, + wait for it to complete instead of failing + immediately.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-W {sec}</option></term> + <listitem> + <para>The same as -w but wait not more than sec + seconds.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-e</option></term> + <listitem> + <para>Execute the program directly without forking and + waiting (keeps an extra file descriptor open).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-E {nnn}</option></term> + <listitem> + <para>Set the file descriptor number to keep open when + exec()ing (implies -e).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-n</option></term> + <listitem> + <para>Do not create the lock file if it does not + exist.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-q</option></term> + <listitem> + <para>Produce no output if lock is already held.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-s</option></term> + <listitem> + <para>Lock in shared (read) mode.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-x</option></term> + <listitem> + <para>Lock in exclusive (write) mode (default).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-t</option></term> + <listitem> + <para>Test for lock existence.</para> + </listitem> + </varlistentry> + + </variablelist> + + </refsect1> + + <refsect1> + <title>EXIT STATUS</title> + + <para>If the lock was successfully acquired, the return value is that + of the program invoked by <command>lckdo</command>. If the lock + couldn't be acquired, EX_TEMPFAIL is returned. If there was a problem + opening/creating or locking the lock file, EX_CANTCREAT or EX_OSERR + will be returned.</para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + <para> + Michael Tokarev + </para> + </refsect1> + +</refentry> diff --git a/mispipe.c b/mispipe.c new file mode 100644 index 0000000..d183d04 --- /dev/null +++ b/mispipe.c @@ -0,0 +1,179 @@ +/* + * mispipe: written by Nathanael Nerode. + * + * Copyright 2004 Nathanael Nerode. + * + * Licensed under the GPL version 2 or above, and dual-licensed under the + * following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Usage: mispipe <command1> <command2> + * Run <command1> | <command2>, but return with the exit status of <command1>. + * + * This is designed for a very specific purpose: logging. + * "foo | logger -t foo" + * will return with the exit status of logger, not that of foo. + * "mispipe foo 'logger -t foo'" + * will return with the exit status of foo. + */ + +/* + * To do: + * Make this into a fancy, internationalized, option-handling hellbeast. + * (But why bother? It does its job quite well.) + */ + +#include <errno.h> /* errno */ +#include <sys/types.h> +#include <unistd.h> /* pipe(), fork(),... */ +#include <stdlib.h> /* system() */ +#include <sys/wait.h> /* waitpid(), etc. */ +#include <stdio.h> +#include <stdarg.h> /* va_list, for error() */ + +static const char* progname; /* Stores argv[0] */ +static int filedes[2]; /* Stores pipe file descriptors */ + +/* Subroutine for 'warning' and 'error' which prefixes progname */ +static void warning_prefix(void) { + fputs(progname, stderr); + fputs(": ", stderr); +} + +/* Issue a warning, then die */ +__attribute__(( noreturn, format (printf, 1, 2) )) +static void error(const char* formatstr, ...) { + va_list ap; + va_start(ap, formatstr); + warning_prefix(); + vfprintf(stderr, formatstr, ap); + va_end(ap); + exit(1); +} + +/* Issue a warning, then die, with errno */ +__attribute__(( noreturn )) +static void error_with_errno(const char* message) { + int saved_errno; + saved_errno=errno; + warning_prefix(); + fputs(message, stderr); + fputs(": error number ", stderr); + fprintf(stderr, "%i\n", saved_errno); + exit(1); +} + +/* Convert 'wait'/'system'-style exit status to 'exit'-style exit status */ +__attribute__(( const )) +static int shorten_status(int status_big) { + if (WIFEXITED(status_big)) + return WEXITSTATUS(status_big); + if (WIFSIGNALED(status_big)) + return 128+WTERMSIG(status_big); + if (WIFSTOPPED(status_big)) + return 128+WSTOPSIG(status_big); +#ifdef WCOREDUMP + if (WCOREDUMP(status_big)) + error("Ow, somebody dumped core!"); +#endif + error("shorten_status got an invalid status (?!)"); +} + +/* All the work for command 2. */ +__attribute__(( noreturn )) +static void subprocess2(const char* cmd) { + /* Close the old standard input. */ + if (close(0)) + error_with_errno("Failed (in child) closing standard input"); + /* Make the reading end of the pipe the new standard input. */ + if (dup2(filedes[0], 0) == -1) + error_with_errno("Failed (in child) redefining standard input"); + /* Close the original file descriptor for it */ + if (close(filedes[0])) + error_with_errno("Failed (in child) closing filedes[0]"); + /* Close the other end of the pipe. */ + if (close(filedes[1])) + error_with_errno("Failed (in child) closing filedes[1]"); + /* Do the second command, and throw away the exit status. */ + system(cmd); + /* Close the standard input. */ + if (close(0)) + error_with_errno("Failed (in child) closing standard output " + " (while cleaning up)"); + exit(0); +} + +int main (int argc, const char ** argv) { + int status_big; /* Exit status, 'wait' and 'system' style */ + pid_t child2_pid; + pid_t dead_pid; + + /* Set progname */ + progname = argv[0]; + + /* Verify arguments */ + if (argc != 3) + error("Wrong number of args, aborting\n"); + /* Open a new pipe */ + if (pipe(filedes)) + error_with_errno("pipe() failed"); + + /* Fork to run second command */ + child2_pid = fork(); + if (child2_pid == 0) + subprocess2(argv[2]); + if (child2_pid == -1) + error_with_errno("fork() failed"); + + /* Run first command inline (seriously!) */ + /* Close standard output. */ + if (close(1)) + error_with_errno("Failed closing standard output"); + /* Make the writing end of the pipe the new standard output. */ + if (dup2(filedes[1], 1) == -1) + error_with_errno("Failed redefining standard output"); + /* Close the original file descriptor for it. */ + if (close(filedes[1])) + error_with_errno("Failed closing filedes[1]"); + /* Close the other end of the pipe. */ + if (close(filedes[0])) + error_with_errno("Failed closing filedes[0]"); + /* Do the first command, and (crucially) get the status. */ + status_big = system(argv[1]); + + /* Close standard output. */ + if (close(1)) + error_with_errno("Failed closing standard output (while cleaning up)"); + + /* Wait for the other process to exit. */ + /* We don't care about the status. */ + dead_pid = waitpid(child2_pid, NULL, WUNTRACED); + if (dead_pid == -1) { + error_with_errno("waitpid() failed"); + } + else if (dead_pid != child2_pid) { + error("waitpid(): Who died? %i\n", dead_pid); + } + + /* Return the desired exit status. */ + return shorten_status(status_big); +} diff --git a/mispipe.docbook b/mispipe.docbook new file mode 100644 index 0000000..bd8faa8 --- /dev/null +++ b/mispipe.docbook @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2006 Joey Hess <joey@kitenet.net> + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>neroden@fastmail.fm</email> + </address> + <author> + <firstname>Nathanael</firstname> + <surname>Nerode</surname> + </author> + <date>2006-09-07</date> + </refentryinfo> + + <refmeta> + <refentrytitle>mispipe</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>mispipe</refname> + <refpurpose>pipe two commands, returning the exit status of + the first</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>mispipe</command> + <arg><replaceable>"command1"</replaceable></arg> + <arg><replaceable>"command2"</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>mispipe</command> pipes two commands + together like the shell does, but unlike piping in the + shell, which returns the exit status of the last command; + when using mispipe, the exit status of the first command + is returned. + </para> + + <para> + Note that some shells, notably <command>bash</command>, + do offer a pipefail option, however, that option does not + behave the same since it makes a failure of any command in + the pipeline be returned, not just the exit status of the + first. + </para> + + </refsect1> + + <refsect1> + <title>EXIT STATUS</title> + + <para>The exit status of the first command. If the process + terminated abnormally (due to a signal), 128 will be added + to its exit status.</para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + <para> + Nathanael Nerode + </para> + </refsect1> +</refentry> diff --git a/parallel.c b/parallel.c new file mode 100644 index 0000000..d283b96 --- /dev/null +++ b/parallel.c @@ -0,0 +1,243 @@ +/* + * parallel.c - run commands in parallel until you run out of commands + * + * Copyright © 2008 Tollef Fog Heen <tfheen@err.no> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#define _GNU_SOURCE +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/select.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#if defined(__FreeBSD_kernel__) +#define WEXITED 0 +#endif + +void usage() { + printf("parallel [OPTIONS] command -- arguments\n\tfor each argument, " + "run command with argument, in parallel\n"); + printf("parallel [OPTIONS] -- commands\n\trun specified commands in parallel\n"); + exit(1); +} + +void exec_child(char **command, char **arguments, int replace_cb, int nargs) { + if (fork() != 0) { + return; + } + + if (command[0]) { + char **argv; + int argc = 0; + int i; + char *s; + + while (command[argc] != 0) { + argc++; + } + if (! replace_cb) + argc++; + argv = calloc(sizeof(char*), argc + nargs); + + for (i = 0; i < argc; i++) { + while (replace_cb && (s=strstr(command[i], "{}"))) { + char *buf=malloc(strlen(command[i]) + strlen(arguments[0])); + s[0]='\0'; + sprintf(buf, "%s%s%s", command[i], arguments[0], s+2); + command[i]=buf; + } + argv[i] = command[i]; + } + if (! replace_cb) + memcpy(argv + i - 1, arguments, nargs * sizeof(char *)); + execvp(argv[0], argv); + exit(1); + } + else { + int ret=system(arguments[0]); + if (WIFEXITED(ret)) { + exit(WEXITSTATUS(ret)); + } + else { + exit(1); + } + } + return; +} + +int wait_for_child(int options) { + id_t id_ignored = 0; + siginfo_t infop; + + infop.si_pid = 0; + waitid(P_ALL, id_ignored, &infop, WEXITED | options); + if (infop.si_pid == 0) { + return -1; /* Nothing to wait for */ + } + if (infop.si_code == CLD_EXITED) { + return infop.si_status; + } + return 1; +} + +int main(int argc, char **argv) { + int maxjobs = -1; + int curjobs = 0; + double maxload = -1; + int argsatonce = 1; + int opt; + char **command = calloc(sizeof(char*), argc); + char **arguments = NULL; + int argidx = 0; + int arglen = 0; + int cidx = 0; + int returncode = 0; + int replace_cb = 0; + char *t; + + while ((argv[optind] && strcmp(argv[optind], "--") != 0) && + (opt = getopt(argc, argv, "+hij:l:n:")) != -1) { + switch (opt) { + case 'h': + usage(); + break; + case 'i': + replace_cb = 1; + break; + case 'j': + errno = 0; + maxjobs = strtoul(optarg, &t, 0); + if (errno != 0 || (t-optarg) != strlen(optarg)) { + fprintf(stderr, "option '%s' is not a number\n", + optarg); + exit(2); + } + break; + case 'l': + errno = 0; + maxload = strtod(optarg, &t); + if (errno != 0 || (t-optarg) != strlen(optarg)) { + fprintf(stderr, "option '%s' is not a number\n", + optarg); + exit(2); + } + break; + case 'n': + errno = 0; + argsatonce = strtoul(optarg, &t, 0); + if (errno != 0 || argsatonce < 1 || (t-optarg) != strlen(optarg)) { + fprintf(stderr, "option '%s' is not a positive number\n", + optarg); + exit(2); + } + break; + default: /* ’?’ */ + usage(); + break; + } + } + + if (replace_cb && argsatonce > 1) { + fprintf(stderr, "options -i and -n are incomaptible\n"); + exit(2); + } + + if (maxjobs < 0) { +#ifdef _SC_NPROCESSORS_ONLN + maxjobs = sysconf(_SC_NPROCESSORS_ONLN); +#else +#warning Cannot autodetect number of CPUS on this system: _SC_NPROCESSORS_ONLN not defined. + maxjobs = 1; +#endif + } + + while (optind < argc) { + if (strcmp(argv[optind], "--") == 0) { + int i; + + optind++; + arglen = argc - optind; + arguments = calloc(sizeof(char *), arglen); + if (! arguments) { + exit(1); + } + + for (i = 0; i < arglen; i++) { + arguments[i] = strdup(argv[optind + i]); + } + optind += i; + } + else { + command[cidx] = strdup(argv[optind]); + cidx++; + } + optind++; + } + + if (argsatonce > 1 && ! command[0]) { + fprintf(stderr, "option -n cannot be used without a command\n"); + exit(2); + } + + while (argidx < arglen) { + double load; + + getloadavg(&load, 1); + + if ((maxjobs == 0 || curjobs < maxjobs) && + (maxload < 0 || load < maxload)) { + + if (argsatonce > arglen - argidx) + argsatonce = arglen - argidx; + exec_child(command, arguments + argidx, + replace_cb, argsatonce); + argidx += argsatonce; + curjobs++; + } + + if (maxjobs == 0 || curjobs == maxjobs) { + returncode |= wait_for_child(0); + curjobs--; + } + + if (maxload > 0 && load >= maxload) { + int r; + sleep(1); /* XXX We should have a better + * heurestic than this */ + r = wait_for_child(WNOHANG); + if (r > 0) + returncode |= r; + if (r != -1) + curjobs--; + } + } + while (curjobs > 0) { + returncode |= wait_for_child(0); + curjobs--; + } + + return returncode; +} + diff --git a/parallel.docbook b/parallel.docbook new file mode 100644 index 0000000..d3ffcce --- /dev/null +++ b/parallel.docbook @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Written by Joey Hess + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>joey@kitenet.net</email> + </address> + <author> + <firstname>Joey</firstname> + <surname>Hess</surname> + </author> + <date>2009-07-02</date> + </refentryinfo> + + <refmeta> + <refentrytitle>parallel</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>parallel</refname> + <refpurpose>run programs in parallel</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>parallel</command> + <arg>options</arg> + <arg>command</arg> + <command>--</command> + <arg>argument ...</arg> + </cmdsynopsis> + <cmdsynopsis> + <command>parallel</command> + <arg>options</arg> + <command>--</command> + <arg>command ...</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>parallel</command> runs the specified command, + passing it a single one of the specified arguments. This is + repeated for each argument. Jobs may be run in + parallel. The default is to run one job per CPU.</para> + + <para>If no command is specified before the --, + the commands after it are instead run in parallel.</para> + + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term><option>-j maxjobs</option></term> + <listitem> + <para>Use to limit the number of jobs + that are run at the same time.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-l maxload</option></term> + <listitem> + <para>Wait as needed to avoid starting + new jobs when the system's load average + is not below the specified limit.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-i</option></term> + <listitem> + <para>Normally the command is passed the + argument at the end of its command line. With + this option, any instances of "{}" in + the command are replaced with the argument.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-n</option></term> + <listitem> + <para>Number of arguments to pass to a + command at a time. Default is 1. + Incompatible with -i</para> + </listitem> + </varlistentry> + + </variablelist> + + </refsect1> + + <refsect1> + <title>EXAMPLE</title> + + <para> + <cmdsynopsis> + <command>parallel sh -c "echo hi; sleep 2; echo bye" -- 1 2 3</command> + </cmdsynopsis> + </para> + + <para>This runs three subshells that each print a message, delay, + and print another message. If your system has multiple + CPUs, parallel will run some of the jobs in parallel, + which should be clear from the order the messages are + output. + </para> + + <para> + <cmdsynopsis> + <command>parallel -j 3 ufraw -o processed -- *.NEF</command> + </cmdsynopsis> + </para> + + <para>This runs three ufraw processes at the same time until + all of the NEF files have been processed. + </para> + + <para> + <cmdsynopsis> + <command>parallel -j 3 -- ls df "echo hi"</command> + </cmdsynopsis> + </para> + + <para>This runs three independent commands in parallel.</para> + + </refsect1> + + <refsect1> + <title>EXIT STATUS</title> + + + <para>Its exit status is the combination of the exit statuses of each + command ran, ORed together. (Thus, if any one command + exits nonzero, <command>parallel</command> as a whole will exit nonzero.)</para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + <para> + Tollef Fog Heen + </para> + </refsect1> + +</refentry> @@ -0,0 +1,61 @@ +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> + +/* Licensed under the GPL + * Copyright (c) Miek Gieben, 2006 + */ + +/* like tee(1), but then connect to other programs using + * pipes _and_ output to standard output + */ + +int +close_pipes(FILE **p, size_t i) +{ + int ret=EXIT_SUCCESS; + size_t j; + for (j = 0; j < i; j++) { + int r = pclose(p[j]); + if (WIFEXITED(r)) + ret |= WEXITSTATUS(r); + else + ret |= 1; + } + return ret; +} + +int +main(int argc, char **argv) { + size_t i, r; + FILE **pipes; + char buf[BUFSIZ]; + + pipes = malloc(((argc - 1) * sizeof *pipes)); + if (!pipes) + exit(EXIT_FAILURE); + + for (i = 1; i < argc; i++) { + pipes[i - 1] = popen(argv[i], "w"); + if (!pipes[i - 1]) { + fprintf(stderr, "Can not open pipe to '%s\'\n", argv[i]); + close_pipes(pipes, argc); + + exit(EXIT_FAILURE); + } + } + argc--; + + while(!feof(stdin) && (!ferror(stdin))) { + r = fread(buf, sizeof(char), BUFSIZ, stdin); + for(i = 0; i < argc; i++) { + if (fwrite(buf, sizeof(char), r, pipes[i]) != r) { + fprintf(stderr, "Write error to `%s\'\n", argv[i + 1]); + close_pipes(pipes, argc); + exit(EXIT_FAILURE); + } + } + } + exit(close_pipes(pipes, argc)); +} diff --git a/pee.docbook b/pee.docbook new file mode 100644 index 0000000..f6a8441 --- /dev/null +++ b/pee.docbook @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright 2006 Joey Hess <joey@kitenet.net> + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + <refentryinfo> + <address> + <email>joey@kitenet.net</email> + </address> + <author> + <firstname>Joey</firstname> + <surname>Hess</surname> + </author> + <date>2006-03-14</date> + </refentryinfo> + + <refmeta> + <refentrytitle>pee</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>pee</refname> + <refpurpose>tee standard input to pipes</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>pee</command> + <group choice="opt"> + <arg rep="repeat"><replaceable>"command"</replaceable></arg> + </group> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>pee</command> is like <command>tee</command> + but for pipes. Each command is run and fed a copy of the + standard input. The output of all commands is sent to + stdout.</para> + + <para>Note that while this is similar to + <command>tee</command>, a copy of the input is not sent + to stdout, like tee does. If that is desired, use + <command>pee cat ...</command></para> + </refsect1> + + <refsect1> + <title>SEE ALSO</title> + + <para> + <citerefentry> + <refentrytitle>tee</refentrytitle><manvolnum>1</manvolnum> + </citerefentry> + </para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + <para> + Miek Gieben + </para> + </refsect1> +</refentry> diff --git a/physmem.c b/physmem.c new file mode 100644 index 0000000..a53a2cf --- /dev/null +++ b/physmem.c @@ -0,0 +1,301 @@ +/* Calculate the size of physical memory. + + Copyright (C) 2000, 2001, 2003, 2005, 2006 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include <unistd.h> + +#if HAVE_SYS_PSTAT_H +# include <sys/pstat.h> +#endif + +#if HAVE_SYS_SYSMP_H +# include <sys/sysmp.h> +#endif + +#if HAVE_SYS_SYSINFO_H && HAVE_MACHINE_HAL_SYSINFO_H +# include <sys/sysinfo.h> +# include <machine/hal_sysinfo.h> +#endif + +#if HAVE_SYS_TABLE_H +# include <sys/table.h> +#endif + +#include <sys/types.h> + +#if HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif + +#if HAVE_SYS_SYSCTL_H +# include <sys/sysctl.h> +#endif + +#if HAVE_SYS_SYSTEMCFG_H +# include <sys/systemcfg.h> +#endif + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +/* MEMORYSTATUSEX is missing from older windows headers, so define + a local replacement. */ +typedef struct +{ + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; +} lMEMORYSTATUSEX; +typedef WINBOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*); +#endif + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) + +/* Return the total amount of physical memory. */ +double +physmem_total (void) +{ +#if defined _SC_PHYS_PAGES && defined _SC_PAGESIZE + { /* This works on linux-gnu, solaris2 and cygwin. */ + double pages = sysconf (_SC_PHYS_PAGES); + double pagesize = sysconf (_SC_PAGESIZE); + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } +#endif + +#if HAVE_PSTAT_GETSTATIC + { /* This works on hpux11. */ + struct pst_static pss; + if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0)) + { + double pages = pss.physical_memory; + double pagesize = pss.page_size; + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } + } +#endif + +#if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE + { /* This works on irix6. */ + struct rminfo realmem; + if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0) + { + double pagesize = sysconf (_SC_PAGESIZE); + double pages = realmem.physmem; + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } + } +#endif + +#if HAVE_GETSYSINFO && defined GSI_PHYSMEM + { /* This works on Tru64 UNIX V4/5. */ + int physmem; + + if (getsysinfo (GSI_PHYSMEM, (caddr_t) &physmem, sizeof (physmem), + NULL, NULL, NULL) == 1) + { + double kbytes = physmem; + + if (0 <= kbytes) + return kbytes * 1024.0; + } + } +#endif + +#if HAVE_SYSCTL && defined HW_PHYSMEM + { /* This works on *bsd and darwin. */ + unsigned int physmem; + size_t len = sizeof physmem; + static int mib[2] = { CTL_HW, HW_PHYSMEM }; + + if (sysctl (mib, ARRAY_SIZE (mib), &physmem, &len, NULL, 0) == 0 + && len == sizeof (physmem)) + return (double) physmem; + } +#endif + +#if HAVE__SYSTEM_CONFIGURATION + /* This works on AIX. */ + return _system_configuration.physmem; +#endif + +#if defined _WIN32 + { /* this works on windows */ + PFN_MS_EX pfnex; + HMODULE h = GetModuleHandle ("kernel32.dll"); + + if (!h) + return 0.0; + + /* Use GlobalMemoryStatusEx if available. */ + if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx"))) + { + lMEMORYSTATUSEX lms_ex; + lms_ex.dwLength = sizeof lms_ex; + if (!pfnex (&lms_ex)) + return 0.0; + return (double) lms_ex.ullTotalPhys; + } + + /* Fall back to GlobalMemoryStatus which is always available. + but returns wrong results for physical memory > 4GB. */ + else + { + MEMORYSTATUS ms; + GlobalMemoryStatus (&ms); + return (double) ms.dwTotalPhys; + } + } +#endif + + /* Guess 64 MB. It's probably an older host, so guess small. */ + return 64 * 1024 * 1024; +} + +/* Return the amount of physical memory available. */ +double +physmem_available (void) +{ +#if defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE + { /* This works on linux-gnu, solaris2 and cygwin. */ + double pages = sysconf (_SC_AVPHYS_PAGES); + double pagesize = sysconf (_SC_PAGESIZE); + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } +#endif + +#if HAVE_PSTAT_GETSTATIC && HAVE_PSTAT_GETDYNAMIC + { /* This works on hpux11. */ + struct pst_static pss; + struct pst_dynamic psd; + if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0) + && 0 <= pstat_getdynamic (&psd, sizeof psd, 1, 0)) + { + double pages = psd.psd_free; + double pagesize = pss.page_size; + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } + } +#endif + +#if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE + { /* This works on irix6. */ + struct rminfo realmem; + if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0) + { + double pagesize = sysconf (_SC_PAGESIZE); + double pages = realmem.availrmem; + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } + } +#endif + +#if HAVE_TABLE && defined TBL_VMSTATS + { /* This works on Tru64 UNIX V4/5. */ + struct tbl_vmstats vmstats; + + if (table (TBL_VMSTATS, 0, &vmstats, 1, sizeof (vmstats)) == 1) + { + double pages = vmstats.free_count; + double pagesize = vmstats.pagesize; + + if (0 <= pages && 0 <= pagesize) + return pages * pagesize; + } + } +#endif + +#if HAVE_SYSCTL && defined HW_USERMEM + { /* This works on *bsd and darwin. */ + unsigned int usermem; + size_t len = sizeof usermem; + static int mib[2] = { CTL_HW, HW_USERMEM }; + + if (sysctl (mib, ARRAY_SIZE (mib), &usermem, &len, NULL, 0) == 0 + && len == sizeof (usermem)) + return (double) usermem; + } +#endif + +#if defined _WIN32 + { /* this works on windows */ + PFN_MS_EX pfnex; + HMODULE h = GetModuleHandle ("kernel32.dll"); + + if (!h) + return 0.0; + + /* Use GlobalMemoryStatusEx if available. */ + if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx"))) + { + lMEMORYSTATUSEX lms_ex; + lms_ex.dwLength = sizeof lms_ex; + if (!pfnex (&lms_ex)) + return 0.0; + return (double) lms_ex.ullAvailPhys; + } + + /* Fall back to GlobalMemoryStatus which is always available. + but returns wrong results for physical memory > 4GB */ + else + { + MEMORYSTATUS ms; + GlobalMemoryStatus (&ms); + return (double) ms.dwAvailPhys; + } + } +#endif + + /* Guess 25% of physical memory. */ + return physmem_total () / 4; +} + + +#if DEBUG + +# include <stdio.h> +# include <stdlib.h> + +int +main (void) +{ + printf ("%12.f %12.f\n", physmem_total (), physmem_available ()); + exit (0); +} + +#endif /* DEBUG */ + +/* +Local Variables: +compile-command: "gcc -DDEBUG -g -O -Wall -W physmem.c" +End: +*/ diff --git a/sponge.c b/sponge.c new file mode 100644 index 0000000..969703f --- /dev/null +++ b/sponge.c @@ -0,0 +1,373 @@ +/* + * sponge.c - read in all available info from stdin, then output it to + * file named on the command line + * + * Copyright © 2006 Tollef Fog Heen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +/* MAX() */ +#include <sys/param.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/resource.h> +/* SIZE_MAX */ +#include <stdint.h> +#include <signal.h> + +#include "physmem.c" + +#define BUFF_SIZE 8192 +#define MIN_SPONGE_SIZE BUFF_SIZE +char *tmpname = NULL; + +void usage() { + printf("sponge <file>: soak up all input from stdin and write it to <file>\n"); + exit(0); +} + +/* all the signal stuff copied from gnu sort */ + +/* The set of signals that are caught. */ +static sigset_t caught_signals; + +/* Critical section status. */ +struct cs_status { + int valid; // was bool + sigset_t sigs; +}; + +/* Enter a critical section. */ +static struct cs_status cs_enter (void) { + struct cs_status status; + status.valid = (sigprocmask(SIG_BLOCK, &caught_signals, &status.sigs) == 0); + return status; +} + +/* Leave a critical section. */ +static void cs_leave (struct cs_status status) { + if (status.valid) { + /* Ignore failure when restoring the signal mask. */ + sigprocmask(SIG_SETMASK, &status.sigs, NULL); + } +} + +static void cleanup () { + if (tmpname) { + unlink(tmpname); + } +} + +static void onexit_cleanup (void) { + struct cs_status cs = cs_enter(); + cleanup(); + cs_leave(cs); +} + +static void sighandler (int sig) { + if (! SA_NOCLDSTOP) + signal(sig, SIG_IGN); + + cleanup(); + + signal(sig, SIG_DFL); + raise(sig); +} + +/* taken from coreutils sort */ +static size_t default_sponge_size (void) { + /* Let MEM be available memory or 1/8 of total memory, whichever + is greater. */ + double avail = physmem_available(); + double total = physmem_total(); + double mem = MAX(avail, total / 8); + struct rlimit rlimit; + + /* Let SIZE be MEM, but no more than the maximum object size or + system resource limits. Avoid the MIN macro here, as it is not + quite right when only one argument is floating point. Don't + bother to check for values like RLIM_INFINITY since in practice + they are not much less than SIZE_MAX. */ + size_t size = SIZE_MAX; + if (mem < size) + size = mem; + if (getrlimit(RLIMIT_DATA, &rlimit) == 0 && rlimit.rlim_cur < size) + size = rlimit.rlim_cur; +#ifdef RLIMIT_AS + if (getrlimit(RLIMIT_AS, &rlimit) == 0 && rlimit.rlim_cur < size) + size = rlimit.rlim_cur; +#endif + + /* Leave a large safety margin for the above limits, as failure can + occur when they are exceeded. */ + size /= 2; + +#ifdef RLIMIT_RSS + /* Leave a 1/16 margin for RSS to leave room for code, stack, etc. + Exceeding RSS is not fatal, but can be quite slow. */ + if (getrlimit(RLIMIT_RSS, &rlimit) == 0 && rlimit.rlim_cur / 16 * 15 < size) + size = rlimit.rlim_cur / 16 * 15; +#endif + + /* Use no less than the minimum. */ + return MAX (size, MIN_SPONGE_SIZE); +} + +void trapsignals (void) { + ssize_t i = 0; + static int const sig[] = { + /* The usual suspects. */ + SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM, +#ifdef SIGPOLL + SIGPOLL, +#endif +#ifdef SIGPROF + SIGPROF, +#endif +#ifdef SIGVTALRM + SIGVTALRM, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + }; + int nsigs = sizeof(sig) / sizeof(sig[0]); + +#if SA_NOCLDSTOP + struct sigaction act; + + sigemptyset(&caught_signals); + for (i = 0; i < nsigs; i++) { + sigaction(sig[i], NULL, &act); + if (act.sa_handler != SIG_IGN) + sigaddset(&caught_signals, sig[i]); + } + + act.sa_handler = sighandler; + act.sa_mask = caught_signals; + act.sa_flags = 0; + + for (i = 0; i < nsigs; i++) + if (sigismember(&caught_signals, sig[i])) + sigaction(sig[i], &act, NULL); +#else + for (i = 0; i < nsigs; i++) + if (signal(sig[i], SIG_IGN) != SIG_IGN) { + signal(sig[i], sighandler); + siginterrupt (sig[i], 1); + } +#endif +} + +static void write_buff_tmp(char* buff, size_t length, FILE *fd) { + if (fwrite(buff, length, 1, fd) < 1) { + perror("error writing buffer to temporary file"); + fclose(fd); + exit(1); + } +} + +static void write_buff_tmp_finish (char* buff, size_t length, FILE *fd) { + if (length) + write_buff_tmp(buff, length, fd); + if (fflush(fd) != 0) { + perror("fflush"); + exit(1); + } +} + +static void write_buff_out (char* buff, size_t length, FILE *fd) { + if (fwrite(buff, length, 1, fd) < 1) { + perror("error writing buffer to output file"); + fclose(fd); + exit(1); + } +} + +static void copy_tmpfile (FILE *tmpfile, FILE *outfile, char *buf, size_t size) { + ssize_t i; + if (lseek(fileno(tmpfile), 0, SEEK_SET)) { + perror("could to seek to start of temporary file"); + fclose(tmpfile); + exit(1); + } + while ((i = read(fileno(tmpfile), buf, size)) > 0) { + write_buff_out(buf, i, outfile); + } + if (i == -1) { + perror("read temporary file"); + fclose(tmpfile); + exit(1); + } + if (fclose(tmpfile) != 0) { + perror("read temporary file"); + exit(1); + } + if (fclose(outfile) != 0) { + perror("error writing buffer to output file"); + exit(1); + } +} + +FILE *open_tmpfile (void) { + struct cs_status cs; + int tmpfd; + FILE *tmpfile; + mode_t mask; + char *tmpdir; + char const * const template="%s/sponge.XXXXXX"; + + trapsignals(); + cs = cs_enter(); + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = "/tmp"; + /* Subtract 2 for `%s' and add 1 for the trailing NULL. */ + tmpname=malloc(strlen(tmpdir) + strlen(template) - 2 + 1); + if (! tmpname) { + perror("failed to allocate memory"); + exit(1); + } + sprintf(tmpname, template, tmpdir); + mask=umask(077); + tmpfd = mkstemp(tmpname); + umask(mask); + atexit(onexit_cleanup); // solaris on_exit(onexit_cleanup, 0); + cs_leave(cs); + + if (tmpfd < 0) { + perror("mkstemp failed"); + exit(1); + } + tmpfile = fdopen(tmpfd, "w+"); + if (! tmpfile) { + perror("fdopen"); + exit(1); + } + return tmpfile; +} + +int main (int argc, char **argv) { + char *buf, *bufstart, *outname = NULL; + size_t bufsize = BUFF_SIZE; + size_t bufused = 0; + FILE *outfile, *tmpfile = 0; + ssize_t i = 0; + size_t mem_available = default_sponge_size(); + int tmpfile_used=0; + + if (argc > 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { + usage(); + } + if (argc == 2) { + outname = argv[1]; + } + + tmpfile = open_tmpfile(); + bufstart = buf = malloc(bufsize); + if (!buf) { + perror("failed to allocate memory"); + exit(1); + } + while ((i = read(0, buf, bufsize - bufused)) > 0) { + bufused = bufused+i; + if (bufused == bufsize) { + if ((bufsize*2) >= mem_available) { + write_buff_tmp(bufstart, bufused, tmpfile); + bufused = 0; + tmpfile_used = 1; + } + else { + bufsize *= 2; + bufstart = realloc(bufstart, bufsize); + if (!bufstart) { + perror("failed to realloc memory"); + exit(1); + } + } + } + buf = bufstart + bufused; + } + if (i < 0) { + perror("failed to read from stdin"); + exit(1); + } + + if (outname) { + mode_t mode; + struct stat statbuf; + int exists = (lstat(outname, &statbuf) == 0); + + write_buff_tmp_finish(bufstart, bufused, tmpfile); + + /* Set temp file mode to match either + * the old file mode, or the default file + * mode for a newly created file. */ + if (exists) { + mode = statbuf.st_mode; + } + else { + mode_t mask = umask(0); + umask(mask); + mode = 0666 & ~mask; + } + if (chmod(tmpname, mode) != 0) { + perror("chmod"); + exit(1); + } + + /* If it's a regular file, or does not yet exist, + * attempt a fast rename of the temp file. */ + if (((exists && + S_ISREG(statbuf.st_mode) && + ! S_ISLNK(statbuf.st_mode) + ) || ! exists) && + rename(tmpname, outname) == 0) { + tmpname=NULL; /* don't try to cleanup tmpname */ + } + else { + /* Fall back to slow copy. */ + outfile = fopen(outname, "w"); + if (!outfile) { + perror("error opening output file"); + exit(1); + } + copy_tmpfile(tmpfile, outfile, bufstart, bufsize); + } + } + else { + if (tmpfile_used) { + write_buff_tmp_finish(bufstart, bufused, tmpfile); + copy_tmpfile(tmpfile, stdout, bufstart, bufsize); + } + else if (bufused) { + /* buffer direct to stdout, no tmpfile */ + write_buff_out(bufstart, bufused, stdout); + } + } + + return 0; +} diff --git a/sponge.docbook b/sponge.docbook new file mode 100644 index 0000000..daab8eb --- /dev/null +++ b/sponge.docbook @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + +Copyright © 2006 Joey Hess + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +--> + +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"file:///usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd" +[]> + +<refentry> + + <refentryinfo> + <author> + <firstname>Joey</firstname> + <surname>Hess</surname> + </author> + <date>2006-02-19</date> + </refentryinfo> + + <refmeta> + <refentrytitle>sponge</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>sponge</refname> + <refpurpose>soak up standard input and write to a file</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>sed '...' file | grep '...' | sponge file</command> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para><command>sponge</command> reads standard input and + writes it out to the specified file. Unlike a shell + redirect, sponge soaks up all its input before + opening the output file. This allows constructing + pipelines that read from and write to + the same file.</para> + <para> + It also creates the output file + atomically by renaming a temp file into place, + and preserves the permissions of the output file + if it already exists. + If the output file is a special file or symlink, + the data will be written to it.</para> + <para>If no output file is specified, sponge outputs to + stdout.</para> + + </refsect1> + + <refsect1> + <title>AUTHOR</title> + <para> + Colin Watson and Tollef Fog Heen + </para> + </refsect1> +</refentry> @@ -0,0 +1,154 @@ +#!/usr/bin/perl + +=head1 NAME + +ts - timestamp input + +=head1 SYNOPSIS + +ts [-r] [-i] [format] + +=head1 DESCRIPTION + +ts adds a timestamp to the beginning of each line of input. + +The optional format parameter controls how the timestamp is formatted, +as used by L<strftime(3)>. The default format is "%b %d %H:%M:%S". In +addition to the regular strftime conversion specifications, "%.S" and "%.s" +are like "%S" and "%s", but provide subsecond resolution +(ie, "30.00001" and "1301682593.00001"). + +If the -r switch is passed, it instead converts existing timestamps in +the input to relative times, such as "15m5s ago". Many common timestamp +formats are supported. Note that the Time::Duration and Date::Parse perl +modules are required for this mode to work. Currently, converting localized +dates is not supported. + +If both -r and a format is passed, the existing timestamps are +converted to the specified format. + +If the -i switch is passed, ts timestamps incrementally instead. Every +timestamp will be the time elapsed since the last timestamp. +The default format changes to "%H:%M:%S", and "%.S" and "%.s" can be used +as well. + +=head1 ENVIRONMENT + +The standard TZ environment variable controls what time zone dates +are assumed to be in, if a timezone is not specified as part of the date. + +=head1 AUTHOR + +Copyright 2006 by Joey Hess <joey@kitenet.net> + +Licensed under the GNU GPL. + +=cut + +use warnings; +use strict; +use POSIX q{strftime}; + +$|=1; + +my $rel=0; +my $inc=0; +use Getopt::Long; +GetOptions("r" => \$rel, "i" => \$inc) || die "usage: ts [-r] [-i] [format]\n"; + +if ($rel) { + eval q{ + use Date::Parse; + use Time::Duration; + }; + die $@ if $@; +} + +my $use_format=@ARGV; +my $format="%b %d %H:%M:%S"; +if ($inc) { + $format="%H:%M:%S"; + $ENV{TZ}='GMT'; +} +$format=shift if @ARGV; + +# For subsecond resolution, Time::HiRes is needed. +my $hires=0; +if ($format=~/\%\.[Ss]/) { + require Time::HiRes; + $hires=1; +} + +my $lastseconds = 0; +my $lastmicroseconds = 0; + +if ($hires) { + ($lastseconds, $lastmicroseconds) = Time::HiRes::gettimeofday(); +} else { + $lastseconds = time; +} + + +while (<>) { + if (! $rel) { + if ($hires) { + my $f=$format; + my ($seconds, $microseconds) = Time::HiRes::gettimeofday(); + if ($inc) { + my $deltaseconds = $seconds - $lastseconds; + my $deltamicroseconds = $microseconds - $lastmicroseconds; + if ($deltamicroseconds < 0) { + $deltaseconds -= 1; + $deltamicroseconds += 1000000; + } + $lastseconds = $seconds; + $lastmicroseconds = $microseconds; + $seconds = $deltaseconds; + $microseconds = $deltamicroseconds; + } + my $s=sprintf("%06i", $microseconds); + $f=~s/\%\.([Ss])/%$1.$s/g; + print strftime($f, localtime($seconds)); + } + else { + if ($inc) { + my $seconds = time; + my $deltaseconds = $seconds - $lastseconds; + $lastseconds = $seconds; + print strftime($format, localtime($deltaseconds)); + } else { + print strftime($format, localtime); + } + } + print " ".$_; + } + else { + s{\b( + \d\d[-\s\/]\w\w\w # 21 dec 17:05 + (?:\/\d\d+)? # 21 dec/93 17:05 + [\s:]\d\d:\d\d # (time part of above) + (?::\d\d)? # (optional seconds) + (?:\s+[+-]\d\d\d\d)? # (optional timezone) + | + \w{3}\s+\d{1,2}\s+\d\d:\d\d:\d\d # syslog form + | + \d\d\d[-:]\d\d[-:]\d\dT\d\d:\d\d:\d\d.\d+ # ISO-8601 + | + (?:\w\w\w,?\s+)? # (optional Day) + \d+\s+\w\w\w\s+\d\d+\s+\d\d:\d\d:\d\d + # 16 Jun 94 07:29:35 + (?:\s+\w\w\w|\s[+-]\d\d\d\d)? + # (optional timezone) + | + \w\w\w\s+\w\w\w\s+\d\d\s+\d\d:\d\d + # lastlog format + )\b + }{ + $use_format + ? strftime($format, localtime(str2time($1))) + : concise(ago(time - str2time($1), 2)) + }exg; + + print $_; + } +} @@ -0,0 +1,232 @@ +#!/usr/bin/perl + +=head1 NAME + +vidir - edit directory + +=head1 SYNOPSIS + +B<vidir> [--verbose] [directory|file|-] ... + +=head1 DESCRIPTION + +vidir allows editing of the contents of a directory in a text editor. If no +directory is specified, the current directory is edited. + +When editing a directory, each item in the directory will appear on its own +numbered line. These numbers are how vidir keeps track of what items are +changed. Delete lines to remove files from the directory, or +edit filenames to rename files. You can also switch pairs of numbers to +swap filenames. + +Note that if "-" is specified as the directory to edit, it reads a list of +filenames from stdin and displays those for editing. Alternatively, a list +of files can be specified on the command line. + +=head1 OPTIONS + +=over 4 + +=item -v, --verbose + +Verbosely display the actions taken by the program. + +=back + +=head1 EXAMPLES + +=over 4 + +=item vidir + +=item vidir *.jpeg + +Typical uses. + +=item find | vidir - + +Edit subdirectory contents too. To delete subdirectories, +delete all their contents and the subdirectory itself in the editor. + +=item find -type f | vidir - + +Edit all files under the current directory and subdirectories. + +=back + +=head1 ENVIRONMENT VARIABLES + +=over 4 + +=item EDITOR + +Editor to use. + +=item VISUAL + +Also supported to determine what editor to use. + +=back + +=head1 AUTHOR + +Copyright 2006 by Joey Hess <joey@kitenet.net> + +Licensed under the GNU GPL. + +=cut + +use File::Spec; +use File::Temp; +use Getopt::Long; + +my $error=0; + +my $verbose=0; +if (! GetOptions("verbose|v" => \$verbose)) { + die "Usage: $0 [--verbose] [directory|file|-]\n"; +} + +my @dir; +if (! @ARGV) { + push @ARGV, "." +} +foreach my $item (@ARGV) { + if ($item eq "-") { + push @dir, map { chomp; $_ } <STDIN>; + close STDIN; + open(STDIN, "/dev/tty") || die "reopen: $!\n"; + } + elsif (-d $item) { + $item =~ s{/?$}{/}; + opendir(DIR, $item) || die "$0: cannot read $item: $!\n"; + push @dir, map { "$item$_" } sort readdir(DIR); + closedir DIR; + } + else { + push @dir, $item; + } +} + +if (grep(/[[:cntrl:]]/, @dir)) { + die "$0: control characters in filenames are not supported\n"; +} + +my $tmp=File::Temp->new(TEMPLATE => "dirXXXXX", DIR => File::Spec->tmpdir); +open (OUT, ">".$tmp->filename) || die "$0: cannot create ".$tmp->filename.": $!\n"; + +my %item; +my $c=0; +foreach (@dir) { + next if /^(.*\/)?\.$/ || /^(.*\/)?\.\.$/; + $item{++$c}=$_; + print OUT "$c\t$_\n"; +} +@dir=(); +close OUT || die "$0: cannot write ".$tmp->filename.": $!\n"; + +my @editor="vi"; +if (-x "/usr/bin/editor") { + @editor="/usr/bin/editor"; +} +if (exists $ENV{EDITOR}) { + @editor=split(' ', $ENV{EDITOR}); +} +if (exists $ENV{VISUAL}) { + @editor=split(' ', $ENV{VISUAL}); +} +$ret=system(@editor, $tmp); +if ($ret != 0) { + die "@editor exited nonzero, aborting\n"; +} + +open (IN, $tmp->filename) || die "$0: cannot read ".$tmp->filename.": $!\n"; +while (<IN>) { + chomp; + if (/^(\d+)\t{0,1}(.*)/) { + my $num=int($1); + my $name=$2; + if (! exists $item{$num}) { + die "$0: unknown item number $num\n"; + } + elsif ($name ne $item{$num}) { + next unless length $name; + my $src=$item{$num}; + + if (! (-e $src || -l $src) ) { + print STDERR "$0: $src does not exist\n"; + delete $item{$num}; + next; + } + + # deal with swaps + if (-e $name || -l $name) { + my $tmp=$name."~"; + my $c=0; + while (-e $tmp || -l $tmp) { + $c++; + $tmp=$name."~$c"; + } + if (! rename($name, $tmp)) { + print STDERR "$0: failed to rename $name to $tmp: $!\n"; + $error=1; + } + elsif ($verbose) { + print "'$name' -> '$tmp'\n"; + } + foreach my $item (keys %item) { + if ($item{$item} eq $name) { + $item{$item}=$tmp; + } + } + } + + if (! rename($src, $name)) { + print STDERR "$0: failed to rename $src to $name: $!\n"; + $error=1; + } + else { + if (-d $name) { + foreach (values %item) { + s/^\Q$src\E/$name/; + } + } + if ($verbose) { + print "'$src' => '$name'\n"; + } + } + } + delete $item{$num}; + } + elsif (/^\s*$/) { + # skip empty line + } + else { + die "$0: unable to parse line \"$_\", aborting\n"; + } +} +close IN || die "$0: cannot read ".$tmp->filename.": $!\n"; +unlink($tmp.'~') if -e $tmp.'~'; + +sub rm { + my $file = shift; + + if (-d $file && ! -l $file) { + return rmdir $file; + } + else { + return unlink $file; + } +} + +foreach my $item (reverse sort values %item) { + if (! rm($item)) { + print STDERR "$0: failed to remove $item: $!\n"; + $error=1; + } + if ($verbose) { + print "removed '$item'\n"; + } +} + +exit $error; @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +=head1 NAME + +vipe - edit pipe + +=head1 SYNOPSIS + +command1 | vipe | command2 + +=head1 DESCRIPTION + +vipe allows you to run your editor in the middle of a unix pipeline and +edit the data that is being piped between programs. Your editor will +have the full data being piped from command1 loaded into it, and when you +save, that data will be piped into command2. + +=head1 ENVIRONMENT VARIABLES + +=over 4 + +=item EDITOR + +Editor to use. + +=item VISUAL + +Also supported to determine what editor to use. + +=back + +=head1 AUTHOR + +Copyright 2006 by Joey Hess <joey@kitenet.net> + +Licensed under the GNU GPL. + +=cut + +use warnings; +use strict; +use File::Temp q{tempfile}; + +$/=undef; + +my ($fh, $tmp)=tempfile(); +die "cannot create tempfile" unless $fh; +print ($fh <STDIN>) || die "write temp: $!"; +close $fh; +close STDIN; +open(STDIN, "</dev/tty") || die "reopen stdin: $!"; +open(OUT, ">&STDOUT") || die "save stdout: $!"; +close STDOUT; +open(STDOUT, ">/dev/tty") || die "reopen stdout: $!"; + +my @editor="vi"; +if (-x "/usr/bin/editor") { + @editor="/usr/bin/editor"; +} +if (exists $ENV{EDITOR}) { + @editor=split(' ', $ENV{EDITOR}); +} +if (exists $ENV{VISUAL}) { + @editor=split(' ', $ENV{VISUAL}); +} +my $ret=system(@editor, $tmp); +if ($ret != 0) { + die "@editor exited nonzero, aborting\n"; +} + +open (IN, $tmp) || die "$0: cannot read $tmp: $!\n"; +print (OUT <IN>) || die "write failure: $!"; +close IN; +unlink($tmp); @@ -0,0 +1,105 @@ +#!/usr/bin/perl + +=head1 NAME + +zrun - automatically uncompress arguments to command + +=head1 SYNOPSIS + +zrun command file.gz [...] + +=head1 DESCRIPTION + +Prefixing a shell command with "zrun" causes any compressed files that are +arguments of the command to be transparently uncompressed to temp files +(not pipes) and the uncompressed files fed to the command. + +This is a quick way to run a command that does not itself support +compressed files, without manually uncompressing the files. + +The following compression types are supported: gz bz2 Z xz lzma lzo + +If zrun is linked to some name beginning with z, like zprog, and the link is +executed, this is equivalent to executing "zrun prog". + +=head1 BUGS + +Modifications to the uncompressed temporary file are not fed back into the +input file, so using this as a quick way to make an editor support +compressed files won't work. + +=head1 AUTHOR + +Copyright 2006 by Chung-chieh Shan <ccshan@post.harvard.edu> + +=cut + +use warnings; +use strict; +use IO::Handle; +use File::Spec; +use File::Temp qw{tempfile}; + +my $program; + +if ($0 =~ m{(?:^|/)z([^/]+)$} && $1 ne 'run') { + $program = $1; + if (! @ARGV) { + die "Usage: z$1 <args>\nEquivalent to: zrun $1 <args>\n"; + } +} +else { + $program = shift; + if (! @ARGV) { + die "Usage: zrun <command> <args>\n"; + } +} + +my @argument; +my %child; +foreach my $argument (@ARGV) { + if ($argument =~ m{^(.*/)?([^/]*)\.(gz|Z|bz2|xz|lzo|lzma)$}s) { + my $suffix = "-$2"; + my @preprocess = $3 eq "bz2" ? qw(bzip2 -d -c) : + $3 eq "xz" ? qw(xz -d -c) : + $3 eq "lzo" ? qw(lzop -d -c) : + $3 eq "lzma" ? qw(lzma -d -c) : qw(gzip -d -c); + + my ($fh, $tmpname) = tempfile(SUFFIX => $suffix, + DIR => File::Spec->tmpdir, UNLINK => 1) + or die "zrun: cannot create temporary file: $!\n"; + + if (my $child = fork) { + $child{$child} = $argument; + $argument = $tmpname; + } + elsif (defined $child) { + STDOUT->fdopen($fh, "w"); + exec @preprocess, $argument; + } + else { + die "zrun: cannot fork to handle $argument: $!\n"; + } + } + push @argument, $argument; +} + +while (%child and (my $pid = wait) != -1) { + if (defined(my $argument = delete $child{$pid})) { + if ($? & 255) { + die "zrun: preprocessing for $argument terminated abnormally: $?\n"; + } + elsif (my $code = $? >> 8) { + die "zrun: preprocessing for $argument terminated with code $code\n"; + } + } +} + +my $status = system $program ($program, @argument); +if ($status & 255) { + die "zrun: $program terminated abnormally: $?\n"; +} +else { + my $code = $? >> 8; + exit $code; +} |