jigdo-0.7.3/.cvsignore0000644000175000017500000000040510261546551014433 0ustar richardrichard2 2s 2u 3 3.4 3g 3u 4g Makefile TODO autom4te.cache config.cache config.log config.status configure copying cvs debian debian-mirrors.jigdo debian.lnk go jigdo jigdo-*.tar.bz2 jigdo-*.tar.gz jigdo_*.changes jigdo_*.deb jigdo_*.dsc readme stable tar tmp version jigdo-0.7.3/COPYING0000644000175000017500000004311007701377531013473 0ustar richardrichard GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You 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 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. , 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. jigdo-0.7.3/Makefile.in0000644000175000017500000002041510433356155014503 0ustar richardrichard# Project: Jigdo (Jigsaw download) # __ _ # |_) /| Copyright (C) 2001-2006 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ # this Makefile mostly forwards make commands to the other directories srcdir = @srcdir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ mandir = @mandir@ datadir = @datadir@ PACKAGE = jigdo INSTALL = @INSTALL@ INSTALL_EXE = @INSTALL@ -s INSTALL_DATA = @INSTALL@ -m 644 INSTALL_SCRIPT = @INSTALL@ -m 755 AWK = @AWK@ # echo `(echo progress051.xpm;sed -n 's/^.*>\(.*\.xpm\)<.*$/\1/p' jigdo.glade)|sort|uniq` icons = close.png jigdo-logo.png pause.png progress-green.png \ restart.png start.png stop.png catalogs = @CATALOGS@ .PHONY: all all_msg clean distclean mostlyclean maintainer-clean \ dep depend doc cvsdist check install test loc deb debrpm \ install-jigdo-file install-jigdo-lite install-jigdo \ install-po # "gfx" symlink is needed for the pixmaps to be found by jigdo all doc mostlyclean dep depend: Makefile -test -h gfx -o -d gfx || ln -s "$(srcdir)/gfx" gfx cd src && $(MAKE) $@ cd doc && $(MAKE) $@ cd po && $(MAKE) $@ check test: Makefile -test -h gfx -o -d gfx || ln -s "$(srcdir)/gfx" gfx cd src && $(MAKE) $@ clean: Makefile cd src && $(MAKE) $@ cd doc && $(MAKE) $@ cd po && $(MAKE) $@ rm -f mirrors.jigdo mirrors.list mirrors.txt distclean: Makefile cd src && $(MAKE) $@ cd doc && $(MAKE) $@ cd po && $(MAKE) $@ rm -f mirrors.jigdo mirrors.list mirrors.txt rm -f jigdo-*.tar.gz jigdo-*.tar.bz2 rm -f *~ \#*\# *.bak rm -f Makefile config.h config.cache config.log config.status rm -f deb/*~ deb/\#*\# deb/*.bak deb/changelog rm -f scripts/*~ scripts/\#*\# scripts/*.bak # -rm -f debian maintainer-clean: Makefile @echo 'This command is intended for maintainers to use; it' @echo 'deletes files that may need special tools to rebuild.' cd src && $(MAKE) $@ cd doc && $(MAKE) $@ cd po && $(MAKE) $@ rm -f mirrors.jigdo mirrors.list mirrors.txt rm -f jigdo-*.tar.gz jigdo-*.tar.bz2 rm -f *~ \#*\# *.bak rm -f Makefile config.h config.cache config.log config.status rm -f configure cd $(srcdir) && autoconf install: install-po install-jigdo-file install-jigdo-lite \ install-jigdo-mirror @IF_GUI@ install-jigdo install-jigdo-file: $(INSTALL) -d $(DESTDIR)$(bindir) $(INSTALL_EXE) src/jigdo-file $(DESTDIR)$(bindir) $(INSTALL) -d $(DESTDIR)$(mandir)/man1 x="doc/jigdo-file.1"; \ test -f "$$x" || x="$(srcdir)/$$x"; \ $(INSTALL) "$$x" $(DESTDIR)$(mandir)/man1 install-jigdo-lite: $(INSTALL) -d "$(DESTDIR)$(bindir)" $(INSTALL) -d "$(DESTDIR)$(datadir)/jigdo" $(INSTALL_SCRIPT) "$(srcdir)/scripts/jigdo-lite" \ "$(DESTDIR)$(bindir)" sed -e 's%"\(mirrors.jigdo"\)%"$(datadir)/jigdo/debian-\1%' \ <"$(srcdir)/scripts/jigdo-lite" \ >"$(DESTDIR)$(bindir)/jigdo-lite" $(INSTALL_DATA) "$(srcdir)/scripts/debian-mirrors.jigdo" \ "$(DESTDIR)$(datadir)/jigdo" $(INSTALL) -d $(DESTDIR)$(mandir)/man1 x="doc/jigdo-lite.1"; \ test -f "$$x" || x="$(srcdir)/$$x"; \ $(INSTALL) "$$x" $(DESTDIR)$(mandir)/man1 install-jigdo-mirror: $(INSTALL) -d "$(DESTDIR)$(bindir)" $(INSTALL_SCRIPT) "$(srcdir)/scripts/jigdo-mirror" \ "$(DESTDIR)$(bindir)" $(INSTALL) -d $(DESTDIR)$(mandir)/man1 x="doc/jigdo-mirror.1"; \ test -f "$$x" || x="$(srcdir)/$$x"; \ $(INSTALL) "$$x" $(DESTDIR)$(mandir)/man1 install-jigdo: $(INSTALL) -d "$(DESTDIR)$(bindir)" $(INSTALL_EXE) src/jigdo "$(DESTDIR)$(bindir)" $(INSTALL) -d "$(DESTDIR)$(datadir)/jigdo/pixmaps" @for x in $(icons); do \ echo "$(INSTALL_DATA) \"$(srcdir)/gfx/$$x\"" \ "\"$(DESTDIR)$(datadir)/jigdo/pixmaps\""; \ $(INSTALL_DATA) "$(srcdir)/gfx/$$x" \ "$(DESTDIR)$(datadir)/jigdo/pixmaps" || exit 1; \ done $(INSTALL) -d $(DESTDIR)$(mandir)/man1 x="doc/jigdo.1"; \ test -f "$$x" || x="$(srcdir)/$$x"; \ $(INSTALL) "$$x" $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) "$(srcdir)/COPYING" \ "$(DESTDIR)$(datadir)/jigdo/COPYING" install-po: if test -n "$(catalogs)"; then for file in $(catalogs); do \ lang=`echo $$file | sed -e 's/\.gmo$$//'`; \ dir="$(DESTDIR)$(datadir)/locale/$$lang/LC_MESSAGES"; \ echo "$(INSTALL) -d \"$$dir\""; \ $(INSTALL) -d "$$dir"; \ s="$(srcdir)/po/$$file"; \ if test -f "po/$$file"; then s="po/$$file"; fi; \ echo "$(INSTALL_DATA) $$s \"$$dir/$(PACKAGE).mo\""; \ $(INSTALL_DATA) "$$s" "$$dir/$(PACKAGE).mo" \ || exit 1; \ done; fi Makefile: Makefile.in configure jigdo.spec sh config.status # update Makefile from Makefile.in configure: configure.ac cd "$(srcdir)" && autoconf rm -f config.cache # allow "make CXXFLAGS=-O0" while staying compatible with non-GNU-make .EXPORT_ALL_VARIABLES: ; #______________________________________________________________________ # Nonstandard targets # Vanity meter :-) loc: f=`find $(srcdir)/src \( -name '*.cc' -o -name '*.[hif]h' \) -a ! -name interface.cc` \ && printf '%d files, %d lines of code\n' \ `echo $$f|wc -w` `cat $$f|wc -l` # Compile and package unofficial Debian package deb: @if test "`pwd`" != "`cd '$(srcdir)' && pwd`"; then \ echo " * Building .deb package only possible if you are";\ echo " * building in the source dir, i.e. invoked the"; \ echo " * configure script as \`./configure'"; \ exit 1; fi test -e debian || ln -s deb debian dpkg-buildpackage -rfakeroot -us -uc -b # Convert Debian package into RPM format debrpm: deb for deb in ../jigdo*.deb; do \ fakeroot alien -r "$$deb"; \ done # Compile and create a tar.bz2 containing the tools and documentation bindist: Makefile @read tmpa tmpb VERSION <"$(srcdir)/jigdo.spec"; \ if test -d "jigdo-bin-$$VERSION"; then \ echo "\`jigdo-bin-$$VERSION' exists - delete it first";\ exit 1; \ fi rm -f src/jigdo-file cd src && $(MAKE) Y='-static' all-msg jigdo-file strip cd doc && $(MAKE) all jdir=jigdo-bin-`read a b v <"$(srcdir)/jigdo.spec" && echo $$v`; \ mkdir "$$jdir"; \ $(INSTALL_SCRIPT) $(srcdir)/scripts/jigdo-lite "$$jdir";\ $(INSTALL_SCRIPT) $(srcdir)/scripts/jigdo-mirror \ "$$jdir"; \ $(INSTALL_EXE) src/jigdo-file "$$jdir"; \ grep -v "^##" $(srcdir)/doc/README-bindist.txt \ >"$$jdir/README"; \ $(INSTALL_DATA) $(srcdir)/doc/*.html "$$jdir"; \ $(INSTALL_DATA) $(srcdir)/doc/*.1 "$$jdir"; \ $(INSTALL_DATA) $(srcdir)/COPYING "$$jdir"; \ $(INSTALL_DATA) $(srcdir)/scripts/debian-mirrors.jigdo \ "$$jdir/mirrors.jigdo"; \ tar -cf "$$jdir.tar" "$$jdir"; \ bzip2 -f -9 "$$jdir.tar"; \ rm -rf "$$jdir" src/jigdo-file debian-mirrors.jigdo: Mirrors.masterlist $(AWK) -f $(srcdir)/scripts/convert-cvsmirrors.awk "$<" >"$@" # Create source tarballs from CVS cvsdist: Makefile @read tmpa tmpb VERSION <"$(srcdir)/jigdo.spec"; \ if test -d "jigdo-bin-$$VERSION"; then \ echo "\`jigdo-bin-$$VERSION' exists - delete it first";\ exit 1; \ fi cvs -Q checkout jigdo cd jigdo && autoconf @f=`find jigdo -name '*.cc' -o -name '*.[hif]h'` \ && printf '%d files, %d lines of code\n' \ `echo $$f|wc -w` `cat $$f|wc -l` @echo "To update gettext: make -C po update-pot update-po" @read tmpa tmpb VERSION <"$(srcdir)/jigdo.spec"; \ cvstag="rel-`echo $$VERSION | sed -e 's/[^0-9a-zA-Z_-]\+/-/g'`"; \ printf "Tag this as \`$$cvstag' in CVS? [y/N/force] "; \ read; \ if test "$$REPLY" = y; then \ echo "cd jigdo && cvs tag $$cvstag ."; \ cd jigdo && cvs tag $$cvstag .; \ elif test "$$REPLY" = force; then \ echo "cd jigdo && cvs tag -F $$cvstag ."; \ cd jigdo && cvs tag -F $$cvstag .; \ fi cd jigdo/doc && $(MAKE) -f ../../doc/Makefile rm -f jigdo/doc/*~ cd src && $(MAKE) depend gtk/interface.cc cp src/Makedeps jigdo/src cp src/gtk/interface.cc src/gtk/interface.hh jigdo/src/gtk cd jigdo/po && $(MAKE) -f ../../po/Makefile update-gmo v=`read a b v <"$(srcdir)/jigdo.spec" && echo $$v`; \ mv jigdo "jigdo-$$v"; \ find "jigdo-$$v" -type f | sort \ | tar --exclude=CVS --exclude=autom4te.cache -T - -cf \ "jigdo-$$v.tar"; \ test -d ~/deb/my && gzip -9 -c \ <"jigdo-$$v.tar" >~/deb/my/"jigdo_$$v.orig.tar.gz"; \ bzip2 -f -9 "jigdo-$$v.tar"; \ rm -rf "jigdo-$$v" jigdo-0.7.3/README0000644000175000017500000000776010433371111013313 0ustar richardrichard Jigsaw Download (jigdo) ~~~~~~~~~~~~~~~~~~~~~~~ See the doc/ directory for documentation - start with the HOWTO for an introduction to jigdo. Jigsaw Download homepage: Debian CD images via jigdo: ---------------------------------------------------------------------- To build the programs, execute: ./configure && make (If no file named "configure" is present in this directory, you have downloaded a jigdo CVS snapshot. Have a look at the "bootstrapping" section in doc/Hacking.txt.) After a successful build, you can install the program (by default into /usr/local) with make install To build Debian packages (.deb) from the sources, execute: deb/rules To build an RPM package (.rpm) from the sources, execute: rpmbuild -ta jigdo-x.y.z.tar.bz2 To build a .tar.bz2 with docs and statically linked binaries, execute: ./configure && make bindist Apart from the usual options (see "./configure --help"), the configure script also recognizes the following options: --without-libdb Don't use libdb3 (it's necessary for jigdo-file's cache) --with-gui Build the jigdo GTK+ GUI application [auto] --with-uint64=TYPE Specify unsigned type of at least 64 bits [auto] (Replace spaces with underscores in TYPE) --disable-nls Disable multi-language support --enable-debug Compile in lots of additional debugging code --disable-libwww-hacks Don't compile fixed versions of some libwww code (Try this if you see link errors/crashes) ---------------------------------------------------------------------- Authors: Richard Atterer is the main jigdo author. Other contributors: - Paul Bolle made the jigdo.spec (based on an earlier version by Alexander Skwar) - Peter Jay Salzman

maintains the Debian jigdo mini HOWTO - Jochen Hepp : jigdo-file patches - Anne Bezemer : Some code snippets in the jigdo-lite script jigdo uses some code written by other people: - Free Software Foundation, Inc.: Library routines. [Files: glibc-*] - James Henstdridge : Code for integration of glib and libwww. [Files: glibwww-*] - Damon Chaplin and Martijn van Beers : Support code for the GUI, generated by Glade. [File: gtk-support.cc] ---------------------------------------------------------------------- Copyright (C) 2001-2006 | richard@ Richard Atterer | atterer.net 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. In addition, as a special exception, the author gives permission to link the jigdo code with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and to distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify any jigdo source file, you may extend this exception to your version of the file, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. Please note: The copyright notice in the file COPYING only applies to the text of the GNU General Public License; the copyright of the individual source files is as specified at the top of each file and above. Also note that the code is licensed under GPL _version_2_ and no other version. Special licensing for my (RA's) code is available on request. jigdo-0.7.3/THANKS0000644000175000017500000000311510065372752013351 0ustar richardrichard jigdo wouldn't have gotten this far without the following people. Thanks to... Phil Hands for giving me an account on cdimage.debian.org and deciding to use jigdo as the main distribution scheme for Debian 3.0r0 Peter Jay Salzman for writing and maintaining the jigdo mini-HOWTO - also thanks to the translators: Conrad Wood, Elcio Mello, Marcelo Ramos Steve McIntyre for his project JTE (Jigdo Template Extractor), which patches mkisofs to allow direct creation of .jigdo/.template files Paul Bolle (and earlier Alexander Skwar) for maintaining the jigdo.spec. Wookey for being the first person to actually try it out and give me feedback :) Santiago Garcia Mantinan for lots of beta testing and for finding that cache bug 5 days before the Debian release Attila Nagy for beta testing, setting up jigdo generation on fsn.hu, for generating the first .jigdo DVD files ever and for submitting it to the BSD ports collection Peter Siering of c't magazine for feedback and using jigdo for the Heise VDR CDs Anne Bezemer for writing the pseudo image kit (PIK) which inspired me to write jigdo, and for doing an early port of jigdo to Windows Mattias Wadenstein for beta testing and mirroring Jason Andrade for beta testing and mirroring Julz for offering me a free CD writer and then sending it around half the world, from New Zealand to Germany! Bernd Herd for offering another free CD writer - wow, thanks, I only need one though! :-) ...and to numerous users for many "jigdo rocks" and "jigdo sucks" mails. jigdo-0.7.3/changelog0000644000175000017500000004007210433430170014277 0ustar richardrichardChangelog for Jigsaw Download -*- Text -*- ------------------------------------------------------------------------ jigdo 0.7.3 -- Richard Atterer, 19 May 2006 - A maintenance release with some bug fixes - Fix for compilation on 64 bit architectures (sigh, as usual...) - Fixes to make the code compile cleanly with GCC 4.1 jigdo 0.7.2 -- Richard Atterer, 12 Jul 2005 - jigdo-file: Added support for bzip2 (de)compression in .template files, as an alternative to zlib compression. Bzip2 is disabled by default (but may become the default in the future). Use --bzip and --gzip to switch between the two. - Patch by Jochen Hepp: New switch --scan-whole-file for "jigdo-file scan" command: Scan whole file instead of only first block - Patch by Jochen Hepp: New switch --no-greedy-matching for "jigdo-file make-template" prevents small matches from cancelling pending larger matches. - jigdo: Switched over from libwww to libcurl for downloads - jigdo-lite: Fixed regular expressions so they work with sed 4.1 and non-GNU sed - jigdo-lite: --noask switch to allow running jigdo-lite from cron jobs (Silas Bennett) - jigdo-file: Bugfix for the code which deduces missing --image/ --jigdo/--template arguments. Broken in 0.7.1, would deduce /x.iso.template from /x.iso, instead of /x.template (Adrian Bunk) - Fix for compilation on 64 bit architectures - Fixed "compiler recent enough" check in configure.ac for GCC 3.5+ - Fixes for GCC 4.0 jigdo 0.7.1 -- Richard Atterer, 20 Jun 2004 - Workaround for problems with big files (DVD images) when compiling with GCC 3.0 to 3.3. - Fix for Windows version: "wget: BUG: unknown command `timeout'" no longer occurs (was spurious anyway, but irritated many people) - Windows version compiled with GCC 3.4, can hopefully create big files (DVD images) - removed "too many files missing, won't attempt to download them from fallback mirror" logic in jigdo-lite because it was broken - .jigdo file format: Semantics of multiple [Image] sections have changed: Formerly, the idea was that one .jigdo could provide multiple images, but the idea never caught on and the idea "one .jigdo => one image" is firmly planted in people's minds now. The original incentive of allowing multiple [Image] sections was to make it possible to avoid duplication of .jigdo data, by providing all 7 CDs and the single DVD in one .jigdo. This duplication can now be avoided by [Include]ing the CDs' files from the DVD's, because all except the first [Image] section are ignored. - Fixed minor bug in jigdo-lite; it would sometimes abort even if no error code was returned by jigdo-file - GUI: Added code for processing of .jigdo files and [Include] - GUI: Added simple caching of downloaded data during .jigdo downloads - Bugfix for "jigdo-file make-image" (Steve McIntyre): Failed assertion `nextAlignedOff>off' and huge .template with >4GB image. - Bugfix for "jigdo-file make-template": Sometimes *incorrectly* reports: "You have found a bug". (Maxim Reznik) - Bugfix for "jigdo-file make-image": Handle I/O errors more gracefully (Brian Bennett) - Bugfix for "jigdo-file make-image" (Andreas Krüger): Do not allocate on the stack an array whose size is the number of matched files in the image. This gave, er, "funny" results with tens of thousands of matches... - Added comfortable debugging aids ("debug(fmt,...)", --debug=...) - Various documentation updates (manpages, Hacking.txt, changelog;-P) - Fix for build problems if size_t is 64 bits wide - Various fixes to allow compilation with GCC 3.4 - Added unit tests for many parts of the code, with support in depend.awk - Fixed bug in html-beautify.awk - Vanity meter reports 26000 lines of code in 120 files jigdo 0.7.0 -- Richard Atterer, 05 May 2003 - GUI: Code for guessing the user's proxy settings. On Unix, reads the configuration files of various browsers. On Windows, reads the MSIE registry entries. - GUI: Single downloads (e.g. of .iso files) work, can be paused and continued, stopped and resumed. - GUI: Work on processing of .jigdo files is begun, but far from completed - Changed heuristics for "jigdo-file make-template". As with all heuristics, may actually turn out to be worse than before for some cases. However, with experiments of mine with i386 Debian DVD images, it resulted in a significant size reduction of the template. - Fix for bug in "make-template" which caused invalid template files to be generated sometimes. Funny how these make-template bugs keep popping up, I've already fixed about 5 of them! :-7 - jigdo-lite: Fix for problem with the "read" builtin with some shells, e.g. dash - Fixed build problem on newer GCC 3.2 - Source code now exceeds 20000 lines of code in almost 100 files. jigdo 0.6.9 -- Richard Atterer, 19 Jan 2003 - jigdo-lite: New batch mode - if you enter (or supply on the command line) several files/URLs, all the questions are only asked once before the first download. Furthermore, the program *always* asks for Debian/Non-US mirror URLs, it only asks for "Files to scan" once, it always downloads all images offered by all .jigdo files, and it does not stop immediately after errors, but continues with the next image. - You can enter URLs like "http://server/cd-{1_NONUS,2,3}.jigdo" and they'll be expanded to several separate URLs. - jigdo-lite: --scan command line option to avoid "Files to scan" question. - By popular demand, always use wget's --continue switch, except when fetching .jigdo files. This *will* cause severe breakage in the case that someone starts the download of a .template of fsn.hu's daily snapshot on one day, and resumes it the next day, but that's just a problem people will have to be aware of. :-/ - jigdo-lite: Use --force-directories, allows --continue to be used for individual package downloads. (Previously, all downloaded .debs etc were stored directly in the tmp dir, so --continue could not be used because there are e.g. several files named "driver-1.bin" on Debian servers.) - jigdo-lite: Use a separate temporary dir for each new download. This allows you to run several jigdo-lite instances in the same dir at the same time. - jigdo-lite: When temporary dir is already present, scan its contents *before* downloading the first batch of files (Goswin Brederlow and others) - jigdo-file: Default for --min-length is now 1k by default, the hardwired absolute minimum is 256 bytes (used to be 4k/1k) - jigdo-file make-template: Changed heuristics when dropping possible file matches. Difficult to explain... essentially, together with the previous change, .template files for Debian CDs will get quite a bit smaller (Santiago Garcia Mantinan) - On Windows, fail with meaningful error for images >2GB (ATM, we don't have big file support with MinGW GCC) - Fixed compilation error with gcc 3 and --without-libdb (Attila) - Improved check for getopt_long support in configure script, it failed on FreeBSD 5 (Attila Nagy) - Added jigdo.pot to output, including initial German translation - configure script: Made --without-gui the default, as people have problems compiling with GTK & libwww, and the GUI is useless ATM - jigdo-lite: Misc fixes to make it work with mingw under Windows - jigdo-lite accepts --help and --version options - jigdo-lite: Changed timeout from wget's default 15 minutes to just 30 seconds (only has an effect if no ~/.jigdo-lite present) - debian-mirrors.jigdo: Bugfix in creation script (HTTP/FTP paths on server were swapped), omit FTP URL if HTTP access possible (Phil Hands) - Build environment: Fix for bug in depend.awk, which made it output dependencies on .ih files when it shouldn't - jigdo-file make-template: Prevent infinite loop if I/O error occurs during scanning of files (Blars Blarson) - jigdo-file make-template: Fixed bug which could lead to segmentation faults if the image contained a long, but only partial match of a file, and another long partial match of another file within the "outer" partial match. - jigdo-file make-template: Bugfix - server line was sometimes added to [Image] section when using --merge - jigdo-file make-template: Fix for bug which is extremely unlikely in practice, caused segfault if read() returned <32 bytes or so while reading start of file. - jigdo-file make-template bugfix: Now creates properly quoted entries in [Parts] sections for filenames containing spaces or characters like "'\ - Improved handling of invalid cache files (created if disc gets full during cache update): Instead of crashing, jigdo-file prints an error. However, libdb still corrupts the cache file. (Dmitry E. Melamud, Petr Slansky) jigdo 0.6.8 -- Richard Atterer, 28 Jun 2002 - jigdo-lite/mirror use "jigdo-file --no-cache" when reading downloaded files from tmp dir. Cache caused problems because ATM the Debian mirrors contain two different "driver-4.bin" files with identical timestamps. (Santiago Garcia Mantinan) - jigdo-lite uses "wget -e continue=off" for people who put continue=on into their wgetrc. (Iain Broadfoot) - At long last, succeeded in fully porting glibwww to Windows - Fixed bug in md5sum generation, which might lead to incorrect md5sums being generated, but in practice never has. jigdo 0.6.7 -- Richard Atterer, 06 Jun 2002 - Updated md5sum code, should now work on big-endian architectures - New version of jigdo-mirror supports include/exclude filtering - No longer use -g switch, unless --enable-debug used - Now compiles with gcc 3.1 jigdo 0.6.6 -- Richard Atterer, 17 May 2002 - jigdo-lite/mirror: Template URLs can be relative to jigdo URL - jigdo-file: Any template URL added by default is relative - jigdo-lite: Don't use "wget -c" to get jigdo/template files - causes problems if the files are updated daily on the server - jigdo-file: >4GB file sizes now really supported on Linux (James W. Jayaputera) - does still not work under Windows. - jigdo-mirror is now installed by "make install" - Updated documentation, new manpage for jigdo-mirror - jigdo-file make-template: New --merge switch allows you to add this run's data to an existing jigdo file. - jigdo-file now writes out a Template-MD5Sum=... entry to the jigdo file. When using --merge, the entry can also be added to an existent [Image] section. jigdo-lite uses it to verify downloaded template data. - jigdo-file make-template: Always moves [Parts] section to the bottom of the file jigdo 0.6.5 -- Richard Atterer, 17 Apr 2002 - jigdo-lite: Better fallback handling, now also works for files that changed (rather than disappeared) on the server. - jigdo-lite: If ~/.jigdo-lite not present, try to fetch mirror info from /etc/apt/sources.list - portability fixes (Mattias Wadenstein) - jigdo GUI: Some more work done, but it's still not usable - new jigdo-mirror script (in the source tarball only) jigdo 0.6.4 -- Richard Atterer, 02 Mar 2002 - Fixed a make-template bug introduced in 0.6.3, which caused jigdo-file to crash if there are more than 32 files with identical content (Attila Nagy) - jigdo-lite: Allow customization of flags passed to jigdo-file and wget, by making it save and restore variables in ~/.jigdo-lite. For example, add "--uri Debian=http://myserver.lan/" to the definition of jigdoOpts to define an additional mirror (turns the selected mirrors into fallback mirrors). - jigdo-lite: Made script work with ash - Added configure check for _snprintf (needed for Windows) jigdo 0.6.3 -- Richard Atterer, 24 Feb 2002 - jigdo-file make-template: Some loop unrolling, some other optimizations, resulting in a speed increase of at least 30%! - FORMAT CHANGE of template files: Template data now includes RsyncSums - this will be useful in the future for aborting downloads early and when upgrading images to newer versions. This version still reads the old template format, but support for that will be dropped before 1.0.0. - jigdo-lite: Can now supply >1 image sections in .jigdo files - jigdo-lite: Support for gzipped .jigdo files - jigdo-lite: Added a small manpage - jigdo-lite: Use "wget --dot-style=mega --passive-ftp" - Really fixed issues this time - by not using sstream at all. (It is not possible to implement an sstream class for the iostream supplied with Debian Potato's GCC 2.95) - Can now build a .deb just by executing "deb/rules" - configure: Can now use --without-gui to prevent jigdo GUI from getting built and installed (does not work with "deb/rules") - Added jigdo.spec for building of RPMs (Alexander Skwar) - Slightly changed the way that missing --image/--jigdo/--template arguments are deduced from other parameters. (Thomas Arnold) - Fixes to make jigdo-file build on 64-bit architectures - Changed abbreviation of "md5sum" command from "md" to "md5". That breaks compatibility, but I keep typing "md5"... :) - Small hack to make "jigdo-file md5 /some/path" include the leading "/" in its output. - jigdo-lite: Fix for obscure problem with "sed /foo/!p" on BSD and some Linux versions - use "egrep -v foo" instead (Attila Nagy) jigdo 0.6.2 -- Richard Atterer, 26 Jan 2002 - Added this changelog file to the distribution! :-) - gettext support (but no translations are present yet) - Portability fixes to jigdo-lite (Anne Bezemer) - Portability fixes to the jigdo-file code (for RedHat/Mandrake systems, systems without such as Debian Potato) - Support for fallback servers in jigdo-lite - Support for fallback servers in jigdo-file, in the form of a new "print-missing-all" command - First upload of jigdo packages to the Debian archive jigdo 0.6.1 -- Richard Atterer, 17 Dec 2001 - Changelog lost jigdo 0.6.0 -- Richard Atterer, 15 Nov 2001 - Because the "proper jigdo" (the GTK+ download manager type app) is not going to be finished anytime soon, I've written a small shell script called "jigdo-lite" which does an equivalent job. It downloads files with wget and assembles them using jigdo-file. jigdo-lite can "upgrade" old images to a newer version. - Very first, ***pre-alpha*** version of the GTK+ jigdo application. There is no download capability or other functionality present at all yet. - As of a few days ago, the first beta site offering downloads of Debian 2.2r4 via jigdo is available on cdimage. Thanks for the account to Phil Hands! jigdo 0.5.3 -- Richard Atterer, 13 Sep 2001 - *** jigdo-file is finished! *** With the introduction of a cache for file checksums, it is finally possible in practice to let it loose on a Debian archive mirror. - Update and large extension of the documentation. - Now compiles with GCC 3. - Now compiles under Windows! You do not even need Cygwin; plain mingw is sufficient. - Numerous small fixes and changes. Source code now exceeds 10000 lines of code. jigdo 0.5.2 -- Richard Atterer, 2 Sep 2001 - Would you believe it, documentation!!! (in the form of a manpage) - "print-missing" sub-command for jigdo-file allows for mirror selection etc - more than the old PIK's functionality is now present - Major internal extensions (nearly 2000 lines of code) which don't show now, but which will eventually come in handy for the GUI tool jigdo 0.5.1 -- Richard Atterer, 22 Jul 2001 - Can now merge files into a half-finished image with >1 jigdo-file invocations. This will be useful e.g. to "upgrade Debian CD images", i.e. you can take some .debs from the old CD, so they needn't be downloaded again. - Can assign labels to input file locations, e.g. "--label /opt/mirrors/debian=Debian" - Cleanup & extension of .jigdo file format jigdo 0.5.0 -- Richard Atterer, about 20 Jun 2001 - Initial release, not very usable. jigdo 0.0.0 -- Richard Atterer, December 2000 - Discussion on debian-cd, jigdo enters vapourware stage. jigdo-0.7.3/configure0000755000175000017500000077327710433432636014371 0ustar richardrichard#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # # Copyright (C) 2003 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="src/jigdo-file.cc" # Factoring default headers for most tests. ac_includes_default="\ #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif #if HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS JIGDO_VERSION AWK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX IF_GXX2 IFNOT_GXX2 INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CXXCPP EGREP exe IF_WINDOWS IF_UNIX have_wget IF_DEBUG IFNOT_DEBUG PKGCONFIG IF_GUI GTKCFLAGS GTKLIBS GLIBLIBS CURLCFLAGS CURLLIBS have_glade USE_NLS CATALOGS IF_CROSSCOMPILING IFNOT_CROSSCOMPILING LIBOBJS LTLIBOBJS' ac_subst_files='SRC_MAKEDEPS' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CXX_set=${CXX+set} ac_env_CXX_value=$CXX ac_cv_env_CXX_set=${CXX+set} ac_cv_env_CXX_value=$CXX ac_env_CXXFLAGS_set=${CXXFLAGS+set} ac_env_CXXFLAGS_value=$CXXFLAGS ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} ac_cv_env_CXXFLAGS_value=$CXXFLAGS ac_env_CXXCPP_set=${CXXCPP+set} ac_env_CXXCPP_value=$CXXCPP ac_cv_env_CXXCPP_set=${CXXCPP+set} ac_cv_env_CXXCPP_value=$CXXCPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-debug Compile in lots of additional debugging code --disable-nls Disable multi-language support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pkg-config-prefix=PATH When cross-compiling, specify prefix of libraries/headers for target architecture none --without-libdb Don't use libdb (it's necessary for jigdo-file's cache) --with-gui Build the jigdo GTK+ GUI application auto --with-libcurl=-lcurl Use given -l switch instead of trying to determine the right one --with-uint64=TYPE Specify unsigned type of at least 64 bits auto (Replace spaces with underscores in TYPE) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd "$ac_popdir" done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Copyright (C) 2003 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers src/config.h" read tmpa tmpb JIGDO_VERSION < "$srcdir/jigdo.spec" cat >>confdefs.h <<_ACEOF #define JIGDO_VERSION "$JIGDO_VERSION" _ACEOF if test -f "/etc/debian_version"; then installDevel() { echo "$as_me:$LINENO: result: * (Your system appears to be Debian-based; try" >&5 echo "${ECHO_T} * (Your system appears to be Debian-based; try" >&6 echo "$as_me:$LINENO: result: * installing the \`$1-dev' package.)" >&5 echo "${ECHO_T} * installing the \`$1-dev' package.)" >&6 } elif test -x "/usr/bin/rpm" -o -x "/usr/local/bin/rpm"; then installDevel() { echo "$as_me:$LINENO: result: * (Your system appears to be RPM-based; try" >&5 echo "${ECHO_T} * (Your system appears to be RPM-based; try" >&6 echo "$as_me:$LINENO: result: * installing the package named \`$2-devel' or" >&5 echo "${ECHO_T} * installing the package named \`$2-devel' or" >&6 echo "$as_me:$LINENO: result: * similar.)" >&5 echo "${ECHO_T} * similar.)" >&6 } else installDevel() { echo "$as_me:$LINENO: result: * (If the software on your system is managed by a" >&5 echo "${ECHO_T} * (If the software on your system is managed by a" >&6 echo "$as_me:$LINENO: result: * package manager like RPM, try installing the" >&5 echo "${ECHO_T} * package manager like RPM, try installing the" >&6 echo "$as_me:$LINENO: result: * package named \`$2-devel' or similar.)" >&5 echo "${ECHO_T} * package named \`$2-devel' or similar.)" >&6 } fi for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_AWK+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then echo "$as_me:$LINENO: result: $AWK" >&5 echo "${ECHO_T}$AWK" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$AWK" && break done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std1 is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std1. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then echo "$as_me:$LINENO: result: $CXX" >&5 echo "${ECHO_T}$CXX" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 echo "${ECHO_T}$ac_ct_CXX" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CXX" && break done test -n "$ac_ct_CXX" || ac_ct_CXX="g++" CXX=$ac_ct_CXX fi # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C++ compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 if test "${ac_cv_cxx_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 GXX=`test $ac_compiler_gnu = yes && echo yes` ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS CXXFLAGS="-g" echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cxx_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cxx_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cxx_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu CFLAGS=`echo "$CFLAGS" | sed 's/\(^\| \)-g\( \|$\)/ /'` CXXFLAGS=`echo "$CXXFLAGS" | sed 's/\(^\| \)-g\( \|$\)/ /'` IF_GXX2="#" IFNOT_GXX2="" if test "$GXX" = yes; then echo "$as_me:$LINENO: checking for GCC version" >&5 echo $ECHO_N "checking for GCC version... $ECHO_C" >&6 gccver=`$CXX -dumpversion` echo "$as_me:$LINENO: result: $gccver" >&5 echo "${ECHO_T}$gccver" >&6; case "$gccver" in 2.*) IF_GXX2="" IFNOT_GXX2="#";; 3.0|3.0.*|3.1|3.1.*|3.3|3.3.*) #AC_MSG_RESULT([ * Big file support may be broken in GCC 3.2 and]) #AC_MSG_RESULT([ * 3.3! If you compile jigdo on Linux (and maybe]) #AC_MSG_RESULT([ * other OSes), it might not be able to create]) #AC_MSG_RESULT([ * files bigger than 2 GB, such as DVD images.]) #AC_MSG_RESULT([ * (Try GCC 2.95 or GCC 3.4 or later)]) esac fi ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL=$ac_install_sh fi fi echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 if test -z "$CXXCPP"; then if test "${ac_cv_prog_CXXCPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi echo "$as_me:$LINENO: result: $CXXCPP" >&5 echo "${ECHO_T}$CXXCPP" >&6 ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for size_t" >&5 echo $ECHO_N "checking for size_t... $ECHO_C" >&6 if test "${ac_cv_type_size_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((size_t *) 0) return 0; if (sizeof (size_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_size_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_size_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 echo "${ECHO_T}$ac_cv_type_size_t" >&6 if test $ac_cv_type_size_t = yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned _ACEOF fi echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 if test "${ac_cv_c_bigendian+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # See if sys/param.h defines the BYTE_ORDER macro. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN bogus endian macros #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then # It does; now see whether it defined to BIG_ENDIAN or not. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_bigendian=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # It does not; compile a test program. if test "$cross_compiling" = yes; then # try to guess the endianness by grepping values into an object file ac_cv_c_bigendian=unknown cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } int main () { _ascii (); _ebcdic (); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long l; char c[sizeof (long)]; } u; u.l = 1; exit (u.c[sizeof (long) - 1] == 1); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=no else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_bigendian=yes fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 echo "${ECHO_T}$ac_cv_c_bigendian" >&6 case $ac_cv_c_bigendian in yes) cat >>confdefs.h <<\_ACEOF #define WORDS_BIGENDIAN 1 _ACEOF ;; no) ;; *) { { echo "$as_me:$LINENO: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&5 echo "$as_me: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} { (exit 1); exit 1; }; } ;; esac echo "$as_me:$LINENO: checking whether we are compiling for Windows" >&5 echo $ECHO_N "checking whether we are compiling for Windows... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #if defined WINNT || defined __WINNT || defined __WINNT__ || defined WIN32 \ || defined _WIN32 || defined __WIN32 || defined __WIN32__ poorsoul #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "poorsoul" >/dev/null 2>&1; then is_windows=yes else is_windows=no fi rm -f conftest* echo "$as_me:$LINENO: result: $is_windows" >&5 echo "${ECHO_T}$is_windows" >&6 if test "$is_windows" = yes; then cat >>confdefs.h <<\_ACEOF #define WINDOWS 1 _ACEOF exe=".exe" IF_WINDOWS="" IF_UNIX="#" else cat >>confdefs.h <<\_ACEOF #define UNIX 1 _ACEOF exe="" IF_WINDOWS="#" IF_UNIX="" fi if test "$is_windows" = "yes"; then if test "$ac_cv_prog_cc_g" = "yes"; then CFLAGS="$CFLAGS -mms-bitfields -march=pentium" fi if test "$ac_cv_prog_cxx_g" = "yes"; then CXXFLAGS="$CXXFLAGS -mms-bitfields -march=pentium" fi fi echo "$as_me:$LINENO: checking whether the C++ compiler is recent enough" >&5 echo $ECHO_N "checking whether the C++ compiler is recent enough... $ECHO_C" >&6 if test "${jigdo_cv_prog_cxx_recent+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include namespace A { namespace B { template struct X { template void function(T* t, U* u); }; template<> struct X { void function(void* t, int* u); }; } void B::X::function(void*, int*) { } } int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_prog_cxx_recent="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_prog_cxx_recent="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_prog_cxx_recent" >&5 echo "${ECHO_T}$jigdo_cv_prog_cxx_recent" >&6 if test "$jigdo_cv_prog_cxx_recent" = "no"; then echo "$as_me:$LINENO: result: * Your compiler failed to recognize some advanced C++" >&5 echo "${ECHO_T} * Your compiler failed to recognize some advanced C++" >&6 echo "$as_me:$LINENO: result: * constructs - it might be too old to compile jigdo." >&5 echo "${ECHO_T} * constructs - it might be too old to compile jigdo." >&6 echo "$as_me:$LINENO: result: * In case compilation fails, try upgrading to a newer" >&5 echo "${ECHO_T} * In case compilation fails, try upgrading to a newer" >&6 echo "$as_me:$LINENO: result: * compiler, e.g. GCC 2.95 or later." >&5 echo "${ECHO_T} * compiler, e.g. GCC 2.95 or later." >&6 fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache echo "$as_me:$LINENO: checking whether the C++ preprocessor supports variable-arg macros" >&5 echo $ECHO_N "checking whether the C++ preprocessor supports variable-arg macros... $ECHO_C" >&6 if test "${jigdo_cv_prog_cxx_varmacro+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #if defined(__GNUC__) && __GNUC__ < 3 # define debug(_args...) do { } while (false) #else # define debug(...) do { } while (false) #endif int foo() { int debug = 0; // No substitution! debug("!"); debug("%1", debug); return debug; } int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_prog_cxx_varmacro="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_prog_cxx_varmacro="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_prog_cxx_varmacro" >&5 echo "${ECHO_T}$jigdo_cv_prog_cxx_varmacro" >&6 if test "$jigdo_cv_prog_cxx_varmacro" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_VARMACRO 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_VARMACRO 0 _ACEOF fi # Extract the first word of "wget", so it can be a program name with args. set dummy wget; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_have_wget+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$have_wget"; then ac_cv_prog_have_wget="$have_wget" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_have_wget="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_have_wget" && ac_cv_prog_have_wget="no" fi fi have_wget=$ac_cv_prog_have_wget if test -n "$have_wget"; then echo "$as_me:$LINENO: result: $have_wget" >&5 echo "${ECHO_T}$have_wget" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test "$have_wget" = "no"; then echo "$as_me:$LINENO: result: * wget was not found on your system. Please install" >&5 echo "${ECHO_T} * wget was not found on your system. Please install" >&6 echo "$as_me:$LINENO: result: * it, it is needed by jigdo-lite." >&5 echo "${ECHO_T} * it, it is needed by jigdo-lite." >&6 fi echo "$as_me:$LINENO: checking for value of --enable-debug" >&5 echo $ECHO_N "checking for value of --enable-debug... $ECHO_C" >&6 # Check whether --enable-debug or --disable-debug was given. if test "${enable_debug+set}" = set; then enableval="$enable_debug" jigdo_debug=$enableval else jigdo_debug=no fi; echo "$as_me:$LINENO: result: \"$jigdo_debug\"" >&5 echo "${ECHO_T}\"$jigdo_debug\"" >&6 if test "$jigdo_debug" = "yes"; then IF_DEBUG="" IFNOT_DEBUG="#" cat >>confdefs.h <<\_ACEOF #define DEBUG 1 _ACEOF if test "$ac_cv_prog_cc_g" = "yes"; then CFLAGS="$CFLAGS -g"; fi if test "$ac_cv_prog_cxx_g" = "yes"; then CXXFLAGS="$CXXFLAGS -g"; fi elif test "$jigdo_debug" != "no"; then { { echo "$as_me:$LINENO: error: Invalid argument to --enable-debug option" >&5 echo "$as_me: error: Invalid argument to --enable-debug option" >&2;} { (exit 1); exit 1; }; } else IF_DEBUG="#" IFNOT_DEBUG="" cat >>confdefs.h <<\_ACEOF #define DEBUG 0 _ACEOF fi echo "$as_me:$LINENO: checking for value of --with-pkg-config-prefix" >&5 echo $ECHO_N "checking for value of --with-pkg-config-prefix... $ECHO_C" >&6 # Check whether --with-pkg-config-prefix or --without-pkg-config-prefix was given. if test "${with_pkg_config_prefix+set}" = set; then withval="$with_pkg_config_prefix" jigdo_pkg_config_prefix="$withval" else jigdo_pkg_config_prefix="" fi; if test "$jigdo_pkg_config_prefix" = ""; then echo "$as_me:$LINENO: result: not set" >&5 echo "${ECHO_T}not set" >&6 jigdo_pkg_config_switch="" else echo "$as_me:$LINENO: result: \"$jigdo_pkg_config_prefix\"" >&5 echo "${ECHO_T}\"$jigdo_pkg_config_prefix\"" >&6 if test "$GXX" = "yes"; then CFLAGS="$CFLAGS -I$jigdo_pkg_config_prefix/include -L$jigdo_pkg_config_prefix/lib" CXXFLAGS="$CXXFLAGS -I$jigdo_pkg_config_prefix/include -L$jigdo_pkg_config_prefix/lib" CPPFLAGS="$CPPFLAGS -I$jigdo_pkg_config_prefix/include" LDFLAGS="$LDFLAGS -L$jigdo_pkg_config_prefix/lib" fi export PKG_CONFIG_PATH="$jigdo_pkg_config_prefix/lib/pkgconfig" jigdo_pkg_config_prefix="--define-variable=prefix=$jigdo_pkg_config_prefix" fi echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi for ac_header in stddef.h unistd.h limits.h string.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for zlibVersion in -lz" >&5 echo $ECHO_N "checking for zlibVersion in -lz... $ECHO_C" >&6 if test "${ac_cv_lib_z_zlibVersion+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char zlibVersion (); int main () { zlibVersion (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_z_zlibVersion=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_z_zlibVersion=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_z_zlibVersion" >&5 echo "${ECHO_T}$ac_cv_lib_z_zlibVersion" >&6 if test $ac_cv_lib_z_zlibVersion = yes; then have_zlib="-lz" else have_zlib="no" fi if test "$is_windows" = yes; then echo "$as_me:$LINENO: checking for zlibVersion in -lzdll" >&5 echo $ECHO_N "checking for zlibVersion in -lzdll... $ECHO_C" >&6 if test "${ac_cv_lib_zdll_zlibVersion+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lzdll $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char zlibVersion (); int main () { zlibVersion (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_zdll_zlibVersion=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_zdll_zlibVersion=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_zdll_zlibVersion" >&5 echo "${ECHO_T}$ac_cv_lib_zdll_zlibVersion" >&6 if test $ac_cv_lib_zdll_zlibVersion = yes; then have_zlib="-lzdll" fi fi if test "${ac_cv_header_zlib_h+set}" = set; then echo "$as_me:$LINENO: checking for zlib.h" >&5 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6 if test "${ac_cv_header_zlib_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking zlib.h usability" >&5 echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking zlib.h presence" >&5 echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for zlib.h" >&5 echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6 if test "${ac_cv_header_zlib_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_zlib_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5 echo "${ECHO_T}$ac_cv_header_zlib_h" >&6 fi if test $ac_cv_header_zlib_h = yes; then have_zlib_h="yes" else have_zlib_h="no" fi if test "$have_zlib" = "no" -o "$have_zlib_h" = "no"; then echo "$as_me:$LINENO: result: * Please install zlib, it is needed by all of the" >&5 echo "${ECHO_T} * Please install zlib, it is needed by all of the" >&6 echo "$as_me:$LINENO: result: * programs." >&5 echo "${ECHO_T} * programs." >&6 installDevel "zlib1g" "zlib" fi if test "$have_zlib" != "no"; then LIBS="$have_zlib $LIBS"; fi echo "$as_me:$LINENO: checking for BZ2_bzCompressInit in -lbz2" >&5 echo $ECHO_N "checking for BZ2_bzCompressInit in -lbz2... $ECHO_C" >&6 if test "${ac_cv_lib_bz2_BZ2_bzCompressInit+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char BZ2_bzCompressInit (); int main () { BZ2_bzCompressInit (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_bz2_BZ2_bzCompressInit=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bz2_BZ2_bzCompressInit=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_bz2_BZ2_bzCompressInit" >&5 echo "${ECHO_T}$ac_cv_lib_bz2_BZ2_bzCompressInit" >&6 if test $ac_cv_lib_bz2_BZ2_bzCompressInit = yes; then have_bzlib="-lbz2" else have_bzlib="no" fi if test "$is_windows" = yes; then echo "$as_me:$LINENO: checking for main in -lbz2" >&5 echo $ECHO_N "checking for main in -lbz2... $ECHO_C" >&6 if test "${ac_cv_lib_bz2_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_bz2_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bz2_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_bz2_main" >&5 echo "${ECHO_T}$ac_cv_lib_bz2_main" >&6 if test $ac_cv_lib_bz2_main = yes; then have_bzlib="-lbz2" fi echo "$as_me:$LINENO: checking for main in -lbz2.dll" >&5 echo $ECHO_N "checking for main in -lbz2.dll... $ECHO_C" >&6 if test "${ac_cv_lib_bz2_dll_main+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2.dll $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { main (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_bz2_dll_main=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bz2_dll_main=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_bz2_dll_main" >&5 echo "${ECHO_T}$ac_cv_lib_bz2_dll_main" >&6 if test $ac_cv_lib_bz2_dll_main = yes; then have_bzlib="-lbz2.dll" fi fi if test "${ac_cv_header_bzlib_h+set}" = set; then echo "$as_me:$LINENO: checking for bzlib.h" >&5 echo $ECHO_N "checking for bzlib.h... $ECHO_C" >&6 if test "${ac_cv_header_bzlib_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_bzlib_h" >&5 echo "${ECHO_T}$ac_cv_header_bzlib_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking bzlib.h usability" >&5 echo $ECHO_N "checking bzlib.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking bzlib.h presence" >&5 echo $ECHO_N "checking bzlib.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: bzlib.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: bzlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: bzlib.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: bzlib.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: bzlib.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: bzlib.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: bzlib.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: bzlib.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: bzlib.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: bzlib.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: bzlib.h: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for bzlib.h" >&5 echo $ECHO_N "checking for bzlib.h... $ECHO_C" >&6 if test "${ac_cv_header_bzlib_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_bzlib_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_bzlib_h" >&5 echo "${ECHO_T}$ac_cv_header_bzlib_h" >&6 fi if test $ac_cv_header_bzlib_h = yes; then have_bzlib_h="yes" else have_bzlib_h="no" fi if test "$have_bzlib" = "no" -o "$have_bzlib_h" = "no"; then echo "$as_me:$LINENO: result: * Please install libbz2, it is needed by all of the" >&5 echo "${ECHO_T} * Please install libbz2, it is needed by all of the" >&6 echo "$as_me:$LINENO: result: * programs." >&5 echo "${ECHO_T} * programs." >&6 installDevel "libbz2" "libbz2" fi if test "$have_bzlib" != "no"; then LIBS="$have_bzlib $LIBS"; fi echo "$as_me:$LINENO: checking for value of --with-libdb" >&5 echo $ECHO_N "checking for value of --with-libdb... $ECHO_C" >&6 # Check whether --with-libdb or --without-libdb was given. if test "${with_libdb+set}" = set; then withval="$with_libdb" #' jigdo_libdb="$withval" else jigdo_libdb="yes" fi; echo "$as_me:$LINENO: result: \"$jigdo_libdb\"" >&5 echo "${ECHO_T}\"$jigdo_libdb\"" >&6 if test "$jigdo_libdb" = "yes"; then if test "${ac_cv_header_db_h+set}" = set; then echo "$as_me:$LINENO: checking for db.h" >&5 echo $ECHO_N "checking for db.h... $ECHO_C" >&6 if test "${ac_cv_header_db_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_db_h" >&5 echo "${ECHO_T}$ac_cv_header_db_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking db.h usability" >&5 echo $ECHO_N "checking db.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking db.h presence" >&5 echo $ECHO_N "checking db.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: db.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: db.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: db.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: db.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: db.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: db.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: db.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: db.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: db.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: db.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: db.h: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for db.h" >&5 echo $ECHO_N "checking for db.h... $ECHO_C" >&6 if test "${ac_cv_header_db_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_db_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_db_h" >&5 echo "${ECHO_T}$ac_cv_header_db_h" >&6 fi if test $ac_cv_header_db_h = yes; then have_db_h="yes" else have_db_h="no" fi echo "$as_me:$LINENO: checking for libdb version in db.h" >&5 echo $ECHO_N "checking for libdb version in db.h... $ECHO_C" >&6 printf "#include \nDB_VERSION_MAJOR DB_VERSION_MINOR\n" >conftest.c set x `eval $ac_cpp conftest.c | egrep '^ *[0-9] *'`; v="$2"; vv="$3" echo "$as_me:$LINENO: result: $v.$vv" >&5 echo "${ECHO_T}$v.$vv" >&6 if test "$v" -ne 3 -a "$v" -ne 4; then echo "$as_me:$LINENO: result: * Warning: jigdo is only known to work with libdb" >&5 echo "${ECHO_T} * Warning: jigdo is only known to work with libdb" >&6 echo "$as_me:$LINENO: result: * version 3 or 4 - maybe you should up/downgrade." >&5 echo "${ECHO_T} * version 3 or 4 - maybe you should up/downgrade." >&6 installDevel "libdb4.2" "libdb4" fi for db in "db-$v.$vv" "db$v.$vv" "db-$v" "db$v" "db"; do # Doesn't work: db.h does a "#define db_create db_create_4002" # AC_CHECK_LIB($db, db_create, have_libdb="$db", have_libdb="no") echo "$as_me:$LINENO: checking for db_create in lib$db" >&5 echo $ECHO_N "checking for db_create in lib$db... $ECHO_C" >&6 oldLIBS="$LIBS" LIBS="$LIBS -l$db" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { db_create(0, 0, 0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then have_libdb="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 have_libdb="no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext echo "$as_me:$LINENO: result: $have_libdb" >&5 echo "${ECHO_T}$have_libdb" >&6 if test "$have_libdb" != "no"; then break; fi LIBS="$oldLIBS" done if test "$have_libdb" = "no" -o "$have_db_h" = "no"; then echo "$as_me:$LINENO: result: * libdb not found - either install it, or use" >&5 echo "${ECHO_T} * libdb not found - either install it, or use" >&6 echo "$as_me:$LINENO: result: * \`configure --without-libdb' to disable the db" >&5 echo "${ECHO_T} * \`configure --without-libdb' to disable the db" >&6 echo "$as_me:$LINENO: result: * file cache of jigdo-file. If this check should not" >&5 echo "${ECHO_T} * file cache of jigdo-file. If this check should not" >&6 echo "$as_me:$LINENO: result: * have failed, force e.g. -ldb to be used with" >&5 echo "${ECHO_T} * have failed, force e.g. -ldb to be used with" >&6 echo "$as_me:$LINENO: result: * \`configure --with-libdb=-ldb'" >&5 echo "${ECHO_T} * \`configure --with-libdb=-ldb'" >&6 installDevel "libdb4" "libdb4" { { echo "$as_me:$LINENO: error: libdb not found." >&5 echo "$as_me: error: libdb not found." >&2;} { (exit 1); exit 1; }; } fi cat >>confdefs.h <<\_ACEOF #define HAVE_LIBDB 1 _ACEOF elif test "$jigdo_libdb" != "no" -a "$jigdo_libdb" != "NO"; then LIBS="$jigdo_libdb $LIBS" cat >>confdefs.h <<\_ACEOF #define HAVE_LIBDB 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_LIBDB 0 _ACEOF fi echo "$as_me:$LINENO: checking for value of --with-gui" >&5 echo $ECHO_N "checking for value of --with-gui... $ECHO_C" >&6 # Check whether --with-gui or --without-gui was given. if test "${with_gui+set}" = set; then withval="$with_gui" jigdo_gui="$withval" else jigdo_gui="auto" fi; echo "$as_me:$LINENO: result: \"$jigdo_gui\"" >&5 echo "${ECHO_T}\"$jigdo_gui\"" >&6 if test "$jigdo_gui" = "auto"; then jigdo_gui="yes" jigdo_gui_failed() { echo "$as_me:$LINENO: result: * Disabling the build of the jigdo GUI application." >&5 echo "${ECHO_T} * Disabling the build of the jigdo GUI application." >&6 echo "$as_me:$LINENO: result: * Use --with-gui=yes to force an attempt to build it." >&5 echo "${ECHO_T} * Use --with-gui=yes to force an attempt to build it." >&6 jigdo_gui="no" } else jigdo_gui_failed() { return; } fi if test "$jigdo_gui" = "yes"; then # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_PKGCONFIG+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$PKGCONFIG"; then ac_cv_prog_PKGCONFIG="$PKGCONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PKGCONFIG="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_PKGCONFIG" && ac_cv_prog_PKGCONFIG="no" fi fi PKGCONFIG=$ac_cv_prog_PKGCONFIG if test -n "$PKGCONFIG"; then echo "$as_me:$LINENO: result: $PKGCONFIG" >&5 echo "${ECHO_T}$PKGCONFIG" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test "$PKGCONFIG" = "no"; then echo "$as_me:$LINENO: result: * GTK+ not installed, or pkg-config not in" >&5 echo "${ECHO_T} * GTK+ not installed, or pkg-config not in" >&6 echo "$as_me:$LINENO: result: * \$PATH. Please install GTK+ 2." >&5 echo "${ECHO_T} * \$PATH. Please install GTK+ 2." >&6 installDevel "libgtk2.0" "libgtk2.0" jigdo_gui_failed fi fi GTKCFLAGS="# GUI application disabled by configure script" GTKLIBS="$GTKCFLAGS" GLIBLIBS="$GTKCFLAGS" CURLCFLAGS="$GTKCFLAGS" CURLLIBS="$GTKCFLAGS" if test "$jigdo_gui" = "yes"; then echo "$as_me:$LINENO: checking for GTK+ 2.4.0 or later" >&5 echo $ECHO_N "checking for GTK+ 2.4.0 or later... $ECHO_C" >&6 gtk_ver="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 --modversion`" echo "$as_me:$LINENO: result: $gtk_ver" >&5 echo "${ECHO_T}$gtk_ver" >&6 if pkg-config gtk+-2.0 --atleast-version=2.4.0; then true; else echo "$as_me:$LINENO: result: * Installed version of GTK+ is too old" >&5 echo "${ECHO_T} * Installed version of GTK+ is too old" >&6 jigdo_gui_failed fi if test "$is_windows" = yes; then gth="gthread-2.0"; else gth=""; fi GTKCFLAGS="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 $gth --cflags 2>/dev/null`" GTKLIBS="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 $gth --libs 2>/dev/null`" GLIBLIBS="`pkg-config $jigdo_pkg_config_prefix glib-2.0 $gth --libs 2>/dev/null`" fi echo "$as_me:$LINENO: checking for value of --with-libcurl" >&5 echo $ECHO_N "checking for value of --with-libcurl... $ECHO_C" >&6 # Check whether --with-libcurl or --without-libcurl was given. if test "${with_libcurl+set}" = set; then withval="$with_libcurl" jigdo_libcurl="$withval" else jigdo_libcurl="auto" fi; echo "$as_me:$LINENO: result: \"$jigdo_libcurl\"" >&5 echo "${ECHO_T}\"$jigdo_libcurl\"" >&6 if test "$jigdo_libcurl" = "auto" \ || test "$jigdo_libcurl" = "yes" \ || test "$jigdo_libcurl" = "no"; then jigdo_libcurl="" fi if test "$jigdo_gui" = "yes"; then if test "$is_windows" = "no"; then echo "$as_me:$LINENO: checking for libcurl 7.11.0 or later" >&5 echo $ECHO_N "checking for libcurl 7.11.0 or later... $ECHO_C" >&6 set x `curl-config --version 2>&1` shift case "$1" in [0-9]*) ;; *) shift;; esac jigdo_curlver="$1" echo "$as_me:$LINENO: result: $jigdo_curlver" >&5 echo "${ECHO_T}$jigdo_curlver" >&6 case "$jigdo_curlver" in [1-9][0-9].*|[8-9].*|7.1[1-9]*|7.[2-9][0-9]*) CURLCFLAGS="`curl-config --cflags 2>/dev/null`" CURLLIBS="`curl-config --libs 2>/dev/null`" if test -n "$jigdo_libcurl"; then CURLLIBS="$jigdo_libcurl"; fi ;; *) echo "$as_me:$LINENO: result: * libcurl not installed, or the installed version" >&5 echo "${ECHO_T} * libcurl not installed, or the installed version" >&6 echo "$as_me:$LINENO: result: * is too old, or curl-config is not in \$PATH." >&5 echo "${ECHO_T} * is too old, or curl-config is not in \$PATH." >&6 echo "$as_me:$LINENO: result: * Please install libcurl 7.11.0 or later, it is" >&5 echo "${ECHO_T} * Please install libcurl 7.11.0 or later, it is" >&6 echo "$as_me:$LINENO: result: * needed by the jigdo GUI application." >&5 echo "${ECHO_T} * needed by the jigdo GUI application." >&6 installDevel "libcurl2" "libcurl2" esac else if test -n "$jigdo_libcurl"; then have_libcurl="$jigdo_libcurl" else echo "$as_me:$LINENO: checking for curl_global_init in -lcurl" >&5 echo $ECHO_N "checking for curl_global_init in -lcurl... $ECHO_C" >&6 if test "${ac_cv_lib_curl_curl_global_init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurl -lwinmm -lws2_32 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char curl_global_init (); int main () { curl_global_init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_curl_curl_global_init=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_curl_curl_global_init=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_curl_curl_global_init" >&5 echo "${ECHO_T}$ac_cv_lib_curl_curl_global_init" >&6 if test $ac_cv_lib_curl_curl_global_init = yes; then have_libcurl="-lcurl" else have_libcurl="no" fi echo "$as_me:$LINENO: checking for curl_global_init in -lcurl " >&5 echo $ECHO_N "checking for curl_global_init in -lcurl ... $ECHO_C" >&6 if test "${ac_cv_lib_curl__curl_global_init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurl -lcurl -lssl -lcrypto -lwinmm -lws2_32 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char curl_global_init (); int main () { curl_global_init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_curl__curl_global_init=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_curl__curl_global_init=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_curl__curl_global_init" >&5 echo "${ECHO_T}$ac_cv_lib_curl__curl_global_init" >&6 if test $ac_cv_lib_curl__curl_global_init = yes; then have_libcurl="-lcurl" fi echo "$as_me:$LINENO: checking for curl_global_init in -llibcurl" >&5 echo $ECHO_N "checking for curl_global_init in -llibcurl... $ECHO_C" >&6 if test "${ac_cv_lib_libcurl_curl_global_init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llibcurl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char curl_global_init (); int main () { curl_global_init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_libcurl_curl_global_init=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_libcurl_curl_global_init=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_libcurl_curl_global_init" >&5 echo "${ECHO_T}$ac_cv_lib_libcurl_curl_global_init" >&6 if test $ac_cv_lib_libcurl_curl_global_init = yes; then have_libcurl="-llibcurl" fi echo "$as_me:$LINENO: checking for curl_global_init in -lcurldll" >&5 echo $ECHO_N "checking for curl_global_init in -lcurldll... $ECHO_C" >&6 if test "${ac_cv_lib_curldll_curl_global_init+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurldll $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char curl_global_init (); int main () { curl_global_init (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_curldll_curl_global_init=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_curldll_curl_global_init=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_curldll_curl_global_init" >&5 echo "${ECHO_T}$ac_cv_lib_curldll_curl_global_init" >&6 if test $ac_cv_lib_curldll_curl_global_init = yes; then have_libcurl="-lcurldll" fi fi if test "${ac_cv_header_curl_curl_h+set}" = set; then echo "$as_me:$LINENO: checking for curl/curl.h" >&5 echo $ECHO_N "checking for curl/curl.h... $ECHO_C" >&6 if test "${ac_cv_header_curl_curl_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: $ac_cv_header_curl_curl_h" >&5 echo "${ECHO_T}$ac_cv_header_curl_curl_h" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking curl/curl.h usability" >&5 echo $ECHO_N "checking curl/curl.h usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking curl/curl.h presence" >&5 echo $ECHO_N "checking curl/curl.h presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: curl/curl.h: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: curl/curl.h: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: curl/curl.h: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: curl/curl.h: present but cannot be compiled" >&5 echo "$as_me: WARNING: curl/curl.h: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: curl/curl.h: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: see the Autoconf documentation" >&5 echo "$as_me: WARNING: curl/curl.h: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: curl/curl.h: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: curl/curl.h: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: curl/curl.h: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: curl/curl.h: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for curl/curl.h" >&5 echo $ECHO_N "checking for curl/curl.h... $ECHO_C" >&6 if test "${ac_cv_header_curl_curl_h+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_header_curl_curl_h=$ac_header_preproc fi echo "$as_me:$LINENO: result: $ac_cv_header_curl_curl_h" >&5 echo "${ECHO_T}$ac_cv_header_curl_curl_h" >&6 fi if test $ac_cv_header_curl_curl_h = yes; then have_curl_h="yes" else have_curl_h="no" fi if test "$have_libcurl" = "no" -o "$have_curl_h" = "no"; then echo "$as_me:$LINENO: result: * Please install libcurl 7.11.0 or later, it is" >&5 echo "${ECHO_T} * Please install libcurl 7.11.0 or later, it is" >&6 echo "$as_me:$LINENO: result: * needed by the jigdo GUI application." >&5 echo "${ECHO_T} * needed by the jigdo GUI application." >&6 else CURLCFLAGS="" CURLLIBS="$have_libcurl" fi fi fi if test "$jigdo_gui" = "yes"; then IF_GUI=""; else IF_GUI="#"; fi if test "$jigdo_gui" = "yes" && test ! -e "$srcdir/src/gtk/interface.hh"; then # Extract the first word of "glade-2", so it can be a program name with args. set dummy glade-2; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_have_glade+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$have_glade"; then ac_cv_prog_have_glade="$have_glade" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_have_glade="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_have_glade" && ac_cv_prog_have_glade="no" fi fi have_glade=$ac_cv_prog_have_glade if test -n "$have_glade"; then echo "$as_me:$LINENO: result: $have_glade" >&5 echo "${ECHO_T}$have_glade" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test "$have_glade" = "no"; then echo "$as_me:$LINENO: result: * glade-2 was not found on your system. Please install" >&5 echo "${ECHO_T} * glade-2 was not found on your system. Please install" >&6 echo "$as_me:$LINENO: result: * it, it is needed to compile the jigdo GUI (actually," >&5 echo "${ECHO_T} * it, it is needed to compile the jigdo GUI (actually," >&6 echo "$as_me:$LINENO: result: * to create the file src/gtk/interface.hh)" >&5 echo "${ECHO_T} * to create the file src/gtk/interface.hh)" >&6 fi fi echo "$as_me:$LINENO: checking for value of --with-uint64" >&5 echo $ECHO_N "checking for value of --with-uint64... $ECHO_C" >&6 # Check whether --with-uint64 or --without-uint64 was given. if test "${with_uint64+set}" = set; then withval="$with_uint64" jigdo_uint64="$withval" else jigdo_uint64="auto" fi; echo "$as_me:$LINENO: result: \"$jigdo_uint64\"" >&5 echo "${ECHO_T}\"$jigdo_uint64\"" >&6 echo "$as_me:$LINENO: checking for unsigned long long" >&5 echo $ECHO_N "checking for unsigned long long... $ECHO_C" >&6 if test "${jigdo_cv_have_ulonglong+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { unsigned long long x = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_have_ulonglong=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_have_ulonglong=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_have_ulonglong" >&5 echo "${ECHO_T}$jigdo_cv_have_ulonglong" >&6 if test "$jigdo_cv_have_ulonglong" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_UNSIGNED_LONG_LONG 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_UNSIGNED_LONG_LONG 0 _ACEOF fi if test "$jigdo_uint64" = auto; then echo "$as_me:$LINENO: checking for unsigned long" >&5 echo $ECHO_N "checking for unsigned long... $ECHO_C" >&6 if test "${ac_cv_type_unsigned_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((unsigned long *) 0) return 0; if (sizeof (unsigned long)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_unsigned_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_unsigned_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_long" >&5 echo "${ECHO_T}$ac_cv_type_unsigned_long" >&6 echo "$as_me:$LINENO: checking size of unsigned long" >&5 echo $ECHO_N "checking size of unsigned long... $ECHO_C" >&6 if test "${ac_cv_sizeof_unsigned_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$ac_cv_type_unsigned_long" = yes; then # The cast to unsigned long works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=0 ac_mid=0 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr $ac_mid + 1` if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=-1 ac_mid=-1 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_hi=`expr '(' $ac_mid ')' - 1` if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo= ac_hi= fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr '(' $ac_mid ')' + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in ?*) ac_cv_sizeof_unsigned_long=$ac_lo;; '') { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } ;; esac else if test "$cross_compiling" = yes; then { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default long longval () { return (long) (sizeof (unsigned long)); } unsigned long ulongval () { return (long) (sizeof (unsigned long)); } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) exit (1); if (((long) (sizeof (unsigned long))) < 0) { long i = longval (); if (i != ((long) (sizeof (unsigned long)))) exit (1); fprintf (f, "%ld\n", i); } else { unsigned long i = ulongval (); if (i != ((long) (sizeof (unsigned long)))) exit (1); fprintf (f, "%lu\n", i); } exit (ferror (f) || fclose (f) != 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_unsigned_long=`cat conftest.val` else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val else ac_cv_sizeof_unsigned_long=0 fi fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_unsigned_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_unsigned_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long _ACEOF echo "$as_me:$LINENO: checking for unsigned long long" >&5 echo $ECHO_N "checking for unsigned long long... $ECHO_C" >&6 if test "${ac_cv_type_unsigned_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((unsigned long long *) 0) return 0; if (sizeof (unsigned long long)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_unsigned_long_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_unsigned_long_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_long_long" >&5 echo "${ECHO_T}$ac_cv_type_unsigned_long_long" >&6 echo "$as_me:$LINENO: checking size of unsigned long long" >&5 echo $ECHO_N "checking size of unsigned long long... $ECHO_C" >&6 if test "${ac_cv_sizeof_unsigned_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$ac_cv_type_unsigned_long_long" = yes; then # The cast to unsigned long works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=0 ac_mid=0 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr $ac_mid + 1` if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=-1 ac_mid=-1 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_hi=`expr '(' $ac_mid ')' - 1` if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo= ac_hi= fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr '(' $ac_mid ')' + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in ?*) ac_cv_sizeof_unsigned_long_long=$ac_lo;; '') { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } ;; esac else if test "$cross_compiling" = yes; then { { echo "$as_me:$LINENO: error: internal error: not reached in cross-compile" >&5 echo "$as_me: error: internal error: not reached in cross-compile" >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default long longval () { return (long) (sizeof (unsigned long long)); } unsigned long ulongval () { return (long) (sizeof (unsigned long long)); } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) exit (1); if (((long) (sizeof (unsigned long long))) < 0) { long i = longval (); if (i != ((long) (sizeof (unsigned long long)))) exit (1); fprintf (f, "%ld\n", i); } else { unsigned long i = ulongval (); if (i != ((long) (sizeof (unsigned long long)))) exit (1); fprintf (f, "%lu\n", i); } exit (ferror (f) || fclose (f) != 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_unsigned_long_long=`cat conftest.val` else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val else ac_cv_sizeof_unsigned_long_long=0 fi fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_unsigned_long_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_unsigned_long_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long _ACEOF if test "$ac_cv_sizeof_unsigned_long" -ge 8; then jigdo_uint64='unsigned long' elif test "$ac_cv_sizeof_unsigned_long_long" -ge 8; then jigdo_uint64='unsigned long long' else #if test "$jigdo_cv_have_ulonglong" = "yes"; then jigdo_uint64='unsigned long long' #else # jigdo_uint64='unsigned long' #fi echo "$as_me:$LINENO: result: * Could not find an unsigned type with at least 64" >&5 echo "${ECHO_T} * Could not find an unsigned type with at least 64" >&6 echo "$as_me:$LINENO: result: * bits (or maybe char has >8 bits?) - defaulting to" >&5 echo "${ECHO_T} * bits (or maybe char has >8 bits?) - defaulting to" >&6 echo "$as_me:$LINENO: result: * \`$jigdo_uint64'. You may want to override" >&5 echo "${ECHO_T} * \`$jigdo_uint64'. You may want to override" >&6 echo "$as_me:$LINENO: result: * this using --with-uint64=TYPE" >&5 echo "${ECHO_T} * this using --with-uint64=TYPE" >&6 fi elif test "$jigdo_uint64" = yes -o "$jigdo_uint64" = no; then { { echo "$as_me:$LINENO: error: Invalid argument to --with-uint64 option" >&5 echo "$as_me: error: Invalid argument to --with-uint64 option" >&2;} { (exit 1); exit 1; }; } else jigdo_uint64=`echo $jigdo_uint64 | sed -e 's/_/ /g'` fi echo "$as_me:$LINENO: checking for 64 bit unsigned integer type" >&5 echo $ECHO_N "checking for 64 bit unsigned integer type... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $jigdo_uint64" >&5 echo "${ECHO_T}$jigdo_uint64" >&6 cat >>confdefs.h <<_ACEOF #define TYPE_UINT64 $jigdo_uint64 _ACEOF echo "$as_me:$LINENO: checking for operator<<(uint64)" >&5 echo $ECHO_N "checking for operator<<(uint64)... $ECHO_C" >&6 if test "${jigdo_cv_prog_cxx_outuint64+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include namespace std { } using namespace std; int main () { $jigdo_uint64 x; cout << x << endl; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_prog_cxx_outuint64="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_prog_cxx_outuint64="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_prog_cxx_outuint64" >&5 echo "${ECHO_T}$jigdo_cv_prog_cxx_outuint64" >&6 if test "$jigdo_cv_prog_cxx_outuint64" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_OUTUINT64 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_OUTUINT64 0 _ACEOF fi echo "$as_me:$LINENO: checking for string::compare(size_t,size_t,string,size_t,size_t)" >&5 echo $ECHO_N "checking for string::compare(size_t,size_t,string,size_t,size_t)... $ECHO_C" >&6 if test "${jigdo_cv_prog_cxx_stringcmp+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include namespace std { } using namespace std; int main () { string s, t; s.compare(0, std::string::npos, t, 0, std::string::npos); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_prog_cxx_stringcmp="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_prog_cxx_stringcmp="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_prog_cxx_stringcmp" >&5 echo "${ECHO_T}$jigdo_cv_prog_cxx_stringcmp" >&6 if test "$jigdo_cv_prog_cxx_stringcmp" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_STRINGCMP 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_STRINGCMP 0 _ACEOF fi echo "$as_me:$LINENO: checking for string::compare(size_t,size_t,const char*,size_t)" >&5 echo $ECHO_N "checking for string::compare(size_t,size_t,const char*,size_t)... $ECHO_C" >&6 if test "${jigdo_cv_prog_cxx_stringstrcmp+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include namespace std { } using namespace std; int main () { string s; const char* t = "x"; s.compare(0, std::string::npos, t, 1); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_prog_cxx_stringstrcmp="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_prog_cxx_stringstrcmp="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_prog_cxx_stringstrcmp" >&5 echo "${ECHO_T}$jigdo_cv_prog_cxx_stringstrcmp" >&6 if test "$jigdo_cv_prog_cxx_stringstrcmp" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_STRINGSTRCMP 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_STRINGSTRCMP 0 _ACEOF fi for ac_func in lstat truncate ftruncate mmap memcpy fileno snprintf \ _snprintf setenv do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for TIOCGWINSZ ioctl" >&5 echo $ECHO_N "checking for TIOCGWINSZ ioctl... $ECHO_C" >&6 if test "${jigdo_cv_ioctl_winsz+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { struct winsize w; int i; ioctl(fileno(stdout), TIOCGWINSZ, &w); i = w.ws_col; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_ioctl_winsz="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_ioctl_winsz="no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_ioctl_winsz" >&5 echo "${ECHO_T}$jigdo_cv_ioctl_winsz" >&6 if test "$jigdo_cv_ioctl_winsz" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_IOCTL_WINSZ 1 _ACEOF elif test "$is_windows" = "no"; then echo "$as_me:$LINENO: result: * jigdo-file progress reports will not be formatted" >&5 echo "${ECHO_T} * jigdo-file progress reports will not be formatted" >&6 echo "$as_me:$LINENO: result: * nicely, because no way has been found to determine" >&5 echo "${ECHO_T} * nicely, because no way has been found to determine" >&6 echo "$as_me:$LINENO: result: * the screen width in characters." >&5 echo "${ECHO_T} * the screen width in characters." >&6 cat >>confdefs.h <<\_ACEOF #define HAVE_IOCTL_WINSZ 0 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_IOCTL_WINSZ 0 _ACEOF fi echo "$as_me:$LINENO: checking for getopt_long in " >&5 echo $ECHO_N "checking for getopt_long in ... $ECHO_C" >&6 if test "${jigdo_cv_func_getopt_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { static const struct option longopts[] = { { 0, 0, 0, 0 } }; int c = getopt_long(0, 0, "", longopts, 0); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_func_getopt_long="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_func_getopt_long="no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_func_getopt_long" >&5 echo "${ECHO_T}$jigdo_cv_func_getopt_long" >&6 if test "$jigdo_cv_func_getopt_long" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_GETOPT_LONG 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_GETOPT_LONG 0 _ACEOF fi echo "$as_me:$LINENO: checking for uname in " >&5 echo $ECHO_N "checking for uname in ... $ECHO_C" >&6 if test "${jigdo_cv_func_uname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { struct utsname ubuf; uname(&ubuf); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_func_uname="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_func_uname="no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_func_uname" >&5 echo "${ECHO_T}$jigdo_cv_func_uname" >&6 if test "$jigdo_cv_func_uname" = "yes"; then cat >>confdefs.h <<\_ACEOF #define HAVE_UNAME 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define HAVE_UNAME 0 _ACEOF fi if test "$ac_cv_func_snprintf" = no -a "$ac_cv_func__snprintf" = "yes"; then cat >>confdefs.h <<\_ACEOF #define snprintf _snprintf _ACEOF fi echo "$as_me:$LINENO: checking for _stati64" >&5 echo $ECHO_N "checking for _stati64... $ECHO_C" >&6 if test "${jigdo_cv_func__stati64+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { struct __stati64 buf; int x = _stati64("file", &buf); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then jigdo_cv_func__stati64=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 jigdo_cv_func__stati64=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $jigdo_cv_func__stati64" >&5 echo "${ECHO_T}$jigdo_cv_func__stati64" >&6 if test "$is_windows" = "yes" -a "$ac_cv_func__stati64" = "yes"; then cat >>confdefs.h <<\_ACEOF #define stat _stati64 _ACEOF fi echo "$as_me:$LINENO: checking for value of --enable-nls" >&5 echo $ECHO_N "checking for value of --enable-nls... $ECHO_C" >&6 # Check whether --enable-nls or --disable-nls was given. if test "${enable_nls+set}" = set; then enableval="$enable_nls" USE_NLS=$enableval else USE_NLS=yes fi; echo "$as_me:$LINENO: result: \"$USE_NLS\"" >&5 echo "${ECHO_T}\"$USE_NLS\"" >&6 CATALOGS="" if test "$USE_NLS" = "yes"; then oldLIBS="$LIBS" echo "$as_me:$LINENO: checking for dgettext in and libc" >&5 echo $ECHO_N "checking for dgettext in and libc... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { dgettext(0, 0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then have_intl="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 have_intl="no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext; echo "$as_me:$LINENO: result: $have_intl" >&5 echo "${ECHO_T}$have_intl" >&6 if test "$have_intl" = "no"; then LIBS="$LIBS -lintl"; echo "$as_me:$LINENO: checking for dgettext in and -lintl" >&5 echo $ECHO_N "checking for dgettext in and -lintl... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { dgettext(0, 0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then have_intl="yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 have_intl="no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext; echo "$as_me:$LINENO: result: $have_intl" >&5 echo "${ECHO_T}$have_intl" >&6 fi if test "$have_intl" = "no"; then echo "$as_me:$LINENO: result: * Disabling gettext support - jigdo will not be" >&5 echo "${ECHO_T} * Disabling gettext support - jigdo will not be" >&6 echo "$as_me:$LINENO: result: * able to display translated status/error" >&5 echo "${ECHO_T} * able to display translated status/error" >&6 echo "$as_me:$LINENO: result: * messages instead of the default English ones." >&5 echo "${ECHO_T} * messages instead of the default English ones." >&6 USE_NLS="no" LIBS="$oldLIBS" fi fi if test "$USE_NLS" = "yes"; then cat >>confdefs.h <<\_ACEOF #define ENABLE_NLS 1 _ACEOF echo "$as_me:$LINENO: checking for catalogs to be installed" >&5 echo $ECHO_N "checking for catalogs to be installed... $ECHO_C" >&6 ALL_LINGUAS=`(cd "$srcdir/po" && echo *.po *.pox "") \ | sed -e "s/\**\.pox* / /g"` if test "$LINGUAS"; then NEW_LINGUAS="" for provided in $ALL_LINGUAS; do x="" for wanted in $LINGUAS; do case "$provided" in "$wanted"*) x=" $provided";; esac case "$wanted" in "$provided"*) x=" $provided";; esac done NEW_LINGUAS="$NEW_LINGUAS$x" done LINGUAS="$NEW_LINGUAS" echo "$as_me:$LINENO: result: LINGUAS:$LINGUAS" >&5 echo "${ECHO_T}LINGUAS:$LINGUAS" >&6 else LINGUAS="$ALL_LINGUAS" echo "$as_me:$LINENO: result: $LINGUAS" >&5 echo "${ECHO_T}$LINGUAS" >&6 fi for x in $LINGUAS; do CATALOGS="$CATALOGS $x.gmo"; done else cat >>confdefs.h <<\_ACEOF #define ENABLE_NLS 0 _ACEOF fi if test "$GXX" = "yes"; then CFLAGS="-Wall $CFLAGS -W" CXXFLAGS="$CXXFLAGS -Wall -W -Wpointer-arith -Wconversion -Woverloaded-virtual" fi if test "$cross_compiling" = yes; then IF_CROSSCOMPILING="" IFNOT_CROSSCOMPILING="#" else IF_CROSSCOMPILING="#" IFNOT_CROSSCOMPILING="" fi touch "$srcdir/src/TAGS" SUBDIRS="glibcurl gtk job net util" SRC_MAKEDEPS="$srcdir/src/Makedeps" if test -e "$SRC_MAKEDEPS"; then true; else echo "$as_me:$LINENO: result: Creating $SRC_MAKEDEPS" >&5 echo "${ECHO_T}Creating $SRC_MAKEDEPS" >&6 touch "$SRC_MAKEDEPS" echo >"$srcdir/src/gtk/interface.hh" (cd "$srcdir/src" find . -type f '(' -name '*.cc' -o -name '*.c' ')' \ | xargs "$ac_cv_prog_AWK" -f ../scripts/depend.awk "$srcdir" $SUBDIRS - ) rm -f "$SRC_MAKEDEPS.bak" "$srcdir/src/gtk/interface.hh" fi for dir in "" $SUBDIRS; do test ! -d "src/$dir" && mkdir "src/$dir" done ac_config_files="$ac_config_files Makefile doc/Makefile src/Makefile po/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2003 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir INSTALL="$INSTALL" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "po/Makefile" ) CONFIG_FILES="$CONFIG_FILES po/Makefile" ;; "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@JIGDO_VERSION@,$JIGDO_VERSION,;t t s,@AWK@,$AWK,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@CXX@,$CXX,;t t s,@CXXFLAGS@,$CXXFLAGS,;t t s,@ac_ct_CXX@,$ac_ct_CXX,;t t s,@IF_GXX2@,$IF_GXX2,;t t s,@IFNOT_GXX2@,$IFNOT_GXX2,;t t s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@CXXCPP@,$CXXCPP,;t t s,@EGREP@,$EGREP,;t t s,@exe@,$exe,;t t s,@IF_WINDOWS@,$IF_WINDOWS,;t t s,@IF_UNIX@,$IF_UNIX,;t t s,@have_wget@,$have_wget,;t t s,@IF_DEBUG@,$IF_DEBUG,;t t s,@IFNOT_DEBUG@,$IFNOT_DEBUG,;t t s,@PKGCONFIG@,$PKGCONFIG,;t t s,@IF_GUI@,$IF_GUI,;t t s,@GTKCFLAGS@,$GTKCFLAGS,;t t s,@GTKLIBS@,$GTKLIBS,;t t s,@GLIBLIBS@,$GLIBLIBS,;t t s,@CURLCFLAGS@,$CURLCFLAGS,;t t s,@CURLLIBS@,$CURLLIBS,;t t s,@have_glade@,$have_glade,;t t s,@USE_NLS@,$USE_NLS,;t t s,@CATALOGS@,$CATALOGS,;t t s,@IF_CROSSCOMPILING@,$IF_CROSSCOMPILING,;t t s,@IFNOT_CROSSCOMPILING@,$IFNOT_CROSSCOMPILING,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t /@SRC_MAKEDEPS@/r $SRC_MAKEDEPS s,@SRC_MAKEDEPS@,,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t s,@INSTALL@,$ac_INSTALL,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_HEADER section. # # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='[ ].*$,\1#\2' ac_dC=' ' ac_dD=',;t' # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='$,\1#\2define\3' ac_uC=' ' ac_uD=',;t' for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } # Do quote $f, to prevent DOS paths from being IFS'd. echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } # Remove the trailing spaces. sed 's/[ ]*$//' $ac_file_inputs >$tmp/in _ACEOF # Transform confdefs.h into two sed scripts, `conftest.defines' and # `conftest.undefs', that substitutes the proper values into # config.h.in to produce config.h. The first handles `#define' # templates, and the second `#undef' templates. # And first: Protect against being on the right side of a sed subst in # config.status. Protect against being in an unquoted here document # in config.status. rm -f conftest.defines conftest.undefs # Using a here document instead of a string reduces the quoting nightmare. # Putting comments in sed scripts is not portable. # # `end' is used to avoid that the second main sed command (meant for # 0-ary CPP macros) applies to n-ary macro definitions. # See the Autoconf documentation for `clear'. cat >confdef2sed.sed <<\_ACEOF s/[\\&,]/\\&/g s,[\\$`],\\&,g t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp t end s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp : end _ACEOF # If some macros were called several times there might be several times # the same #defines, which is useless. Nevertheless, we may not want to # sort them, since we want the *last* AC-DEFINE to be honored. uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs rm -f confdef2sed.sed # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >>conftest.undefs <<\_ACEOF s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, _ACEOF # Break up conftest.defines because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS echo ' :' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.defines >/dev/null do # Write a limited-size here document to $tmp/defines.sed. echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#define' lines. echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/defines.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines echo ' fi # grep' >>$CONFIG_STATUS echo >>$CONFIG_STATUS # Break up conftest.undefs because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #undef templates' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.undefs >/dev/null do # Write a limited-size here document to $tmp/undefs.sed. echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#undef' echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/undefs.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail rm -f conftest.undefs mv conftest.tail conftest.undefs done rm -f conftest.undefs cat >>$CONFIG_STATUS <<\_ACEOF # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then echo "/* Generated by configure. */" >$tmp/config.h else echo "/* $ac_file. Generated by configure. */" >$tmp/config.h fi cat $tmp/in >>$tmp/config.h rm -f $tmp/in if test x"$ac_file" != x-; then if diff $ac_file $tmp/config.h >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } rm -f $ac_file mv $tmp/config.h $ac_file fi else cat $tmp/config.h rm -f $tmp/config.h fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi jigdo-0.7.3/configure.ac0000644000175000017500000006706010433366324014732 0ustar richardricharddnl Process this file with autoconf to produce a configure script. dnl $Id: configure.ac,v 1.25 2006/05/19 16:05:40 atterer Exp $ AC_INIT(src/jigdo-file.cc) AC_CONFIG_HEADER(src/config.h) dnl ______________________________________________________________________ dnl Version number of our program. read tmpa tmpb JIGDO_VERSION < "$srcdir/jigdo.spec" AC_DEFINE_UNQUOTED(JIGDO_VERSION, "$JIGDO_VERSION") AC_SUBST(JIGDO_VERSION) dnl ______________________________________________________________________ dnl installDevel is given 2 arguments: Name of "devel" package on dnl Debian-based systems, name on RPM-based systems if test -f "/etc/debian_version"; then installDevel() { AC_MSG_RESULT([ * (Your system appears to be Debian-based; try]) AC_MSG_RESULT([ * installing the \`$1-dev' package.)]) } elif test -x "/usr/bin/rpm" -o -x "/usr/local/bin/rpm"; then installDevel() { AC_MSG_RESULT([ * (Your system appears to be RPM-based; try]) AC_MSG_RESULT([ * installing the package named \`$2-devel' or]) AC_MSG_RESULT([ * similar.)]) } else installDevel() { AC_MSG_RESULT([ * (If the software on your system is managed by a]) AC_MSG_RESULT([ * package manager like RPM, try installing the]) AC_MSG_RESULT([ * package named \`$2-devel' or similar.)]) } fi dnl ______________________________________________________________________ dnl Checks for programs. AC_PROG_AWK AC_LANG_C AC_PROG_CC dnl Add -Wno-unused-parameter to CFLAGS if supported dnl ORIG_CFLAGS="$CFLAGS" dnl CFLAGS="$CFLAGS -Wno-unused-parameter" # GCC >=3.0 dnl AC_TRY_COMPILE(, int x = 0, , CFLAGS="$ORIG_CFLAGS") AC_LANG_CPLUSPLUS AC_PROG_CXX CFLAGS=`echo "$CFLAGS" | sed 's/\(^\| \)-g\( \|$\)/ /'` CXXFLAGS=`echo "$CXXFLAGS" | sed 's/\(^\| \)-g\( \|$\)/ /'` IF_GXX2="#" IFNOT_GXX2="" if test "$GXX" = yes; then AC_MSG_CHECKING(for GCC version) gccver=`$CXX -dumpversion` AC_MSG_RESULT($gccver); case "$gccver" in 2.*) IF_GXX2="" IFNOT_GXX2="#";; 3.0|3.0.*|3.1|3.1.*|3.3|3.3.*) #AC_MSG_RESULT([ * Big file support may be broken in GCC 3.2 and]) #AC_MSG_RESULT([ * 3.3! If you compile jigdo on Linux (and maybe]) #AC_MSG_RESULT([ * other OSes), it might not be able to create]) #AC_MSG_RESULT([ * files bigger than 2 GB, such as DVD images.]) #AC_MSG_RESULT([ * (Try GCC 2.95 or GCC 3.4 or later)]) esac fi AC_SUBST(IF_GXX2) AC_SUBST(IFNOT_GXX2) AC_PROG_INSTALL dnl ____________________ dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_C_BIGENDIAN AC_MSG_CHECKING(whether we are compiling for Windows) AC_EGREP_CPP(poorsoul, [ #if defined WINNT || defined __WINNT || defined __WINNT__ || defined WIN32 \ || defined _WIN32 || defined __WIN32 || defined __WIN32__ poorsoul #endif], is_windows=yes, is_windows=no) AC_MSG_RESULT($is_windows) if test "$is_windows" = yes; then AC_DEFINE(WINDOWS) exe=".exe" IF_WINDOWS="" IF_UNIX="#" else AC_DEFINE(UNIX) exe="" IF_WINDOWS="#" IF_UNIX="" fi AC_SUBST(exe) AC_SUBST(IF_WINDOWS) AC_SUBST(IF_UNIX) if test "$is_windows" = "yes"; then if test "$ac_cv_prog_cc_g" = "yes"; then CFLAGS="$CFLAGS -mms-bitfields -march=pentium" fi if test "$ac_cv_prog_cxx_g" = "yes"; then CXXFLAGS="$CXXFLAGS -mms-bitfields -march=pentium" fi fi dnl ____________________ AC_CACHE_CHECK(whether the C++ compiler is recent enough, jigdo_cv_prog_cxx_recent, AC_TRY_COMPILE( #include namespace A { namespace B { template struct X { template void function(T* t, U* u); }; template<> struct X { void function(void* t, int* u); }; } void B::X::function(void*, int*) { } },, jigdo_cv_prog_cxx_recent="yes", jigdo_cv_prog_cxx_recent="no") ) if test "$jigdo_cv_prog_cxx_recent" = "no"; then AC_MSG_RESULT([ * Your compiler failed to recognize some advanced C++]) AC_MSG_RESULT([ * constructs - it might be too old to compile jigdo.]) AC_MSG_RESULT([ * In case compilation fails, try upgrading to a newer]) AC_MSG_RESULT([ * compiler, e.g. GCC 2.95 or later.]) fi AC_CACHE_SAVE dnl ________________________________________ dnl C99-style vararg macros are not supported by GCC 2.95 AC_CACHE_CHECK([whether the C++ preprocessor supports variable-arg macros], jigdo_cv_prog_cxx_varmacro, AC_TRY_COMPILE( #if defined(__GNUC__) && __GNUC__ < 3 # define debug(_args...) do { } while (false) #else # define debug(...) do { } while (false) #endif int foo() { int debug = 0; // No substitution! debug("!"); debug("%1", debug); return debug; },, jigdo_cv_prog_cxx_varmacro="yes", jigdo_cv_prog_cxx_varmacro="no") ) if test "$jigdo_cv_prog_cxx_varmacro" = "yes"; then AC_DEFINE(HAVE_VARMACRO, 1) else AC_DEFINE(HAVE_VARMACRO, 0) fi dnl ________________________________________ dnl Doesn't work dnl AC_CACHE_CHECK([whether C++ supports accessing files larger than 4 GB], dnl jigdo_cv_prog_cxx_lfs, dnl AC_TRY_COMPILE([#include ], dnl [using namespace std; ifstream i; i.seekg(0x100000000, ios::beg);], dnl jigdo_cv_prog_cxx_lfs="yes", jigdo_cv_prog_cxx_lfs="no") dnl ) dnl if test "$jigdo_cv_prog_cxx_lfs" = "no"; then dnl AC_MSG_RESULT([ * There is a problem accessing large files.]) dnl AC_MSG_RESULT([ * jigdo may be unable to work with DVD-sized images.]) dnl fi dnl ________________________________________ AC_CHECK_PROG(have_wget, wget, yes, no) if test "$have_wget" = "no"; then AC_MSG_RESULT([ * wget was not found on your system. Please install]) AC_MSG_RESULT([ * it, it is needed by jigdo-lite.]) fi dnl ________________________________________ AC_MSG_CHECKING(for value of --enable-debug) AC_ARG_ENABLE(debug, [ --enable-debug Compile in lots of additional debugging code], jigdo_debug=$enableval, jigdo_debug=no) AC_MSG_RESULT(\"$jigdo_debug\") if test "$jigdo_debug" = "yes"; then IF_DEBUG="" IFNOT_DEBUG="#" AC_DEFINE(DEBUG, 1) if test "$ac_cv_prog_cc_g" = "yes"; then CFLAGS="$CFLAGS -g"; fi if test "$ac_cv_prog_cxx_g" = "yes"; then CXXFLAGS="$CXXFLAGS -g"; fi elif test "$jigdo_debug" != "no"; then AC_MSG_ERROR(Invalid argument to --enable-debug option) else IF_DEBUG="#" IFNOT_DEBUG="" AC_DEFINE(DEBUG, 0) fi AC_SUBST(IF_DEBUG) AC_SUBST(IFNOT_DEBUG) AC_MSG_CHECKING(for value of --with-pkg-config-prefix) AC_ARG_WITH(pkg-config-prefix, [ --with-pkg-config-prefix=PATH When cross-compiling, specify prefix of libraries/headers for target architecture [none]], jigdo_pkg_config_prefix="$withval", jigdo_pkg_config_prefix="") if test "$jigdo_pkg_config_prefix" = ""; then AC_MSG_RESULT(not set) jigdo_pkg_config_switch="" else AC_MSG_RESULT(\"$jigdo_pkg_config_prefix\") if test "$GXX" = "yes"; then CFLAGS="$CFLAGS -I$jigdo_pkg_config_prefix/include -L$jigdo_pkg_config_prefix/lib" CXXFLAGS="$CXXFLAGS -I$jigdo_pkg_config_prefix/include -L$jigdo_pkg_config_prefix/lib" CPPFLAGS="$CPPFLAGS -I$jigdo_pkg_config_prefix/include" LDFLAGS="$LDFLAGS -L$jigdo_pkg_config_prefix/lib" fi export PKG_CONFIG_PATH="$jigdo_pkg_config_prefix/lib/pkgconfig" jigdo_pkg_config_prefix="--define-variable=prefix=$jigdo_pkg_config_prefix" fi dnl dnl cppunit dnl CPPUNITCFLAGS='# `cppunit-config --cflags`' dnl CPPUNITLIBS='# `cppunit-config --libs`' dnl IF_CPPUNIT="#" dnl AC_CHECK_PROG(have_cppunit_config, cppunit-config, yes, no) dnl if test "$have_cppunit_config" = "yes"; then dnl CPPUNITCFLAGS="`cppunit-config --cflags`" dnl CPPUNITLIBS="`cppunit-config --libs`" dnl ORIG_LIBS="$LIBS" dnl LIBS="$LIBS $CPPUNITCFLAGS $CPPUNITLIBS" dnl AC_CACHE_CHECK([whether cppunit works], dnl jigdo_cv_prog_cppunit, dnl AC_TRY_LINK( dnl [ #include dnl #include dnl #include dnl using namespace CppUnit; ], dnl [ TextUi::TestRunner runner; dnl TestFactoryRegistry& registry = dnl TestFactoryRegistry::getRegistry(); dnl runner.addTest(registry.makeTest()); dnl runner.run("", false); ], dnl jigdo_cv_prog_cppunit="yes", jigdo_cv_prog_cppunit="no" dnl ) dnl ) dnl LIBS="$ORIG_LIBS" dnl if test "$jigdo_cv_prog_cppunit" = "yes"; then dnl IF_CPPUNIT="" dnl else dnl AC_MSG_RESULT([ * cppunit does not work - disabled.]) dnl fi dnl elif test "$jigdo_debug" = "yes"; then dnl AC_MSG_RESULT([ * cppunit was not found on your system. Please]) dnl AC_MSG_RESULT([ * install it if you want to run the unit tests.]) dnl fi dnl AC_SUBST(IF_CPPUNIT) dnl AC_SUBST(CPPUNITCFLAGS) dnl AC_SUBST(CPPUNITLIBS) dnl ______________________________________________________________________ dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(stddef.h unistd.h limits.h string.h) dnl Checks for libraries and accompanying header files AC_CHECK_LIB(z, zlibVersion, have_zlib="-lz", have_zlib="no") if test "$is_windows" = yes; then AC_CHECK_LIB(zdll, zlibVersion, have_zlib="-lzdll", ) fi AC_CHECK_HEADER(zlib.h, have_zlib_h="yes", have_zlib_h="no") if test "$have_zlib" = "no" -o "$have_zlib_h" = "no"; then AC_MSG_RESULT([ * Please install zlib, it is needed by all of the]) AC_MSG_RESULT([ * programs.]) installDevel "zlib1g" "zlib" fi if test "$have_zlib" != "no"; then LIBS="$have_zlib $LIBS"; fi AC_CHECK_LIB(bz2, BZ2_bzCompressInit, have_bzlib="-lbz2", have_bzlib="no") if test "$is_windows" = yes; then dnl For some reason, can't check for BZ2_bzCompressInit without #include AC_CHECK_LIB(bz2, main, have_bzlib="-lbz2", ) AC_CHECK_LIB(bz2.dll, main, have_bzlib="-lbz2.dll", ) fi AC_CHECK_HEADER(bzlib.h, have_bzlib_h="yes", have_bzlib_h="no") if test "$have_bzlib" = "no" -o "$have_bzlib_h" = "no"; then AC_MSG_RESULT([ * Please install libbz2, it is needed by all of the]) AC_MSG_RESULT([ * programs.]) installDevel "libbz2" "libbz2" fi if test "$have_bzlib" != "no"; then LIBS="$have_bzlib $LIBS"; fi AC_MSG_CHECKING(for value of --with-libdb) AC_ARG_WITH(libdb, [ --without-libdb Don't use libdb (it's necessary for jigdo-file's cache)], #' jigdo_libdb="$withval", jigdo_libdb="yes") AC_MSG_RESULT(\"$jigdo_libdb\") if test "$jigdo_libdb" = "yes"; then AC_CHECK_HEADER(db.h, have_db_h="yes", have_db_h="no") AC_MSG_CHECKING(for libdb version in db.h) printf "#include \nDB_VERSION_MAJOR DB_VERSION_MINOR\n" >conftest.c set x `eval $ac_cpp conftest.c | egrep '^ *[[0-9]] *'`; v="$2"; vv="$3" AC_MSG_RESULT($v.$vv) if test "$v" -ne 3 -a "$v" -ne 4; then AC_MSG_RESULT([ * Warning: jigdo is only known to work with libdb]) AC_MSG_RESULT([ * version 3 or 4 - maybe you should up/downgrade.]) installDevel "libdb4.2" "libdb4" fi for db in "db-$v.$vv" "db$v.$vv" "db-$v" "db$v" "db"; do # Doesn't work: db.h does a "#define db_create db_create_4002" # AC_CHECK_LIB($db, db_create, have_libdb="$db", have_libdb="no") AC_MSG_CHECKING(for db_create in lib$db) oldLIBS="$LIBS" LIBS="$LIBS -l$db" AC_TRY_LINK([#include ], db_create(0, 0, 0), have_libdb="yes", have_libdb="no") AC_MSG_RESULT($have_libdb) if test "$have_libdb" != "no"; then break; fi LIBS="$oldLIBS" done if test "$have_libdb" = "no" -o "$have_db_h" = "no"; then AC_MSG_RESULT([ * libdb not found - either install it, or use]) AC_MSG_RESULT([ * \`configure --without-libdb' to disable the db]) AC_MSG_RESULT([ * file cache of jigdo-file. If this check should not]) AC_MSG_RESULT([ * have failed, force e.g. -ldb to be used with]) AC_MSG_RESULT([ * \`configure --with-libdb=-ldb']) installDevel "libdb4" "libdb4" AC_MSG_ERROR(libdb not found.) fi AC_DEFINE(HAVE_LIBDB, 1) dnl make compilation fail if libdb not there elif test "$jigdo_libdb" != "no" -a "$jigdo_libdb" != "NO"; then dnl Pass argument (e.g. "-ldb") to compiler LIBS="$jigdo_libdb $LIBS" AC_DEFINE(HAVE_LIBDB, 1) else AC_DEFINE(HAVE_LIBDB, 0) fi dnl ________________________________________ AC_MSG_CHECKING(for value of --with-gui) AC_ARG_WITH(gui, [ --with-gui Build the jigdo GTK+ GUI application [auto]], jigdo_gui="$withval", jigdo_gui="auto") AC_MSG_RESULT(\"$jigdo_gui\") dnl ____________________ if test "$jigdo_gui" = "auto"; then jigdo_gui="yes" jigdo_gui_failed() { AC_MSG_RESULT([ * Disabling the build of the jigdo GUI application.]) AC_MSG_RESULT([ * Use --with-gui=yes to force an attempt to build it.]) jigdo_gui="no" } else jigdo_gui_failed() { return; } fi if test "$jigdo_gui" = "yes"; then AC_CHECK_PROG(PKGCONFIG, pkg-config, yes, no) if test "$PKGCONFIG" = "no"; then AC_MSG_RESULT([ * GTK+ not installed, or pkg-config not in]) AC_MSG_RESULT([ * \$PATH. Please install GTK+ 2.]) installDevel "libgtk2.0" "libgtk2.0" jigdo_gui_failed fi fi GTKCFLAGS="# GUI application disabled by configure script" GTKLIBS="$GTKCFLAGS" GLIBLIBS="$GTKCFLAGS" dnl LIBWWWCFLAGS="$GTKCFLAGS" dnl LIBWWWLIBS="$GTKCFLAGS" CURLCFLAGS="$GTKCFLAGS" CURLLIBS="$GTKCFLAGS" dnl The following adds flags for GTK+ and glib if test "$jigdo_gui" = "yes"; then AC_MSG_CHECKING(for GTK+ 2.4.0 or later) gtk_ver="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 --modversion`" AC_MSG_RESULT($gtk_ver) if pkg-config gtk+-2.0 --atleast-version=2.4.0; then true; else AC_MSG_RESULT([ * Installed version of GTK+ is too old]) jigdo_gui_failed fi if test "$is_windows" = yes; then gth="gthread-2.0"; else gth=""; fi dnl gth="gthread-2.0" GTKCFLAGS="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 $gth --cflags 2>/dev/null`" GTKLIBS="`pkg-config $jigdo_pkg_config_prefix gtk+-2.0 $gth --libs 2>/dev/null`" GLIBLIBS="`pkg-config $jigdo_pkg_config_prefix glib-2.0 $gth --libs 2>/dev/null`" fi dnl ____________________ AC_MSG_CHECKING(for value of --with-libcurl) AC_ARG_WITH(libcurl, [ --with-libcurl=-lcurl Use given -l switch instead of trying to determine the right one], jigdo_libcurl="$withval", jigdo_libcurl="auto") AC_MSG_RESULT(\"$jigdo_libcurl\") if test "$jigdo_libcurl" = "auto" \ || test "$jigdo_libcurl" = "yes" \ || test "$jigdo_libcurl" = "no"; then jigdo_libcurl="" fi dnl The following adds flags for libcurl if test "$jigdo_gui" = "yes"; then if test "$is_windows" = "no"; then AC_MSG_CHECKING(for libcurl 7.11.0 or later) set x `curl-config --version 2>&1` shift case "$1" in [[0-9]*]) ;; *) shift;; esac jigdo_curlver="$1" AC_MSG_RESULT($jigdo_curlver) case "$jigdo_curlver" in [[1-9][0-9].*|[8-9].*|7.1[1-9]*|7.[2-9][0-9]*]) CURLCFLAGS="`curl-config --cflags 2>/dev/null`" CURLLIBS="`curl-config --libs 2>/dev/null`" if test -n "$jigdo_libcurl"; then CURLLIBS="$jigdo_libcurl"; fi ;; *) AC_MSG_RESULT([ * libcurl not installed, or the installed version]) AC_MSG_RESULT([ * is too old, or curl-config is not in \$PATH.]) AC_MSG_RESULT([ * Please install libcurl 7.11.0 or later, it is]) AC_MSG_RESULT([ * needed by the jigdo GUI application.]) installDevel "libcurl2" "libcurl2" esac else dnl On Windows, no curl-config is supplied if test -n "$jigdo_libcurl"; then have_libcurl="$jigdo_libcurl" else dnl Just -lcurl will attempt static linking. You will need to specify dnl "make X=-DCURL_STATICLIB" for this to work dnl Will try SSL/non-SSL curl AC_CHECK_LIB(curl, curl_global_init, have_libcurl="-lcurl", have_libcurl="no", -lwinmm -lws2_32) AC_CHECK_LIB(curl , curl_global_init, have_libcurl="-lcurl", , -lcurl -lssl -lcrypto -lwinmm -lws2_32) dnl This will pick up DLLs to link against AC_CHECK_LIB(libcurl, curl_global_init, have_libcurl="-llibcurl",) AC_CHECK_LIB(curldll, curl_global_init, have_libcurl="-lcurldll",) fi AC_CHECK_HEADER(curl/curl.h, have_curl_h="yes", have_curl_h="no") if test "$have_libcurl" = "no" -o "$have_curl_h" = "no"; then AC_MSG_RESULT([ * Please install libcurl 7.11.0 or later, it is]) AC_MSG_RESULT([ * needed by the jigdo GUI application.]) else CURLCFLAGS="" CURLLIBS="$have_libcurl" fi fi fi if test "$jigdo_gui" = "yes"; then IF_GUI=""; else IF_GUI="#"; fi AC_SUBST(IF_GUI) AC_SUBST(GTKCFLAGS) AC_SUBST(GTKLIBS) AC_SUBST(GLIBLIBS) dnl AC_SUBST(LIBWWWCFLAGS) dnl AC_SUBST(LIBWWWLIBS) AC_SUBST(CURLCFLAGS) AC_SUBST(CURLLIBS) dnl ____________________ if test "$jigdo_gui" = "yes" && test ! -e "$srcdir/src/gtk/interface.hh"; then AC_CHECK_PROG(have_glade, glade-2, yes, no) if test "$have_glade" = "no"; then AC_MSG_RESULT([ * glade-2 was not found on your system. Please install]) AC_MSG_RESULT([ * it, it is needed to compile the jigdo GUI (actually,]) AC_MSG_RESULT([ * to create the file src/gtk/interface.hh)]) fi fi dnl ____________________ AC_MSG_CHECKING(for value of --with-uint64) AC_ARG_WITH(uint64, [ --with-uint64=TYPE Specify unsigned type of at least 64 bits [auto] (Replace spaces with underscores in TYPE)], jigdo_uint64="$withval", jigdo_uint64="auto") AC_MSG_RESULT(\"$jigdo_uint64\") AC_CACHE_CHECK(for unsigned long long, jigdo_cv_have_ulonglong, AC_TRY_COMPILE(, unsigned long long x = 0, jigdo_cv_have_ulonglong=yes, jigdo_cv_have_ulonglong=no)) if test "$jigdo_cv_have_ulonglong" = "yes"; then AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1) else AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 0) fi if test "$jigdo_uint64" = auto; then AC_CHECK_SIZEOF(unsigned long, 0) AC_CHECK_SIZEOF(unsigned long long, 0) if test "$ac_cv_sizeof_unsigned_long" -ge 8; then jigdo_uint64='unsigned long' elif test "$ac_cv_sizeof_unsigned_long_long" -ge 8; then jigdo_uint64='unsigned long long' else #if test "$jigdo_cv_have_ulonglong" = "yes"; then jigdo_uint64='unsigned long long' #else # jigdo_uint64='unsigned long' #fi AC_MSG_RESULT([ * Could not find an unsigned type with at least 64]) AC_MSG_RESULT([ * bits (or maybe char has >8 bits?) - defaulting to]) AC_MSG_RESULT([ * \`$jigdo_uint64'. You may want to override]) AC_MSG_RESULT([ * this using --with-uint64=TYPE]) fi elif test "$jigdo_uint64" = yes -o "$jigdo_uint64" = no; then AC_MSG_ERROR(Invalid argument to --with-uint64 option) else jigdo_uint64=`echo $jigdo_uint64 | sed -e 's/_/ /g'` fi AC_MSG_CHECKING(for 64 bit unsigned integer type) AC_MSG_RESULT($jigdo_uint64) AC_DEFINE_UNQUOTED(TYPE_UINT64, $jigdo_uint64) AC_CACHE_CHECK(for operator<<(uint64), jigdo_cv_prog_cxx_outuint64, AC_TRY_COMPILE( [ #include namespace std { } using namespace std; ], [ $jigdo_uint64 x; cout << x << endl; ], jigdo_cv_prog_cxx_outuint64="yes", jigdo_cv_prog_cxx_outuint64="no") ) if test "$jigdo_cv_prog_cxx_outuint64" = "yes"; then AC_DEFINE(HAVE_OUTUINT64, 1) else AC_DEFINE(HAVE_OUTUINT64, 0) fi AC_CACHE_CHECK([for string::compare(size_t,size_t,string,size_t,size_t)], jigdo_cv_prog_cxx_stringcmp, AC_TRY_COMPILE( [ #include namespace std { } using namespace std; ], [ string s, t; s.compare(0, std::string::npos, t, 0, std::string::npos); ], jigdo_cv_prog_cxx_stringcmp="yes", jigdo_cv_prog_cxx_stringcmp="no") ) if test "$jigdo_cv_prog_cxx_stringcmp" = "yes"; then AC_DEFINE(HAVE_STRINGCMP, 1) else AC_DEFINE(HAVE_STRINGCMP, 0) fi AC_CACHE_CHECK([for string::compare(size_t,size_t,const char*,size_t)], jigdo_cv_prog_cxx_stringstrcmp, AC_TRY_COMPILE( [ #include namespace std { } using namespace std; ], [ string s; const char* t = "x"; s.compare(0, std::string::npos, t, 1); ], jigdo_cv_prog_cxx_stringstrcmp="yes", jigdo_cv_prog_cxx_stringstrcmp="no") ) if test "$jigdo_cv_prog_cxx_stringstrcmp" = "yes"; then AC_DEFINE(HAVE_STRINGSTRCMP, 1) else AC_DEFINE(HAVE_STRINGSTRCMP, 0) fi dnl ____________________ dnl Checks for library functions. AC_CHECK_FUNCS(lstat truncate ftruncate mmap memcpy fileno snprintf \ _snprintf setenv) dnl Check whether reading width of TTY via ioctl() works AC_CACHE_CHECK([for TIOCGWINSZ ioctl], jigdo_cv_ioctl_winsz, AC_TRY_COMPILE( [ #include #include #include #include ], [ struct winsize w; int i; ioctl(fileno(stdout), TIOCGWINSZ, &w); i = w.ws_col; ], jigdo_cv_ioctl_winsz="yes", jigdo_cv_ioctl_winsz="no") ) if test "$jigdo_cv_ioctl_winsz" = "yes"; then AC_DEFINE(HAVE_IOCTL_WINSZ, 1) elif test "$is_windows" = "no"; then AC_MSG_RESULT([ * jigdo-file progress reports will not be formatted]) AC_MSG_RESULT([ * nicely, because no way has been found to determine]) AC_MSG_RESULT([ * the screen width in characters.]) AC_DEFINE(HAVE_IOCTL_WINSZ, 0) else AC_DEFINE(HAVE_IOCTL_WINSZ, 0) fi dnl Check whether getopt_long is present AC_CACHE_CHECK([for getopt_long in ], jigdo_cv_func_getopt_long, AC_TRY_LINK( [ #include ], [[ static const struct option longopts[] = { { 0, 0, 0, 0 } }; int c = getopt_long(0, 0, "", longopts, 0); ]], jigdo_cv_func_getopt_long="yes", jigdo_cv_func_getopt_long="no" ) ) if test "$jigdo_cv_func_getopt_long" = "yes"; then AC_DEFINE(HAVE_GETOPT_LONG, 1) else AC_DEFINE(HAVE_GETOPT_LONG, 0) fi dnl Check whether uname() is present AC_CACHE_CHECK([for uname in ], jigdo_cv_func_uname, AC_TRY_LINK( [ #include ], [ struct utsname ubuf; uname(&ubuf); ], jigdo_cv_func_uname="yes", jigdo_cv_func_uname="no" ) ) if test "$jigdo_cv_func_uname" = "yes"; then AC_DEFINE(HAVE_UNAME, 1) else AC_DEFINE(HAVE_UNAME, 0) fi dnl On native Windows (MinGW32), there is no snprintf, just _snprintf if test "$ac_cv_func_snprintf" = no -a "$ac_cv_func__snprintf" = "yes"; then AC_DEFINE(snprintf, _snprintf) fi dnl On native Windows, must use _stati64 for big file support AC_CACHE_CHECK(for _stati64, jigdo_cv_func__stati64, AC_TRY_COMPILE([ #include #include ], [ struct __stati64 buf; int x = _stati64("file", &buf);], jigdo_cv_func__stati64=yes, jigdo_cv_func__stati64=no)) if test "$is_windows" = "yes" -a "$ac_cv_func__stati64" = "yes"; then AC_DEFINE(stat, _stati64) fi dnl ______________________________________________________________________ dnl Check for Native Language Support (gettext) AC_MSG_CHECKING(for value of --enable-nls) AC_ARG_ENABLE(nls, [ --disable-nls Disable multi-language support], USE_NLS=$enableval, USE_NLS=yes) AC_MSG_RESULT(\"$USE_NLS\") AC_SUBST(USE_NLS) CATALOGS="" if test "$USE_NLS" = "yes"; then oldLIBS="$LIBS" AC_MSG_CHECKING(for dgettext in and libc) AC_TRY_LINK([#include ], dgettext(0, 0), have_intl="yes", have_intl="no"); AC_MSG_RESULT($have_intl) if test "$have_intl" = "no"; then LIBS="$LIBS -lintl"; AC_MSG_CHECKING(for dgettext in and -lintl) AC_TRY_LINK([#include ], dgettext(0, 0), have_intl="yes", have_intl="no"); AC_MSG_RESULT($have_intl) fi if test "$have_intl" = "no"; then AC_MSG_RESULT([ * Disabling gettext support - jigdo will not be]) AC_MSG_RESULT([ * able to display translated status/error]) AC_MSG_RESULT([ * messages instead of the default English ones.]) USE_NLS="no" LIBS="$oldLIBS" fi fi if test "$USE_NLS" = "yes"; then AC_DEFINE(ENABLE_NLS, 1) AC_MSG_CHECKING(for catalogs to be installed) ALL_LINGUAS=`(cd "$srcdir/po" && echo *.po *.pox "") \ | sed -e "s/\**\.pox* / /g"` if test "$LINGUAS"; then NEW_LINGUAS="" for provided in $ALL_LINGUAS; do x="" for wanted in $LINGUAS; do case "$provided" in "$wanted"*) x=" $provided";; esac case "$wanted" in "$provided"*) x=" $provided";; esac done NEW_LINGUAS="$NEW_LINGUAS$x" done LINGUAS="$NEW_LINGUAS" AC_MSG_RESULT(LINGUAS:$LINGUAS) else LINGUAS="$ALL_LINGUAS" AC_MSG_RESULT($LINGUAS) fi for x in $LINGUAS; do CATALOGS="$CATALOGS $x.gmo"; done else AC_DEFINE(ENABLE_NLS, 0) fi AC_SUBST(CATALOGS) dnl ______________________________________________________________________ dnl Checks for other --enable-xxx, --with-xxx switches dnl AC_MSG_CHECKING(for value of --enable-libwww-hacks) dnl AC_ARG_ENABLE(libwww-hacks, dnl [ --disable-libwww-hacks Don't compile fixed versions of some libwww dnl code (Try this if you see link errors/crashes)], dnl jigdo_libwww_hacks=$enableval, jigdo_libwww_hacks=yes) dnl AC_MSG_RESULT(\"$jigdo_libwww_hacks\") dnl if test "$jigdo_libwww_hacks" = yes; then dnl IF_LIBWWW_HACKS="" # enable hacks dnl elif test "$jigdo_libwww_hacks" != no; then dnl AC_MSG_ERROR(Invalid argument to --enable-libwww-hacks option) dnl else dnl IF_LIBWWW_HACKS="#" # disable hacks, by commenting out Makefile line dnl fi dnl AC_SUBST(IF_LIBWWW_HACKS) dnl ______________________________________________________________________ dnl Do this at end in order not to influence other compile tests which dnl might return warnings. Turning the warnings into errors would make dnl the tests fail. dnl if test "$jigdo_debug" = "yes"; then dnl #if test "$GCC" = yes; then CFLAGS="$CFLAGS -Werror"; fi dnl if test "$GXX" = yes; then CXXFLAGS="$CXXFLAGS -Werror"; fi dnl fi if test "$GXX" = "yes"; then CFLAGS="-Wall $CFLAGS -W" CXXFLAGS="$CXXFLAGS -Wall -W -Wpointer-arith -Wconversion -Woverloaded-virtual" fi dnl ______________________________________________________________________ if test "$cross_compiling" = yes; then IF_CROSSCOMPILING="" IFNOT_CROSSCOMPILING="#" else IF_CROSSCOMPILING="#" IFNOT_CROSSCOMPILING="" fi AC_SUBST(IF_CROSSCOMPILING) AC_SUBST(IFNOT_CROSSCOMPILING) dnl ______________________________________________________________________ dnl Touch TAGS to prevent it from being recreated when a normal user dnl builds the program. TAGS should only be updated once changes are dnl made to the source. touch "$srcdir/src/TAGS" SUBDIRS="glibcurl gtk job net util" SRC_MAKEDEPS="$srcdir/src/Makedeps" dnl When bootstrapping from CVS, we need to make sure that correct dnl dependencies cause gtk/interface.hh to be created if test -e "$SRC_MAKEDEPS"; then true; else AC_MSG_RESULT(Creating $SRC_MAKEDEPS) touch "$SRC_MAKEDEPS" echo >"$srcdir/src/gtk/interface.hh" (cd "$srcdir/src" find . -type f '(' -name '*.cc' -o -name '*.c' ')' \ | xargs "$ac_cv_prog_AWK" -f ../scripts/depend.awk "$srcdir" $SUBDIRS - ) rm -f "$SRC_MAKEDEPS.bak" "$srcdir/src/gtk/interface.hh" fi AC_SUBST_FILE(SRC_MAKEDEPS) dnl Create subdirs in src directory for dir in "" $SUBDIRS; do test ! -d "src/$dir" && mkdir "src/$dir" done AC_OUTPUT(Makefile doc/Makefile src/Makefile po/Makefile) jigdo-0.7.3/deb/.cvsignore0000644000175000017500000000001607701377541015171 0ustar richardrichardchangelog cvs jigdo-0.7.3/deb/compat0000644000175000017500000000000207701377541014372 0ustar richardrichard4 jigdo-0.7.3/deb/control0000644000175000017500000000462010265457631014577 0ustar richardrichardSource: jigdo Section: utils Priority: extra Maintainer: Richard Atterer Build-Depends: debhelper (>= 4), zlib1g-dev, libbz2-dev, libdb4.3-dev | libdb4.2-dev | libdb4-dev, libgtk2.0-dev, libcurl3-dev | libcurl2-dev Standards-Version: 3.5.6 Package: jigdo Architecture: any Depends: ${shlibs:Depends} Suggests: jigdo-file Description: Distribution of huge files - GTK+ download manager Jigsaw Download, or short jigdo, is a scheme developed primarily to make it easy to distribute huge filesystem images (e.g. CD (ISO9660) or DVD (UDF) images) over the internet, but it could also be used for other data which is awkward to handle due to its size, like audio/video files or large software packages. . jigdo tries to ensure that the large file is downloaded in small parts which can be stored on different servers. People who want to download the image do so by telling the jigdo download tool to process one ".jigdo" file; using it, jigdo downloads the parts and reassembles the image. jigdo-file is used to prepare the files for download. . This package contains "jigdo", the GTK+ download tool. . This package was built directly from the author's source package at . IT IS NOT AN OFFICIAL DEBIAN PACKAGE. Package: jigdo-file Architecture: any Depends: wget, ${shlibs:Depends} Suggests: jigdo Conflicts: jigdo (<< 0.6.0) Replaces: jigdo (<< 0.6.0) Description: Distribution of huge files - tool for preparing data for download Jigsaw Download, or short jigdo, is a scheme developed primarily to make it easy to distribute huge filesystem images (e.g. CD (ISO9660) or DVD (UDF) images) over the internet, but it could also be used for other data which is awkward to handle due to its size, like audio/video files or large software packages. . jigdo tries to ensure that the large file is downloaded in small parts which can be stored on different servers. People who want to download the image do so by telling the jigdo download tool to process one ".jigdo" file; using it, jigdo downloads the parts and reassembles the image. jigdo-file is used to prepare the files for download. . This package contains "jigdo-file", the tool used for creating ".jigdo" files, and also "jigdo-lite", a script to download Debian CD images. . This package was built directly from the author's source package at . IT IS NOT AN OFFICIAL DEBIAN PACKAGE. jigdo-0.7.3/deb/copyright0000644000175000017500000000174010262765335015127 0ustar richardrichardCopyright (C) 2001-2005 | richard@ Richard Atterer | atterer.net 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. Please note: The copyright notice in the file COPYING only applies to the text of the GNU General Public License; the copyright of the individual source files is as specified at the top of each file and above. Also note that the code is licensed under GPL _version_2_ and no other version. Special licensing for my (RA's) code is available on request. On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. jigdo-0.7.3/deb/install-build-deps.sh0000755000175000017500000000023410261525207017213 0ustar richardrichard#! /bin/sh cmd() { echo "$@"; "$@"; } cmd apt-get install \ debhelper \ zlib1g-dev \ libbz2-dev \ libdb4.2-dev \ libgtk2.0-dev \ libcurl3-dev jigdo-0.7.3/deb/jigdo-file.docs0000644000175000017500000000007707701377541016063 0ustar richardrichardchangelog doc/TechDetails.txt doc/debian-jigdo-mini-howto.html jigdo-0.7.3/deb/jigdo.docs0000644000175000017500000000001207701377541015133 0ustar richardrichardchangelog jigdo-0.7.3/deb/jigdo.undocumented0000644000175000017500000000001007701377541016673 0ustar richardrichardjigdo.1 jigdo-0.7.3/deb/rules0000755000175000017500000000475710433370236014257 0ustar richardrichard#!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # Non-standard default goal. This also does enough to allow you to # compile from CVS just by typing "deb/rules" build-deb: debian/changelog if test ! -d debian -a ! -h debian; then ln -s deb debian; fi if test ! -f configure; then autoconf; fi dpkg-buildpackage -rfakeroot -us -uc -b rm -f debian deb/changelog debian/changelog: printf "jigdo (%s) unstable; urgency=low\n\n * This version was built directly from upstream sources. It is not\n an official Debian package.\n\n -- Richard Atterer %s\n" "`read a b v deb/changelog src/Makedeps: $(MAKE) depend # If the user did "./configure --some-option && make deb", then # recover the --some-option from config.status and use it when # reconfiguring with correct --prefix configure: configure-stamp configure-stamp: dh_testdir @o=`test config.status && sed -n "/^# [^ ]*configure/ { s/\(^# [^ ]*\|'\?--\(prefix\|mandir\|infodir\)=[^ ]*\)//gp; q;}" config.status`; \ echo "(user-supplied configure options: $$o)"; \ cmd="./configure $$o --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info"; \ echo "$$cmd"; $$cmd touch configure-stamp build: configure-stamp src/Makedeps debian/changelog build-stamp build-stamp: dh_testdir $(MAKE) $(MAKE) -C src test touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp -$(MAKE) distclean dh_clean install: build dh_testdir dh_testroot dh_clean -k $(MAKE) DESTDIR=debian/jigdo install-jigdo install-po $(MAKE) DESTDIR=debian/jigdo-file \ install-jigdo-file install-jigdo-lite install-jigdo-mirror ln -sf "../common-licenses/GPL-2" \ "debian/jigdo/usr/share/jigdo/COPYING" for package in jigdo jigdo-file; do \ mkdir -p debian/$$package/usr/share/bug/$$package; \ at="@"; echo "Send-To: jigdo\${at}atterer.net" \ >debian/$$package/usr/share/bug/$$package/control; \ done # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installdocs dh_installexamples dh_installmenu # dh_undocumented # dh_installchangelogs dh_link dh_strip dh_compress dh_fixperms dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure jigdo-0.7.3/doc/.cvsignore0000644000175000017500000000004707701403045015175 0ustar richardrichardMakefile .cvsignore *.1 *.html cvs tmp jigdo-0.7.3/doc/BuildOnWindows.txt0000644000175000017500000002067510324230402016644 0ustar richardrichard There are several different ways of building jigdo for Windows: Recommended: A) On Linux, cross-compiling for Windows. Most comfortable in the long run, recommended! Described below. B) On Windows, using MinGW + MSYS. Described below. Should work, but not tested: - On Windows, using Cygwin, but actually calling the MinGW gcc/g++. Possible, but I do not see the point - these days, MSYS has enough features to execute the configure script. - On Windows, using Cygwin, and instructing it (with -mms-bitfields) to generate code which does not use the Cygwin DLL. Never tried, might work. ---------------------------------------------------------------------- Getting the Windows versions of the libraries (applies to A and B) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These days, jigdo depends on a quite large number of libraries. An incomplete list is: zlib, libbzip2, libcurl + OpenSSL, GTK+. Tracking down, downloading and installing all the Windows versions of these libraries, together with header files, DLLs etc is quite a bit of work. For this reason, I have put together a script which does all this work for you. "scripts/win-lib-install.sh" only needs to be given destination directories for the downloaded .zip files (variable "dl" near the top of the script) and the unpacked software (variable "inst"). Executing this script is possible either under Linux or Windows (MSYS, to be exact). ---------------------------------------------------------------------- A) Build On Linux, cross-compiling for Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have the choice between building on Linux and Windows, choose Linux! MinGW/MSYS is really quite slow, compiling natively is not a lot of fun. To cross-compile jigdo: - Get a cross-compiler which runs on Linux and produces code for Windows. MinGW , a special x86 version of GCC, is the right choice. You can build the cross-compiler yourself from the GCC/MinGW sources or search for precompiled versions - see . For Debian Linux, it is much simpler: Execute "apt-get install mingw32 mingw32-binutils mingw32-runtime" to install the Debian mingw cross-compiler package. The cross-compiler programs must be installed in a directory which is in your PATH, so that you can invoke them as "i586-mingw32msvc-gcc", "i586-mingw32msvc-g++" etc. - Get Windows versions of all the libraries that jigdo needs, by executing "scripts/win-lib-install.sh". - Now run the configure script as follows: ./configure --host i586-mingw32msvc --with-pkg-config-prefix=/inst --without-libdb Instead of "/inst", substitute the value the "inst" variable that you chose. The value must be an absolute path. The --with-pkg-config-prefix switch is a speciality of jigdo's configure script, it is not supported by other projects. Also, it only works if the detected compiler is GCC. The configure script automatically adds the necessary -mms-bitfields switch for GCC, and -march=pentium to optimize for Pentium and later processors. - Type "make" and hope for the best! :-) Using samba, I export the directory with the cross-compiled binaries to another machine running Windows - very useful because tests are possible immediately after compilation. By the way, another very useful tool in this situation is "Synergy" , it allows you to share a keyboard and mouse between the Linux and Windows machines. When you double-click jigdo.exe or jigdo-file.exe under Windows, you'll likely get a message about missing DLLs. To fix this, either copy all the required DLLs from $inst/bin to the same directory as the .exe, or write a small jigdo.bat file which adds the directory with the DLLs to PATH: path Y:\inst\bin;%PATH% jigdo.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 NOTE: configure.in sets up @MWINDOWS@ in such a way that a console window ("DOS window") will open alongside the jigdo window if you compile jigdo.exe with --enable-debug. Otherwise, the console window will not appear. The -mwindows switch to the g++ link command prevents the window from opening. ---------------------------------------------------------------------- B) Build on Windows, using MinGW + MSYS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MinGW GCC is the special version of GCC which can create Windows .exe files. MSYS is an environment which provides you with a Unix command line environment based on MinGW; a shell and all the utilities you need to execute configure scripts. To compile jigdo under Windows: - Download and install the following files from : msysDTK-x.x.x.exe (MSYS developer toolkit) MSYS-x.x.x.exe (MSYS; shell and many Unix utilities) MinGW-x.x.x.exe (gcc, g++, binutils) - Before win-lib-install.sh can run successfully under MSYS, you need to download and install unzip.exe: http://www.info-zip.org/UnZip.html#Win32 wget.exe: http://gnuwin32.sourceforge.net/packages/wget.htm Copy the binaries into your PATH, e.g. into /bin under MSYS. - Set up the "inst" and "dl" variables in win-lib-install.sh and run it to install the libraries. - "./configure --without-libdb" and "make" The configure script automatically adds the necessary -mms-bitfields switch for GCC, and -march=pentium to optimize for Pentium and later processors. In the future, jigdo on Win might use the NSIS installer from . For debugging, the gdb supplied with mingw is useful, and so is Dr. MinGW: In case you want to install software in a different location than /mingw/local and want to set LIBRARY_PATH, C_INCLUDE_PATH or CPLUS_INCLUDE_PATH, note that with mingw, the path separator is not ':', but ';' ---------------------------------------------------------------------- Misc notes from older versions of this file, may or may not apply: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - If the zlib distribution does not ship with a lib/libzdll.a file or similar for mingw, generate it as suggested in its USAGE.txt, with a command like this from within the $GTKWIN directory: i586-mingw32msvc-dlltool -D zlib1.dll -d lib/zlib.def -l lib/libzdll.a - Cygwin actually includes mingw as one of the installable components. I have never tried that, though it probably works... better don't use Cygwin's toolchain with the -mno-cygwin switch, apparently this causes problems sometimes. - For the mingw binaries, C:\mingw is accessible as /mingw. For Cygwin binaries, that is not the case ("/" in Cygwin is mapped to C:\cygwin). To fix this, execute from within Cygwin: ln -s /cygdrive/c/mingw /mingw - Should you experience weird problems during compilation later on, remove or rename the mingw version of make, e.g. with mv /mingw/bin/make.exe /mingw/bin/make-mingw.exe In other words, use the Cygwin version. I have encountered a number of bizarre problems with the mingw make when compiling libwww. [The same applies to MSYS; prefer the MSYS make over the MinGW one. Luckily, the slightly broken MinGW make is called "mingw-make.exe" (or something like that) today.] The following script may be useful to you. I put it in my .bashrc under Cygwin, then just entering "mingw" will set up all environment variables ready for mingw compilation, and "nomingw" will switch back to Cygwin. function mingw() { if test -z "$old_PS1"; then dirs=/mingw/local/* old_PS1="$PS1" old_PATH="$PATH" PS1='\[\033[33m\w\033[0m\] MinGW> ' export CC='gcc -march=pentium -fnative-struct' export CXX='c++ -march=pentium -fnative-struct' export MACHTYPE="i586-pc-mingw32" for dir in $dirs; do C_INCLUDE_PATH="$C_INCLUDE_PATH;/mingw/local/$dir/include" CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH;/mingw/local/$dir/include" LIBRARY_PATH="$LIBRARY_PATH;/mingw/local/$dir/lib" PATH="$PATH:/mingw/local/$dir/bin:/mingw/local/$dir/lib" done unset dir dirs export PATH="/mingw/bin:$PATH" export C_INCLUDE_PATH="${C_INCLUDE_PATH#;}" export CPLUS_INCLUDE_PATH="${CPLUS_INCLUDE_PATH#;}" export LIBRARY_PATH="${LIBRARY_PATH#;}" fi } function nomingw() { if test "$old_PS1"; then export PS1="$old_PS1" export PATH="$old_PATH" unset old_PS1 CC CXX LIBRARY_PATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH unset old_PATH fi } Contact me in case of problems. Richard Atterer jigdo-0.7.3/doc/Hacking.txt0000644000175000017500000004332010263053011015272 0ustar richardrichard Hacking the jigdo source code ============================= This file is not very organized... if in doubt, use the source, Luke! Use --enable-debug when hacking the source, this enables quite a few additional sanity checks (the "Paranoid()" assertions - the "Assert()" assertions are always enabled), #defines GTK_DISABLE_DEPRECATED, calls abort() on failed assertions instead of just printing the assertion, etc. Mailing list, CVS ~~~~~~~~~~~~~~~~~ The jigdo-user mailing list is currently used both for end-user support and development. Subscribe: https://lists.berlios.de/mailman/listinfo/jigdo-user Archive: https://lists.berlios.de/pipermail/jigdo-user Address: jigdo-user at lists.berlios.de Anonymous CVS access to the latest source code is available. Execute cvs -d:pserver:anonymous@cvs.jigdo.berlios.de:/cvsroot/jigdo login Just press return at the login prompt, then execute cvs -z3 -d:pserver:anonymous@cvs.jigdo.berlios.de:/cvsroot/jigdo co jigdo Latest code: http://cvs.berlios.de/cgi-bin/viewcvs.cgi/jigdo/jigdo/jigdo.tar.gz?tarball=1 Browse tree: http://cvs.berlios.de/cgi-bin/viewcvs.cgi/jigdo/jigdo/ Bootstrapping the code ~~~~~~~~~~~~~~~~~~~~~~ If you checkout the code from CVS or download the CVS snapshot, some additional files have to be generated - in the jigdo release tarballs, these files are already present. The Makefiles automatically handle regeneration of those files if they are not present; the only thing that you need to do manually is to invoke autoconf to create the configure script, then you can do "./configure --enable-debug ..." and "make" as usual. Additional tools required for bootstrapping the code: - autoconf, to create the configure script - docbook-utils (docbook2html, docbook2man), to generate HTML documentation and manpages from the SGML in the "doc" subdirectory. - the gettext tools (msgfmt), to generate the .gmo files in the "po" subdirectory. - glade, to generate "src/gtk/interface.cc" from "jigdo.glade" - awk (or gawk/mawk), to generate "src/Makedeps" and to post-process "src/gtk/interface.cc" On Debian systems, bootstrapping and compiling is possible simply by typing "deb/rules". Patches ~~~~~~~ Small bugfixes are very welcome, just send them to the mailing list. Before starting anything bigger, better check with me whether it coincides with my ideas - just to avoid frustration later on. I'm pretty open to all ideas, though, especially if they save me some work. :) ATM, GCC 2.95 is still supported, so contributed code should compile with GCC 2.95 as well as the latest GCC 4.x. See compat.hh for a few replacements for things which GCC 2.95 doesn't have. Sub-projects ~~~~~~~~~~~~ There are some things I'd *love* to see done by somebody else: Windows/Solaris/BSD/MacOS porting: ATM, I test and build jigdo on Linux and Windows, and occasionally on Solaris/BSD. Especially maintaining the Windows port is quite a lot of work... It'd also be nice if someone ensured that it builds on Redhat/SuSE/Mandrake, and maintained the jigdo.spec file. Paul Bolle has been doing this, get in touch with him if you want to improve the .spec file. Andrew Mathieson has started maintaining a Mac OS X port - the Fink port of jigdo had become rather neglected. See Non-interactive frontend for jigdo: The current GTK+ jigdo program is meant to be used interactively, not from a script or similar. The moment it is released, I will be inundated with requests for a version which does not need GTK+ or which behaves more like wget, or which uses curses, or... The code is organized in a way which makes adding new frontends quite easy. CGI-like program for on-the-fly creation from .jigdo/.template files: NOTE: Steve McIntyre has implemented something like this as part of JTE, see With Debian, we have lots of mirror admins who have the bandwidth to host CD images, but not the disc space. They'd like a CGI-like program which generates a pseudo dirlisting with .iso links in it, and, when such an .iso link is downloaded, it creates the .iso data from the .jigdo/.template files and a local Debian mirror _on_the_fly_ as it is being sent over the net. NB however: This *cannot* be a CGI for performance reasons and because it must support HTTP/1.1 ranges (i.e. resuming of downloads). Also, it will be necessary to "pre-process" the .template file because repeatedly ungzipping it would be too slow. If not a CGI, what form should it have? I don't know - I'd go for an Apache module, but Attila Nagy tells me Apache needs too much memory if you have many thousand concurrent downloads. Maybe one could add the functionality to an existing small httpd? ".rawtemplate" support: NOTE: This is now being tackled by Steve McIntyre, see It is possible to make .jigdo creation for Debian CDs much faster by hacking mkisofs to support more direct output of .jigdo/.template files: First, change mkisofs to output not .iso format, but a special ".rawtemplate" (or whatever) format. That format is similar to the normal .template format, except that files are not referenced by their md5sum, but by their filename, and that none of the data is compressed. This should be quite simple, and thus the modifications to mkisofs should be few and small, making it more likely that such a patch is accepted by mkisofs upstream. Next, the .rawtemplate is piped into jigdo-file, which creates .jigdo and .template files as usual. However, due to its cache, it won't need to read the files' contents to know their md5sum, so the overall process is much faster. Translations ~~~~~~~~~~~~ Both jigdo-file and jigdo use gettext, translations are welcome. Please announce your efforts on the mailing list before you start, to avoid duplication of effort. Note: Don't translate any of the strings beginning with '#'. glade doesn't allow me to selectively switch off the "gettextizing" of some strings, and it would make editing the .glade file awkward if I left those strings empty, so I'm marking them with the '#'. Directory layout ~~~~~~~~~~~~~~~~ src: Files which don't belong into one of the subdirs... Also contains lots of stuff from before the point where I started creating subdirs - much of this should move into a subdir sooner or later. src/net: Low-level network access. This used to use libwww for the actual net access. I switched over to libcurl between 0.7.1 and 0.7.2 and haven't looked back! The only disadvantage of libcurl over libwww is that it doesn't support HTTP pipelining, but libwww really is very difficult to work with. All code accessing libcurl should be isolated in this dir *only*. Files in this dir may make calls to glib, but not gtk. src/job: Application logic A "job" is a certain task, e.g. downloading an URL, processing a .jigdo file etc. For the gtk frontend, one job corresponds to one line in the lower part of the window - but this visualisation is the gtk GUI's decision. All jobs are classes in namespace "Job". Each such class declares an abstract child class (i.e. an interface) named "IO", which the code of the user interface needs to implement. For example, Job::SingleURL::IO is implemented by some code in the src/gtk directory (GtkSingleUrl), which when called sets up some GTK widgets. Files in this dir may make calls to glib, but not gtk. src/gtk: GTK+ graphical user interface Provides a way to access the facilities of "jobs" via GTK widgets etc. This is the only dir whose files may make calls to GTK (and of course also glib). src/util: Independent small helper classes Something goes in here if all of the following is true: - It is a single header file, optionally accompanied by a single .cc file and/or a corresponding *test.cc file - It accesses neither gtk nor glib - It is a small, reusable tool, maybe useful for other projects BTW, I try at all costs only to use C libraries from the code, never C++ libraries. You save yourself a lot of trouble that way, IMHO. Code style ~~~~~~~~~~ You're not required to adhere to my rules here, but maybe you'll find them useful... I use K&R-style indentation, indenting by 2 spaces for each level of indentation. I dislike using tabs in the source because it seems people can never agree on a value for the tab indentation. Emacs can be told never to generate tabs when indenting code; see below. Source files in different directories must not have the same leafname, or depend.awk and also #include will break. (IMHO identical leafnames are also a source of confusion.) Functions/methods should not be much longer than one screen. Source files should never be larger than 1000 lines, and probably not even larger than 500 lines. I'm perfectly aware that the code violates these rules all the time. ;-/ All code must, *MUST* be commented. If possible, larger components should be accompanied by a *-test.cc file which tests the code. If the name of the file is "foo-test.cc" (with a "-"), depend.awk will automatically generate a simple Makefile entry. If that entry is not sufficient, call the file "footest.cc" and manually add a Makefile rule. Lines must not be longer than 77 characters. To format comments to exactly that width, and also never to use tabs for indentation, use this in your ~/.emacs: (add-hook 'c++-mode-hook (lambda () (interactive) (setq fill-column 77) (setq indent-tabs-mode nil))) Don't use reference parameters if a function/method writes to its args, use pointers instead. If I call a function, I want to see that I'm calling it as "function(&data)", so "data" may be modified. Don't use multiple inheritance, except if all but one of the base classes are abstract or trivial (where "trivial" is things like NoCopy, which prevents copying of its derived classes). Don't have identically named classes in different namespaces, big confusion will ensue. (Don't over-use namespaces in the first place.) Header files (.fh, .hh, .ih) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To avoid excessive recompilation, I use three levels of headers. .fh contain just forward declarations of classes and global functions, and must not include .hh or .ih headers. .hh are the main headers, and .ih files contain functions which are compiled inline for --disable-debug and not inline for --enable-debug. This is done by checking for the definedness of NOINLINE. "make depend" updates the dependencies in src/Makedeps. The contents of this file are then appended to the Makefile by the configure script. See also the comments in scripts/depend.awk. Unit tests ~~~~~~~~~~ Note: Because the unit tests use stringstreams and other useful stuff which GCC 2.95 doesn't provide, they won't work with that version of GCC. I tried out cppunit and had a look at other things, but did not like any of the solutions, because IMHO they are over-engineered and unnecessarily complicated. I prefer creating a new executable for each test, and not to pack all tests into one executable, because this avoids linking problems if you replace some classes with dummy versions for one test, but want to link in the original code for another. My alternative solution is very simple: Each test is an executable which returns zero on success, and non-zero otherwise. You can use a normal Assert() to check for correct results, because it calls abort() if the assertion fails, which will return a non-zero exit value. To add the test unit program in file subdir/foobar-test.cc (which supposedly tests the code in subdir/foobar.cc): - Add a line "#test-deps" to your subdir/foobar-test.cc source file inside a comment. The directive can optionally be followed by a space-separated list of .o files which need to be linked into the executable. $(TEST-DEFAULTOBJS) are added automatically to this list in the Makefile (expands to "util/log.o util/string-utf.o util/debug.o") - Add "subdir/foobar-test@exe@" to the definition of "test-programs" in Makefile.in - "make depend" - this reads the above "#test-deps" directive and creates an appropriate Makefile entry for the unit test. - "make test" to run all tests, in quiet mode, or "make test-v" in verbose mode (if "dir/prog" failed, re-executes it as "dir/prog all" to enable debug info). If --enable-debug was passed to the configure script, the unit tests are also run by "make all" When linking the tests, $(TEST-LDFLAGS) is added to the link command. By convention, if the test is called with exactly one arg, that arg sets up the debug units for which output is to be enabled - just like the arg to the --debug switch of jigdo and jigdo-file. Add support for this in main() as follows: #include int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); ... This is useful while testing the unit test: Whereas "make test" will call the test as "subdir/foobar-test", you can execute it as "subdir/foobar-test all" to enable debug output. Have a look at log.hh for the useful debug("fmt", args...) feature. Implementation ~~~~~~~~~~~~~~ The application logic in job/ and net/ is separated from the frontend in gtk/. Each Job corresponds to one action (downloading a file, processing a .jigdo file), the GTK GUI decides to visualize this as one line in the display. The GTK code maintains a list of JobLines, each of which references a Job of some kind. A simple type of job is a SingleURL, which takes care of downloading a single file. A more complicated job is the processing of a .jigdo file. This involves a number of sub-jobs for downloading data. The "master" job is a MakeImageDl, it starts other "child" jobs for the individual downloads. The action of *assembling* the data is not implemented as a job, instead as a MakeImage object which is owned by the MakeImageDl and fed data by it. See makeimage.hh. Important for the UI: Any UI (whether GTK+ or other) is only allowed to create and destroy top-level jobs. Any job created by a "master" MakeImageDl can only be deleted by the same MakeImageDl. This means that the GUI code which handles SingleURL GTK+ visualization must distinguish between a "child mode" (we don't own the SingleUrl) and "non-child mode" (we created and own the SingleUrl; this will only be the case if the UI also allows single-file downloads). It might even be advisable to have two classes for the two cases; ATM the GTK+ frontend doesn't. MakeImageDl ~~~~~~~~~~~ This serves as an example (and it's the most complicated one) of how the frontend in gtk/ is decoupled from the application logic in job/ and other dirs. Basic idea: In the object that makes data available (e.g. SingleUrl in the case of a HTTP/FTP download), maintain a pointer to an IO object. This pointer is stored inside a public IOSource member (e.g. SingleUrl::io). SingleUrl::IO is an abstract class, to be implemented by the "consumer" of the data. This consumer is fed the data via calls to virtual methods like SingleUrl::IO::singleUrl_data(). There are many IO classes; Job::IO is their base (see job.hh). Implementers of SingleUrl::IO include GtkSingleUrl (GTK+ frontend) and JigdoIO (decompression/parsing of .jigdo data.) The consumer registers its IO-implementing object using IOSource::addListener(). There were *lots* of problems with dangling pointers with early versions of the producer-consumer mechanism: Typically, the frontend object would be deleted because of some user intervention, but the pointer to its producer would still be dereferenced to make one last "hey frontend, you are deleting me" (aka job_deleted()) call. This was not un-fixable, but difficult to maintain. So with the current code, all IO implementers are automatically deregistered from their IOSource when they are destroyed. Often, there is more than one consumer. For example, when downloading .jigdo data, a SingleUrl creates the data (more accurately its Download does) and writes it to disc. The MakeImageDl which owns that SingleUrl wants to have a look at the data and interpret the sections inside the .jigdo, which it does by registering a JigdoIO with SingleUrl::io in MakeImageDl::createJigdoDownload() ("io" member inherited from DataSource). In order to get notified of any news, the GtkSingleUrl frontend also registers itself with the SingleUrl in GtkSingleUrl::openOutputAndRun(). In the most complicated scenario, the downloaded data must also be decompressed by the JigdoIO, resulting in the following flow of data - the "->" can be interpreted both as "an IO* points this way" and "information flows this way": Download -> SingleUrl -+-> JigdoIO -> JigdoIO +-> GtkSingleUrl It might be confusing that JigdoIO appears twice; this is because the same JigdoIO object has a double role as consumer of the SingleUrl data and as consumer of the uncompressed data of the Gunzip it owns. IOW, it implements both DataSource::IO and Gunzip::IO. MakeImageDl's job is to start downloads as necessary and to pump their data into the MakeImage object it owns. MakeImage then creates the output image. With childFor(), MakeImageDl is a factory of DataSource objects. A DataSource can be either a download (SingleUrl) or a "dummy" spooler object which just reads data from a file in the cache (CachedUrl). After creation of the DataSource, an appropriate consumer for the downloaded data (e.g. a JigdoIO) is attached to it. JigdoIO and its related classes are helpers of the MakeImageDl. They hold a pointer to their "master" MakeImageDl and notify it whenever downloads fail or succeed. The MakeImageDl can act as necessary, e.g. because the complete information of the .jigdo file is available to it, it can retry the failed download, but use a different server. -- __ _ |_) /| Richard Atterer | \/¯| http://atterer.net ¯ '` ¯ jigdo-0.7.3/doc/Makefile.in0000644000175000017500000000244607730450556015262 0ustar richardrichard# Project: Jigdo (Jigsaw download) # __ _ # |_) /| Copyright (C) 2001-2003 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ srcdir = @srcdir@ VPATH = @srcdir@ AWK = @AWK@ DOCBOOK2MAN = docbook2man DOCBOOK2HTML = docbook2html #______________________________ .SUFFIXES: .SUFFIXES: .1 .sgml .html .sgml.1: mkdir -p tmp && cp "$<" tmp \ && cd tmp && $(DOCBOOK2MAN) "`echo $< | sed 's%.*/%%'`" sed -e 's/ */ /g' <"`ls tmp/*.1`" >"$@" rm -rf tmp # sed -e 's%\\%\\\\%g;' <"$<" | tr -s '[:space:]' ' ' \ # | $(DOCBOOKTOMAN) - >"$@" .sgml.html: rm -f "$@" $(DOCBOOK2HTML) "$<" --nochunks mv "$@" "$@~" $(AWK) -f "$(srcdir)/../scripts/html-beautify.awk" \ "$@~" >"$@" #______________________________________________________________________ .PHONY: all clean distclean mostlyclean maintainer-clean \ dep depend doc check all: jigdo-file.1 jigdo-file.html jigdo-lite.1 jigdo-lite.html \ jigdo-mirror.1 jigdo-mirror.html jigdo.1 jigdo.html \ debian-jigdo-mini-howto.html doc mostlyclean dep depend check: ; clean: Makefile rm -rf tmp distclean: Makefile rm -rf tmp rm -f *~ \#*\# *.bak rm -f Makefile maintainer-clean: Makefile distclean rm -f *.html *.1 Makefile: Makefile.in cd .. && sh config.status # update Makefile from Makefile.in jigdo-0.7.3/doc/README-bindist.txt0000644000175000017500000000657210233001777016336 0ustar richardrichard## The text below is written to the README file that is distributed in ## the tarballs with statically linked binaries (created with "make ## bindist"), jigdo-bin-x.y.z.tar.bz2 Jigsaw Download (jigdo) ~~~~~~~~~~~~~~~~~~~~~~~ Jigsaw Download homepage: Debian CD images via jigdo: jigdo-lite ~~~~~~~~~~ This program enables you to retrieve big files (for example, CD images) that someone offers for download in the form of ".jigdo" files. First, locate a site which offers such files with a ".jigdo" extension - see for CD and DVD images of Debian Linux in jigdo format. Next, download the image as follows: - Open a command line terminal and change directory to the same directory as this README (using the command "cd directoryname"). - Run the jigdo-lite script, by entering "./jigdo-lite". - The script will ask for the URL of a ".jigdo" file - enter the URL of your choice. - Follow the instructions printed by jigdo-lite - hopefully they will be self-explanatory. You can also have a look at the file "jigdo-lite.html" for a little documentation. "jigdo-file.html" contains a detailed technical description of jigdo. Making jigdo-lite use your proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To make jigdo-lite use your proxy for its downloads, first start "jigdo-lite" as described above. As soon as the input prompt has appeared, abort the program again by pressing Ctrl-C. You will find that jigdo-lite has created a file called ".jigdo-lite" in your home directory. Load this into an editor and find the line that starts with "wgetOpts". The following switches can be added to the line: -e ftp_proxy=http://LOCAL-PROXY:PORT/ -e http_proxy=http://LOCAL-PROXY:PORT/ --proxy-user=USER --proxy-passwd=PASSWORD Of course, you only need --proxy-user/password if your proxy requires authentication. The switches need to be added to the end of the wgetOpts line *before* the final ' character. All options must be on one line. jigdo-mirror ~~~~~~~~~~~~ The included jigdo-mirror script is for people who have a full Debian FTP mirror on their harddisc as well as a mirror of the Debian jigdo files, and who want to create all the images offered by these jigdo files. Additionally, if the script is run at regular intervals, updated/added/deleted jigdo files are detected. See "jigdo-mirror.html" for more information. ---------------------------------------------------------------------- Copyright (C) 2001-2005 | richard@ Richard Atterer | atterer.net 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. Please note: The copyright notice in the file COPYING only applies to the text of the GNU General Public License; the copyright of the individual source files is as specified at the top of each file and above. Also note that the code is licensed under GPL _version_2_ and no other version. Special licensing for my (RA's) code is available on request. jigdo-0.7.3/doc/README-windist.txt0000644000175000017500000000515210233001777016354 0ustar richardrichard Jigsaw Download (jigdo) for Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Jigsaw Download homepage: Debian CD images via jigdo: jigdo-lite ~~~~~~~~~~ This program enables you to retrieve big files (for example, CD images) that someone offers for download in the form of ".jigdo" files. First, locate a site which offers such files with a ".jigdo" extension - see for CD and DVD images of Debian Linux in jigdo format. Next, download the image as follows: - Double-click on the "jigdo-lite.bat" file. - The script will ask for the URL of a ".jigdo" file - enter the URL of your choice. - Follow the instructions printed by jigdo-lite - hopefully they will be self-explanatory. Making jigdo-lite use your proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To make jigdo-lite use your proxy for its downloads, first double-click on "jigdo-lite.bat" to start the program. As soon as the input prompt has appeared, abort the program again and close the command window. You will find that jigdo-lite has created a file called "jigdo-lite-settings.txt" in the same directory as this README.txt file. Load this into an editor and find the line that starts with "wgetOpts". The following switches can be added to the line: -e ftp_proxy=http://LOCAL-PROXY:PORT/ -e http_proxy=http://LOCAL-PROXY:PORT/ --proxy-user=USER --proxy-passwd=PASSWORD Of course, you only need --proxy-user/password if your proxy requires authentication. The switches need to be added to the end of the wgetOpts line *before* the final ' character. All options must be on one line. ---------------------------------------------------------------------- Copyright (C) 2001-2005 | richard@ Richard Atterer | atterer.net 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. Please note: The copyright notice in the file COPYING only applies to the text of the GNU General Public License; the copyright of the individual source files is as specified at the top of each file and above. Also note that the code is licensed under GPL _version_2_ and no other version. Special licensing for my (RA's) code is available on request. jigdo-0.7.3/doc/TechDetails.txt0000644000175000017500000002441010224334171016124 0ustar richardrichard File format for .jigdo files ============================ This is described in detail in the manual for jigdo-file. See the files `jigdo-file.*' in this directory. Additional notes: - The reason for all those quoting rules in the .jigdo is that in the future, it may be allowed to add options to some lines, e.g.: MyServer=http://foo.com/ --referer=... --priority=... --decrypt - The difference between the Base64 encoding used by jigdo and the real one (RFC2045) is that jigdo uses `-' instead of `+' and it uses `_' instead of `/'. Additionally, no `=' characters are appended to the end of the Base64 string. Support for automatic ungzipping of .jigdo files is currently not present in jigdo-file, only in jigdo. Algorithm for finding the files in the image ============================================ How do you efficiently search for lots of files within another large file? First, a rolling checksum of the first blockLen bytes of each input file is calculated. Next, a "window" of blockLen bytes moves over the large image. For each window position, the checksum is calculated and looked up in a hash table which contains the checksums of the input files. As soon as the first blockLen bytes of a file have been seen in the image this way, the program starts calculating the MD5 sum of blocks of length md5BlockLength (where blockLen < md5BlockLength; the defaults are 1k, 64k resp.). These MD5 checksums are compared to pre-calculated MD5 sums of md5BlockLength-sized chunks of the file that matched, until one doesn't match, or the last one matched. Unfortunately, having to deal with situations like overlapping matches in the image, and output of the .template to stdout, lead to quite complex code (see mktemplate.cc). The reason why this whole process is quite fast is that moving the RsyncSum window by one byte is a very cheap operation: Two table lookups, one multiplication and four additions/subtractions. The checksum calculation algorithm is based on that used by rsync , but it has been extended by me from 32 bit to 64 bit, and also strengthened by the use of a lookup table. The jigdo-file file cache ------------------------- Even without --cache specified, jigdo-file uses an internal cache for holding information about files. This information includes at least: file length, rsync sum of head of file, MD5 sums of chunks of file, MD5 sum of the entire file. When files are first entered into the cache, the entire file is *not* yet read, only enough of it to generate the rsync sum and the first chunk's MD5 sum. (If this is also the last MD5 sum because the file is small, everything is generated.) The rest of the file is only read "on demand" if/when necessary. File format of .template and .tmp files ======================================= The image template files consist of three types of parts: - Header: Short ASCII header identifying the file as a jigdo template - Raw data: those parts of the image that were not matched, compressed with zlib - Description: A description of the image, saying which parts were matched by files and are thus not included in the template After the header of a template file, one or more raw data parts and one description part follow. Each part consists of a 4-byte ID ("DATA" or "BZIP" for the zlib/bzip2-compressed data parts, "DESC" for the description), followed by 6 bytes of length. The length values are little-endian (i.e. least-significant byte first) because that *is* the proper end to open an egg. The length includes the ID and length field itself, so for an empty part (containing just the ID and the length field) the length field would have the value 10. Temporary image files (.tmp files) consist of the image data, followed by a description part with the same format as in .template files. Those areas of the temporary image to which no file data has yet been written are filled with zero bytes. Header ------ The header is three lines of ASCII text, terminated with CR LF. They read: JigsawDownload template is of the form "1.0" - two integers separated by a dot. The integers can consist of more than one digit, e.g. "1.42". The number is the file format version of the jigdo template file, which is completely unrelated to the program's version number, but is the same as the format version of the location list (.jigdo) file. The first integer indicates backwards-incompatible changes to the format whenever its value changes. is the name and version number of the program that was used to create the template file. It is just for information and should not normally be considered when creating an image from the template. Its format is "programName/1.23", i.e. name and version number separated by "/". The field must not contain any space characters and only one "/" character. jigdo-file uses values like "jigdo-file/1.0.0". is any string excluding control characters. It contains some information, e.g. an URL, for those people who open the file in a text editor and haven't a clue what it is. ;-) Description ----------- This is binary data. Integer values are little-endian. File lengths are 6 bytes long - 256TB ought to be enough for everybody... The order of entries with type==2 or type==6 corresponds to how matched files and areas of unmatched data appear in the image file. There must only be one entry of type==5 in the list [which must probably be the last entry]. The pseudo program below would be suitable for decoding the description data. #Bytes Value Description ---------------------------------------------------------------------- 4 descID "ID for the part: 'DESC' = the hex bytes 44 45 53 43" 6 descLen "Length of part" while (less than descLen-16 bytes read) { 1 type switch (type) { case 5: "Information about the image file" 6 imgLen "Length in bytes of the original image" 16 imgMD5 "MD5 checksum of the original image" 4 blockLen "Nr of bytes used for calculating RsyncSums below" case 2: "Unmatched data, contained in 'Raw data'" 6 skipLen "Length in bytes of area of unmatched data" case 6: "Information about matched file" 6 fileLen "Length in bytes of file contained in image" 8 fileRsync "RsyncSum64 of first blockLen bytes of the file. fileLen < blockLen never happens." 16 fileMD5 "MD5 checksum of the file" } } 6 descLen "Length of part - identical to descLen at start of part" ---------------------------------------------------------------------- The following types are obsolete: case 1: "Information about the image file" Obsoleted by type 5. 6 imgLen "Length in bytes of the original image" 16 imgMD5 "MD5 checksum of the original image" case 3: "Information about matched file" Obsoleted by type 6. 6 fileLen "Length in bytes of file contained in image" 16 fileMD5 "MD5 checksum of the file" ---------------------------------------------------------------------- The reason why the length is repeated at the end is for determining the start offset of the DESC part just by reading those 6 bytes from the end of the template file. Otherwise, one would have to read the headers of all DATA parts to get to the DESC part. jigdo-file relies on this. [...and the reason why the DESC part comes last and not first in the template data is that jigdo-file allows outputting the template to stdout, but all the data contained in the description part is only available at the end of template creation.] Note: In the DESC part appended to a *temporary image file*, jigdo-file uses another type of entry: Once the type of a type==6 entry changes to type==7, that entry's data has successfully been written to the image. Raw data -------- Binary data, a stream compressed with zlib (see RFC1950) for "DATA" or libbz2 for "BZIP". For each type==2 entry in the description data, the uncompressed stream contains skipLen bytes of data. This is data that did not match any of the files presented to the creator of the template. For each type==6 entry in the description data, the raw data stream contains _nothing_at_all_ - in other words, the raw data is just a number of concatenated type==2 data chunks. No raw data sections may be present at all if the description data contains no type==2 entries. #Bytes Value Description ---------------------------------------------------------------------- 4 dataID "ID for the part: 'DATA' = the hex bytes 44 41 54 41 or 'BZIP' = the hex bytes 42 5a 49 50" 6 dataLen "Length of part, i.e. length of compressed data + 16" 6 dataUnc "Number of bytes of *uncompressed* data of this part" dataLen-16 "Compressed data" ---------------------------------------------------------------------- gzip: jigdo-file always takes care to subdivide the complete raw data into parts which are not larger than about 256kB each compressed. This is done to support certain applications of jigdo where seeking in the image is necessary. (Additionally, this allows jigdo-file to write the final template file to a non-seekable output, e.g. to stdout.) bzip2: The data is subdivided into chunks whose uncompressed size is almost exactly the size of the bzip2 chunk for that compression setting. For example, with the -9 switch, each chunk is about 900000 bytes long uncompressed. (More accurately, it is 899950 bytes long.) Example for an application which needs to seek: A CGI program which creates an image on the fly as it is being sent to a browser will need to seek to certain offsets in the image if it is to support HTTP 1.1 ranges. Note that the data of one type==2 entry in the description data may be distributed over more than one raw data part. File format version history --------------------------- 1.0 (jigdo-file/0.5.0): Initial format 1.1 (jigdo-file/0.6.3): Format change in template files: New type 5 obsoletes type 1. New type 6 obsoletes type 3. 1.2 (jigdo-file/0.7.2): Format change in template files: In addition to raw data that is gzipped ("DATA"), allow bzip2 ("BZIP") raw data. jigdo-0.7.3/doc/debian-jigdo-mini-howto.html0000644000175000017500000020140610433432675020500 0ustar richardrichard Debian Jigdo mini-HOWTO

Debian Jigdo mini-HOWTO

Peter Jay Salzman

Copyright © 2001 Peter Jay Salzman

2005-12-05 ver 1.8

Abstract

Getting Debian ISOs has always been a painful, slow and supremely inefficient process. Jigdo is a tool for distributing and obtaining Debian ISOs in an easy, fast and very efficient manner. This HOWTO describes why you should use jigdo, a little bit about how it works and how you use it to get and update Debian ISOs.

Jigdo is a very general tool, and isn't tied specifically to Debian ISOs. The jigdo tools can be used to make any ISO available for download in the same easy, fast and efficient manner they're used for Debian ISOs. This HOWTO will cover this as well, but we'll focus primarily on downloading Debian ISOs.


Table of Contents
1. Administrata
1.1. Authorship and Copyright
1.2. Acknowledgements
1.3. Comments and Corrections
1.4. Latest Version And Translations
2. Why jigdo?
2.1. How Does One Get A Debian ISO Image Set?
2.2. Why Not Download The Whole ISO Image?
2.3. What Is Jigdo?
3. How Jigdo Works (optional)
3.1. Preparing The ISO For Download
3.2. The .template File
3.3. The .jigdo File
3.4. Downloading The Image
4. Downloading Your First Image (In 5 Easy Steps)
4.1. Install Jigdo
4.2. Download The .template And .jigdo Files
4.3. Run jigdo-lite
4.4. Specify A Mirror
4.5. Downloading Of The ISO
5. Updating Your Image
6. Frequently Asked Questions
6.1. Why does jidgo ask twice for scanning for existing files? Is it enough to say yes once ?
6.2. Jigdo Has Problems Downloading Certain Filenames.
6.3. How do I make jigdo use my proxy?
6.4. Jigdo-lite fails with an error - have I downloaded all those MBs in vain?
6.5. [11 Aug 2002]: Why aren't the translations of this HOWTO on LDP?
6.6. What do I do if my jigdo download gets interrupted?
6.7. My jigdo download won't complete because the .jigdo file is broken. When I download a new, fixed .jigdo file, do I need to download all the data over again?
6.8. Can I use jigdo to download images for DVD?
6.9. Can I burn the .iso.tmp file to CD?
6.10. Jigdo-lite is broken! It downloads packages and deletes them. I know it doesn't write them to the iso.tmp file because the file size doesn't change!
6.11. I'm having trouble getting jigdo-easy to work.
6.12. For image updates, I want jigdo-lite to scan 14 loop-mounted images in one go. How can I do this?
6.13. Jigdo-lite is too verbose. How can I supress some or all of its messages?
6.14. Can I use jigdo on platforms other than Linux?
6.15. On MS Windows, why do I get a "No such file or directory" error message?
6.16. On MS Windows, why won't my image grow larger than 2GB?
6.17. On MS Windows, jigdo-lite.bat fails with an error message saying "sh" was not found.
6.18. Can I run multiple instances of jigdo-lite to download images in parallel?
6.19. Is there a GUI interface available?
7. Errata
7.1. jigdo-easy
7.2. More About Scan Sources
7.3. jigdo-file-cache.db
7.4. Resources

1. Administrata

1.1. Authorship and Copyright

This document is copyright (c) 2001 Peter Jay Salzman, <p@dirac.orgZZZ>. Permission is granted to copy, distribute and/or modify this document under the terms of the Open Software License (OSL), version 1.1. I hate HOWTO's that include the license; it's a tree killer. You can read the OSL at http://opensource.org/licenses/osl-1.1.txt.

If you want to create a derivative work or publish this HOWTO for commercial purposes, I'd appreciate it if you contact me first. This will give me a chance to give you the most recent version. I'd also appreciate either a copy of whatever it is you're doing or a spinach, garlic, mushroom, feta cheese and artichoke heart pizza.


1.2. Acknowledgements

I would like to thank the author of jigdo, Richard Atterer, simply for writing jigdo. Anyone who has obtained Debian ISOs by other means will know why. This HOWTO started out as some webpages I wrote about my experience with jigdo. Richard took the time to email me extensive corrections, clarifications and answers to questions I had about jigdo. Since then, he has read my work many times. Richard is a developer who not only cares about his work, but also about the people who use it. Sadly, this is becoming less common in this busy world we live in. Thanks, Richard!

I'd also like to thank Conrad Wood, Elcio Mello, Marcelo Ramos, Yufeng Wang, Tsukasa Yamaguchi, Yuri Kozlov, and Oguz Yarimtepe for translating this mini-HOWTO into languages other than English. I feel totally honored that they have found my words worthy of their time and effort. Thanks, guys!

Lastly, I'd like to thank Mark van Lent, Gordon Huff, David Anselmi, Thierry Cabuzel, Russell L. Harris, and Jens Seidel for kind words and corrections.


1.3. Comments and Corrections

I care a great deal about the people who use this document. Even mini-HOWTOs take a long time to write, and I wouldn't have invested so much effort into something people don't understand. If you have comments, corrections or suggestions, even in matters like writing style, don't hesitate to email me. As long as I'm not totally swamped by my PhD dissertation and the book I'm writing on debugging code with GDB/DDD for No Starch Press, I'll do my best to respond to each email I receive about this mini-HOWTO. News flash: I've completed my Ph.D.; now I'm swamped with job hunting. Does anyone need to hire a theoretical physicist?


1.4. Latest Version And Translations

German:

Conrad Wood <cnw@conradwood.netZZZ>.

Portuguese

Elcio Mello.

Spanish

Marcelo Ramos <mramos@montevideo.com.uyZZZ>.

Chinese

Yufeng Wang

Japanese

Tsukasa Yamaguchi. Available at http://www.linux.or.jp/JF/JFdocs/Debian-Jigdo.

Russian

Yuri Kozlov <kozlov.y@gmail.comZZZ>. Available at http://alioth.debian.org/project/showfiles.php?group_id=30279.

Turkish

Oguz Yarimtepe <oguzy@comu.edu.trZZZ>. Available at http://docs.comu.edu.tr/howto/debian-jigdo.html.

In addition to the URLs given above, all the translations (as well as the English version) are available at my website: http://www.dirac.org/linux/debian/jigdo. If you'd like to translate this mini-HOWTO to another language, please contact me at <p@dirac.orgZZZ>.

The English version of this HOWTO can also be found at The Linux Documentation Project: http://tldp.org/docs.html.


2. Why jigdo?

2.1. How Does One Get A Debian ISO Image Set?

If you want a set of Debian CDs there are many ways of getting them. One way is to buy them from vendors who sell Debian CDs. This definitely has merit since some of the vendors donate money back to the Debian project. Your donations help make sure that Debian is around for a long time.

Another way of getting a set of Debian CDs is to burn your own set. This first entails obtaining an ISO image and then burning that ISO image to a blank CD. Before jigdo, there were two ways of creating Debian CDs:

  1. Downloading the entire ISO

  2. Using the pseudo-image kit (PIK)

This document is about the newer and better way of obtaining Debian ISO images, using a tool called jigdo. In fact, the PIK is now officially dead and all further references to it have been removed from this document. The canonical method of getting Debian ISO images is with jigdo.


2.2. Why Not Download The Whole ISO Image?

There are mirrors which offer http and ftp downloads of Debian ISOs. The problem is that there are very few mirror sites, and their bandwidth can't support everyone who wants Debian ISOs. For example, fsn.hu has reportedly saturated the connection of its provider. The outgoing traffic reaches a few terabytes per month!

In addition, Debian testing and unstable get updated often. Your ISOs may become outdated the same day you download them unless you find some sneaky way of updating them like mounting the ISO on a loopback device and using rsync (which is what the PIK did). So if you want up-to-date ISO images, you must download a new set of ISO images every day. Clearly, this is not the way you want to obtain Debian ISOs!

Even if you want to download the stable ISO images, they still get updated every few months. Downloading the ISO images will give you up-to-date images for a few months, but every time a new revision of Debian stable is released, you'll need to go through the painful process of downloading the entire ISO set from scratch. This is not a good use of your time and the mirror's resources.


2.3. What Is Jigdo?

Jigdo (which stands for "Jigsaw Download") was written by Richard Atterer and is released under the GNU GPL. It's a tool that allows efficient downloading and updating of an ISO image. Any ISO image. Jigdo is not Debian specific, however Debian has chosen it to be the official method of downloading ISO images.

A common misconception is that jigdo creates ISO images; it doesn't. Let's discuss the overall process of how jigdo allows you to obtain an ISO image. Let Adam (a Debian release manager) be the person offering the ISO image. Let Betty (a Debian user) be the person who wants to download the ISO image.

  1. Adam first creates an ISO image suitable for burning a CD. He might use a utility like mkisofs or debian-cd to create the ISO image. He also creates two small files associated with his newly created image: a .jigdo file and a .template file. He makes these two files available for download to anyone who wants to obtain his ISO image.

  2. Betty then downloads the .jigdo and .template files. She uses jigdo-lite along with these two files to download Adam's ISO image.

  3. When Debian gets updated, Adam creates a new version of the ISO and generates new .jigdo and .template files.

  4. When Betty wants to update her CDs, she downloads the new .jigdo and .template files and uses them with jigdo-light to update her copy of the ISO images. The important thing here is that she only downloads the differences between her old ISO and Adam's new ISO. She does not have to re-download the parts that are unchanged.

Jigdo comes with two utilities: jigdo-file (used by Adam) which creates the .jigdo and .template files, and jigdo-lite (used by Betty) which uses these two files to download or update the ISO. If all you want to do is obtain/update Debian ISOs, you'll only use jigdo-lite. You can forget that jigdo-file even exists.   :-)

Jigdo addresses all the problems with the other methods of obtaining Debian ISO images:

  • It's much faster than downloading the entire ISO image.

  • Unlike downloading the entire ISO image, it can take an outdated CD (or a loop mounted outdated ISO image), download only the files that have changed since the CD (or ISO image) was created and create a new updated ISO. Very similar to how you use cvs to update source code.

  • jigdo-lite uses wget which, by default, uses http to transfer files. Unlike rsync, http is never blocked by firewalls (except the ones behind which you shouldn't be using jigdo to begin with).

  • Jigdo is very kind to the bandwidth of the servers offering the Debian images. The Debian mirrors can handle a bigger load of people using jigdo to download Debian images than with other methods of getting them.

Clearly, jigdo is the best method of obtaining Debian ISO images.


3. How Jigdo Works (optional)

You don't need to know this material to download Debian ISOs, but it may help demystify how jigdo works. If you're not interested in the details, simply fast forward to Section 4, "How Do I Use Jigdo".


3.1. Preparing The ISO For Download

A CD image is a filesystem called iso9660, but for this discussion, we can safely talk about a CD image as being a big file called an "ISO image" (about 650MB) that contains files at various offsets. For instance, if a CD contains a 567 byte file named README, the ISO image might contain the README file's contents between offsets 20480000 and 20480567. You can visualize a CD image as:

                    --------------------------------------------------------
      ISO Image:    |xxxx| file-0 |xx| file-1 |xxx| file-2 |x| file-3 |xxxx|
                    --------------------------------------------------------
      

The "x" areas of the image contain things like directory information, zero padding, disk name, boot block, etc.

jigdo-file takes two things as input: the complete CD image (so the ISO already needs to have been made) and a set of files which may or may not be in the image. Here's a visualization of jigdo-file's input:

                    --------------------------------------------------------
      ISO Image:    |xxxx| file-0 |xx| file-1 |xxx| file-2 |x| file-3 |xxxx|
                    --------------------------------------------------------

                         ----------  ----------              ----------    ----------
      Loose Files:       | file-0 |  | file-1 |              | file-3 |    | file-4 |
                         ----------  ----------              ----------    ----------
      

Through magic, jigdo-file finds out which of the loose files are contained in the ISO image and their offsets within the ISO file. It outputs two files: a ".template" file and a ".jigdo" file.


3.2. The .template File

Given an input of an ISO image and a set of files which may or may not be in the ISO image, jigdo-file outputs a .template file for that ISO image. Here's what the .template file looks like:

                    --------------------------------------------------------
      .template:    |xxxx| md5-0  |xx| md5-1  |xxx|cccccccc|x| md5-3  |xxxx|
                    --------------------------------------------------------
      

jigdo-file found that the files file-0, file-1 and file-3 were contained in the ISO image. It removed the contents of the these files and replaced them with each file's md5 checksum (the md5-0, md5-1, etc).

The "x" data (directory information, zero padding, etc) within the ISO image is compressed and written to the .template file. Finally, any files within the ISO image that weren't supplied as loose files (like file-2) are also compressed and written to the .template file. This is shown as "c" data in the .template file visualization.

Loose files which were supplied to jigdo-file that aren't found in the ISO image (like file-4) are ignored.


3.3. The .jigdo File

Given an input of an ISO image and a set of loose files which may or may not be in the ISO image, jigdo-file outputs a .jigdo file for that ISO image. The Debian .jigdo files are gzipped, so you need to use zcat or zless to view them. Here's what a .jigdo file looks like when you gunzip it:

      md5-0=http://somemirror.org/file-0
      md5-1=http://somemirror.org/file-1
      md5-2=http://somemirror.org/file-2
      md5-3=http://somemirror.org/file-3
      

The .jigdo file simply provides a mapping between the md5sum of a file within the ISO image and the download URL of that file. There are some other things within the .jigdo file, and if you look through it, you'll see the .jigdo file has the same format as a ".ini" file. It should be self explanatory, but if you want the nitty-gritty details, see the jigdo documentation.

The format shown above is not quite what you'd see in a typical .jigdo file, but it's very similar. If you look at the [Servers] section at the bottom of the .jigdo file, you'll see exactly what the difference is between what I showed above and an actual .jigdo file.


3.4. Downloading The Image

Once you use jigdo-file to generate a .jigdo and .template file for an ISO image, anyone can use jigdo-lite to download that image. jigdo-lite downloads all the files of a Debian ISO using wget, assembles them and forms a copy of the original ISO image on the fly.


4. Downloading Your First Image (In 5 Easy Steps)

We'll assume that you're starting from scratch and don't have any Debian ISOs on hand. Once you burn your set of ISOs, you can use jigdo-lite later to update them. We'll cover updating your ISOs in the next section.


4.1. Install Jigdo

First install the jigdo-file package:

      # apt-get install jigdo-file
      

Jigdo is under aggressive development. Bug fixes and enhancements are constant, so if you're using stable or testing, download jigdo-file from unstable at http://packages.debian.org/unstable/utils/jigdo-file.html. As of 28 Nov 2005 it's at version 0.7.2-2.


4.2. Download The .template And .jigdo Files

For each ISO image you want to download, you'll need both the .jigdo and .template file for that image. Both files follow the same naming convention:

      distro-arch-n.jigdo
      distro-arch-n.template
      

where distro is the name of the distro (like "sarge"), arch is the architecture (like "i386") and n is the disk number (like "1").

For example, sarge has 8 images, so you need to download 8 .jigdo files and 8 .template files. They can be downloaded from http://www.debian.org/CD/jigdo-cd/. The first .jigdo and .template file are named sarge-i386-1.jigdo and sarge-i386-1.template respectively.


4.3. Run jigdo-lite

Run jigdo-lite and give it the .jigdo file of the image you want to download. Using Sarge as an example:

      lucifer$ ls
      sarge-i386-1.jigdo  sarge-i386-1.template
      lucifer$ jigdo-lite sarge-i386-1.jigdo 
      
      Jigsaw Download "lite"
      Copyright 2001-2003 by Richard Atterer <jigdo@atterer.net>
      Getting mirror information from /etc/apt/sources.list
      
      -----------------------------------------------------------------
      Images offered by `sarge-i386-1.jigdo':
        1: 'Debian GNU/Linux testing "Sarge"
               - Official Snapshot i386 Binary-1 CD' (sarge-i386-1.iso)
      
      Further information about `sarge-i386-1.iso':
      Generated on Fri, 7 Feb 2003 20:31:28 -0700
      
      -----------------------------------------------------------------
      If you already have a previous version of the CD you are
      downloading, jigdo can re-use files on the old CD that are also
      present in the new image, and you do not need to download them
      again. Mount the old CD ROM and enter the path it is mounted under
      (e.g. `/mnt/cdrom').
      Alternatively, just press enter if you want to start downloading
      the remaining files.
      Files to scan:
      

If you suspended jigdo-lite with control+z (don't do this; I'll tell you what you'd see) and looked at the output of ls, you'd find a new file in the directory named sarge-i386-1.jigdo.unpacked. It turns out that .jigdo files are gzip'ed. This file is simply a gunzip'ed version of the .jigdo file.

Right now, jigdo-lite is telling us that if we have an outdated version of first CD of sarge, we should give the pathname to the CD. This is how you update your ISO images (or complete your incomplete downloads). Since we're assuming that you're starting from scratch and have no Debian ISOs yet, we have nothing to scan. We'll cover this in Section 5, so just press ENTER.

See also Section 7.2, "More About Scan Sources".


4.4. Specify A Mirror

You'll see:

      -----------------------------------------------------------------
      The jigdo file refers to files stored on Debian mirrors. Please
      choose a Debian mirror as follows: Either enter a complete URL
      pointing to a mirror (in the form
      `ftp://ftp.debian.org/debian/'), or enter any regular expression
      for searching through the list of mirrors: Try a two-letter
      country code such as `de', or a country name like `United
      States', or a server name like `sunsite'.
      Debian mirror [http://linux.csua.berkeley.edu/debian/]: 
			

By default, jigdo-lite pulls the mirror from your /etc/apt/sources.list. If you want to use a different mirror, you would specify a different mirror here. If this is the mirror you want to use, press ENTER. Jigdo-lite will then write a .jigdo-lite file in your home directory.

Next, if the .jigdo file you're using references a package which needs to be downloaded from a Non-US server, jigdo-lite will prompt you for a Debian Non-US mirror. The message displayed (and your response) will be very similar to the mirror dialog in the previous paragraph.

      -----------------------------------------------------------------
      The jigdo file also refers to the Non-US section of the Debian
      archive. Please repeat the mirror selection for Non-US. Do not
      simply copy the URL you entered above; this does not work because
      the path on the servers differs!
      Debian non-US mirror [http://linux.csua.berkeley.edu/debian-non-US//]: 
			

Jigdo-lite will write your choice to ~/.jigdo-lite. However, if the image you're about to download doesn't contain Non-US software you won't see this dialog.

If you want to change the default mirrors you use with jigdo at any time in the future, you can modify these two lines in ~/.jigdo-lite:

      debianMirror='http://some-mirror-to-use/debian/'
      nonusMirror='http://some-other-mirror/debian-non-US/'
      

4.5. Downloading Of The ISO

After you specify the mirror(s), jigdo-lite will begin downloading files to assemble the ISO image:

      Not downloading .template file - `sarge-i386-1.template' already present
      
      -----------------------------------------------------------------
      Merging parts from `file:' URIs, if any...
      Found 0 of the 826 files required by the template
      Will not create image or temporary file - try again with different input files
      --09:35:12--  http://mirror/debian/pool/main/p/pack/pack_3.10-1_i386.deb
          => `sarge-i386-1.iso.tmpdir/mirror/debian/pool/main/p/pack/pack_3.10-1_i386.deb
      Resolving linux.csua.berkeley.edu... done.
      Connecting to linux.csua.berkeley.edu[128.32.112.231]:80... connected.
      HTTP request sent, awaiting response... 200 OK
      Length: 1,911,624 [application/x-debian-package]
      
      19% [======>                              ] 378,304      149.87K/s    ETA 00:09
      

There'll be a lot of messages flying across your screen; if this is confusing to you, see Section 6.13. While jigdo-lite is downloading the packages, switch to another console (or open another xterm) and do an ls in the directory you're running jigdo-lite in. Now there should be 6 files in the directory:

  • sarge-i386-1.iso.list

  • sarge-i386-1.iso.tmp

  • jigdo-file-cache.db

  • sarge-i386-1.iso.tmpdir/

  • sarge-i386-1.jigdo

  • sarge-i386-1.jigdo.unpacked

  • sarge-i386-1.template

The sarge-i386-1.iso.tmpdir/ directory contains all the Debian packages that jigdo-lite downloads. Every so often, the directory gets flushed and the files get written to sarge-i386-1.iso.tmp, which is an temporarily incomplete version of the ISO image you want. Note that sarge-i386-1.iso.tmp won't appear until the first time sarge-i386-1.iso.tmpdir/ gets flushed.

jigdo-file-cache.db is a Berekeley DB file containing md5sums of any files read in when you specify a directory at the Files to scan: prompt. It's described in Section 7.3.

At this point, go play some Quake III because this will take some time (you may want to play on a different machine because jigdo is very disk intensive when it flushes files to the .iso.tmp file). At some point, the download will finish and you'll be staring at:

      FINISHED --13:32:58--
      Downloaded: 7,469,872 bytes in 9 files
      Found 9 of the 9 files required by the template                              
      Successfully created `sarge-i386-3.raw'
      
      -----------------------------------------------------------------
      Finished!
      The fact that you got this far is a strong indication that `sarge-i386-3.raw'
      was generated correctly. I will perform an additional, final check,
      which you can interrupt safely with Ctrl-C if you do not want to wait.
      
      OK: Checksums match, image is good!   
			

5. Updating Your Image

Presumably, you've read the last section, followed the instructions, burned your newly created ISO files onto CD and are feeling warm and fuzzy. Sooner or later, some packages will get updated and now you want to donate your old CDs to some newbie at your local LUG's installfest and burn yourself a set of updated CDs. Since you're well on the way to becoming a jigdo-guru, we won't go into as much painful detail as we did in the last section.

The first step is to download the .jigdo and .template files, again, for the images you want to update. You may wonder why you need to download them a second time. The reason is because the updated image you want to download has changed. Files may have been added or deleted, but even if not, any updated packages or files will have a different checksum from the checksum listed in the .jigdo and .template files you used when you first downloaded the images.

At this point, you're either holding an outdated Debian CD in your hand or you have the CD's outdated ISO image on your hard drive. Let's go through the steps of getting an updated ISO file. If you have a CD, put it in your CD drive and mount it:

      $ mount /cdrom
	

On the other hand, if you have an ISO file you'd like to update, mount it as a loop device (you may need to be root to do this). Using Woody as an example:

      # mount -o loop woody-i386-1.iso /mnt
	

Now run jigdo-lite with the .jigdo file as an argument.

      $ jigdo-lite woody-i386-1.jigdo 
      
      -----------------------------------------------------------------
      Jigsaw Download "lite"
      Copyright 2001-2002 by Richard Atterer <jigdo@atterer.net>
      Loading settings from `/home/p/.jigdo-lite'
      
      -----------------------------------------------------------------
      Images offered by `woody-i386-1.jigdo':
        1: Debian GNU/Linux 3.0 r0 Woody
             - Official i386 Binary-1 CD (debian-30r0-i386-binary-1.iso)
      
      Further information about `debian-30r0-i386-binary-1.iso':
      Generated on Thu, 18 Jul 2002 14:34:12 +0100
      
      -----------------------------------------------------------------
      If you already have a previous version of the CD you are
      downloading, jigdo can re-use files on the old CD that are also
      present on the new image, and you do not need to download them
      again.  You found the secret message; you're a very careful
      reader.  Mount the old CD ROM and enter the path it is mounted
      under (e.g. `/mnt/cdrom'). Alternatively, just press enter if you
      want to start the download of any remaining files.
      
      You can also enter a single digit from the list below to
      select the respective entry for scanning:
        1: /mnt
      Files to scan:
	

jigdo-lite is asking us to give it the location of your mounted CD (if you're updating a CD) or your loop mounted ISO file (if you're using the ISO file). I'm using an ISO file loop mounted on /mnt, so I'll enter /mnt. If you're updating a CD, enter the mount directory of your CD, which is most likely /cdrom. In either case, jigdo-lite will scan the directory of your mounted media, determine which files need updating and re-use the files which don't need updating. See also Section 7.2, "More About Scan Sources".

You may see something like:

      Files to scan: /mnt/other
      
      Not downloading .template file - `woody-i386-1.template' already present
      jigdo-file: Output file `debian-30r0-i386-binary-1.iso' already exists - delete
      it or use --force
      jigdo-file failed with code 3 - aborting.
	

What happened? Actually, I wanted to show you this because you'll bump into it sooner or later. I'm updating an ISO file, but the outdated image file is in the same directory I'm working in. Jigdo-lite wants to generate a file called woody-i386-1.iso but there's already a file by that name in the current directory (the outdated image). Jigdo-lite doesn't want to destroy that file, so it bails and lets me know that I can either delete that file or use --force to overwrite the file. You could also rename or move the file too, but I guess jigdo-lite assumes we already know this.   :-)

Don't be timid about moving or renaming the image file just because it's loop mounted. The filesystem uses inodes under the hood, and even if you move or rename the file, the inode stays the same. You won't hurt the filesystem mounted under /mnt. As for deleting the ISO file, that won't hurt the mounted filesystem either. A file's inode gets deallocated only when the inode's reference count drops to zero. Mounting the ISO image bumps the reference count up, so the file really gets deleted only after you rm the file and umount the loop device. All you people who are updating the CD don't have to worry about any of this. :-)

I'll rename the ISO file to woody-i386-1.iso.old and run jigdo-lite again. Let's try again:

      $ jigdo-lite woody-i386-1.jigdo
      
      -----------------------------------------------------------------
      Jigsaw Download "lite"
      Copyright 2001-2002 by Richard Atterer <jigdo@atterer.net>
      Loading settings from `/home/p/.jigdo-lite'
      
      -----------------------------------------------------------------
      Images offered by `woody-i386-1.jigdo':
        1: Debian GNU/Linux 3.0 r0 Woody - Official i386 Binary-1 CD
             (debian-30r0-i386-binary-1.iso)

      Further information about `debian-30r0-i386-binary-1.iso':
      Generated on Thu, 18 Jul 2002 14:34:12 +0100
      
      -----------------------------------------------------------------
      If you already have a previous version of the image you are
      downloading, jigdo can re-use files on the old image that are also
      present on the new image, and you do not need to download them
      again. Mount the old CD ROM and enter the path it is mounted under
      (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want
      to start the download of any remaining files.
      You can also enter a single digit from the list below to
      select the respective entry for scanning:
        1: /mnt
      Files to scan: /mnt
      Not downloading .template file - `woody-i386-1.template' already present
      ...
      Found 1200 of the 1224 files required by the template                          
      ...

jigdo-lite remembers that I wanted to scan /mnt and tells me I can either type 1 to scan that directory or type the directory in again. Since I'm a perverse person, I type the name of the directory again.

The ellipsis represent some text that changes rapidly. The first ellipsis is a dynamic list of what files jigdo-lite is scanning. The second ellipses denotes progress in writing woody-i386-1.iso.tmp. Once jigdo-lite finishes scanning the files and writing the temporary ISO file, it prints:

      Copied input files to temporary file `woody-i386-1.iso.tmp'
         - repeat command and supply more files to continue
      
      -----------------------------------------------------------------
      If you already have a previous version of the image you are
      downloading, jigdo can re-use files on the old image that are also
      present on the new image, and you do not need to download them
      again. Mount the old CD ROM and enter the path it is mounted under
      (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want
      to start the download of any remaining files.
      You can also enter a single digit from the list below to
      select the respective entry for scanning:
        1: /mnt
      Files to scan: 
	

Since you normally don't have another source of files to scan other than your loop mounted ISO file (or your CD), press ENTER. Jigdo-lite will then ask you about which mirrors you want to use, just like it did when you downloaded your ISO for the first time. You've already answered these questions before, but if you truly don't remember, you might want to re-read Section 4.4.

At this point, you'll see jigdo-lite working its magic. Now wasn't that easy?


6. Frequently Asked Questions

Questions prepended with a date indicate a time sensitive question (a question that relates to a temporary situation). If you see one of these questions and know that the temporary situation has changed, please contact me and let me know so I can remove the question from the mini-HOWTO.


6.1. Why does jidgo ask twice for scanning for existing files? Is it enough to say yes once ?

It keeps asking this as long as you enter a path to scan. The idea is that you may want to scan several old CDs, so you can insert one after the other into the drive and keep supplying the path "D:\" (or whatever). See also Section 7.2, "More About Scan Sources".


6.2. Jigdo Has Problems Downloading Certain Filenames.

When downloading Debian images under Windows, jigdo-lite may appear to have trouble downloading one or more of the following files:

      libbusiness-onlinepayment-bankofamerica-perl_xxx_all.deb
      libbusiness-onlinepayment-authorizenet-perl_xxx_all.deb
      libbusiness-onlinepayment-payconnect-perl_xxx_all.deb
      libmasonx-request-withapachesession-perl_xxx_all.deb
      libtemplate-plugin-calendar-simple-perl_xxx_all.deb
      

Move the jigdo download directory up by as many directories as possible, closer to the drives's root directory.

The NTFS filesystem has a 255 character limit on a file's pathname. When jigdo-lite downloads files from the internet, it makes a copy of the server directory structure in its download directory. With their very long names, the above Debian packages may exceed the allowed path length, which leads to error messages like "Cannot write to `[very long pathname]' (No such file or directory)".

Some people may now wonder: Why does jigdo-lite use wget's "--force-directories" switch, which creates these problematic directory hierarchies?

Early versions of jigdo-lite did not use it, but then some folks requested that jigdo-lite always use the "--continue" switch to avoid half-downloaded .deb files being ignored and deleted when you interrupt and restart jigdo-lite.

Unfortunately, it turned out that this led to problems: The Debian servers contained several identically named files (e.g. "root.bin") in different directories, and if you interrupted jigdo-lite at roughly the right time during the download, the chances were high that the resumed download would append data to the wrong half-downloaded file, corrupting it and making the entire jigdo download fail.


6.3. How do I make jigdo use my proxy?

Edit ~/.jigdo-lite (or jigdo-lite-settings.txt for the Microsoft Windows version) into a text editor and find the line that starts with "wgetOpts". The following switches can be added to that line:

      -e ftp_proxy=http://LOCAL-PROXY:PORT/
      -e http_proxy=http://LOCAL-PROXY:PORT/
      --proxy-user=USER
      --proxy-passwd=PASSWORD
      

Of course, substitute the correct values for your proxy server. The last two options are only necessary if your proxy uses password authentication. The switches need to be added to the end of the wgetOpts line before the final ' character. All options must be on one line.

Alternatively, under Linux you can also set up the ftp_proxy and http_proxy environment variables, for example in the file /etc/environment or ~/.bashrc.


6.4. Jigdo-lite fails with an error - have I downloaded all those MBs in vain?

If jigdo-file aborts after downloading a considerable chunk of the ISO contents, you'll have a large ".iso.tmp" file. There are several things to try to salvage your download:

  • Restart the download by pressing RETURN. Maybe some of the files could not be downloaded because of timeouts or other transient errors. Try to download the missing files again.

  • Try a different mirror. Some Debian mirrors are slightly out of sync -- maybe a different mirror still holds files that were deleted from the one you specified, or it has already been updated with files that are not yet present on your mirror. This has happened quite a few times with me.

  • Retrieve the missing parts of the image using rsync. First, you need to find out the correct rsync URL of the image you are downloading: Choose a server that offers rsync access to the stable or testing images, then determine the correct path and filename. Directory listings can be obtained with commands like rsync rsync://cdimage.debian.org/debian-cd/.

    Next, remove the ".tmp" extension from jigdo-lite's temporary file by renaming it, and pass both the remote URL and the local filename to rsync: rsync rsync://server.org/path/binary-i386-1.iso binary-i386-1.iso You may want to use rsync's --verbose and --progress switches to get status messages, and --block-size=8192 to increase its speed.

  • Under Linux, you can loop-mount the .tmp file to access the packages that were already downloaded, and reuse them for generating an image from a newer .jigdo file. To do this, first issue the following commands as root in the directory with the broken download: mkdir mnt; mount -t iso9660 -o loop *.tmp mnt. Next, start a new download in a different directory, and enter the path of the mnt directory at the "Files to scan" prompt.

    Under Microsoft Windows you can do the same thing by loop mounting the temporary ISO image using "virtual drive" software. Daemon tools and Nero Image Drive are both very popular. See also http://tinyurl.com/c39zr for more options.


6.5. [11 Aug 2002]: Why aren't the translations of this HOWTO on LDP?

I've been having trouble getting the translations of this HOWTO submitted to the non-English LDP editors.

The German LDP editor, Marco Budde <Budde@tu-harburg.de> refuses to accept the German translation because it was written in Docbook and not Linuxdoc, even though Docbook is the preferred SGML language for the LDP. It's a shame that we have people within the open source community who would sabotage our community from the inside.

The Portuguese LDP editor, Alfredo Carvalho <ajpc@poli.org>, has completely ignored my submission of the Portuguese translation.

If you care about having LDP documents in these languages, I urge you to write to these editors and ask them to please be more responsible about accepting translated documents. For the time being, you can download these translations from my personal website, http://www.dirac.org/linux/debian/jigdo.

Shame on you, Marco Budde <Budde@tu-harburg.de>.

Shame on you, Alfredo Carvalho <ajpc@poli.org>.


6.6. What do I do if my jigdo download gets interrupted?

If your download gets interrupted, all you need to do is restart jigdo-lite and hit ENTER at all the question prompts. Jigdo-lite will pick up where it left off.


6.7. My jigdo download won't complete because the .jigdo file is broken. When I download a new, fixed .jigdo file, do I need to download all the data over again?

You may find that the .jigdo file you downloaded is broken. It's uncommon, but it does happen from time to time with moving targets like Debian testing or unstable.

If you find that .jigdo is broken, you'll need to download a new .jigdo file (when a fixed one becomes available), but you won't need to download all the ISO data again.

You can use the same loop mounting trick we use when updating an ISO image. The difference is that there's no finished .iso file to start with, but the .iso.tmp file is an ISO image too and can be used to finish the download without having to re-download all the data that was downloaded before the broken .jigdo file caused jigdo-lite to halt. Simply loop mount the .iso.tmp file on /mnt and when you re-run jigdo-lite with the fixed .jigdo file, tell jigdo-lite to scan /mnt. Don't forget to rename or move the .iso.tmp file so it doesn't interfere with jigdo-lite which will want to create a new .iso.tmp file.


6.8. Can I use jigdo to download images for DVD?

Absolutely; the process is identical to downloading CD images. The only thing you need to do differently is to download the .jigdo and .template files for DVDs instead of CDs. You can find the DVD .jigdo and .template files at http://www.debian.org/CD/jigdo-cd/.

On Linux, you need kernel 2.4 or later to create DVD-sized files.

Under MS Windows, you need to use jigdo-win-0.7.1a (released 21 July 2004) or later to create DVD-sized images. This is because of a bug in the large file support of Mingw32, the compiler used to create the MS Windows executables. The bug got fixed on this date, and jigdo-win-0.7.1a was released.


6.9. Can I burn the .iso.tmp file to CD?

Thanks to Gordon Huff and David Anselmi, we now know the answer is "yes you can". But more importantly, Gordon gave a good reason why you'd want to do this in the first place. Paraphrasing Gordon:

My friend's Win98 has a *nice* cable connection. I arrive in the morning, start jigdo (more than one, actually) and then we go to the store, tie back the kiwi plant, put up the Christmas lights and Christmas tree, trim the tree, order and split a pizza and fire up the satellite dish.

I leave my friends place with several iso.tmp's on CDRWs. When I get home, I use the iso's that didn't finish to update my jigdo setup at home which is a dial-up.


6.10. Jigdo-lite is broken! It downloads packages and deletes them. I know it doesn't write them to the iso.tmp file because the file size doesn't change!

Jigdo works just fine -- the .iso.tmp file is created at the beginning with its final size, but filled with zero bytes. Later, parts of it are overwritten with the downloaded data.

You can tell that jigdo is making progress by looking at the messages "Found X of the Y files required by the template" that are printed from time to time. The first value "X" should increase. When X equals Y, the download is finished.


6.12. For image updates, I want jigdo-lite to scan 14 loop-mounted images in one go. How can I do this?

When updating CD images, it's tiresome to keep loop-mounting and unmounting images. However, by default the Linux kernel only supports eight loop devices, and jigdo-lite's menu of previously entered paths only has five entries.

To scan many loop-mounted images, you must first tell the Linux kernel to support more than the default eight devices. This is done by giving the "max_loop" parameter to the module when loading it, e.g. with "modprobe loop max_loop=16" on the command line or by adding the line "options loop max_loop=16" to /etc/modules.conf. In Debian, you must put this line into a file named e.g. /etc/modutils/local-loop and then run update-modules because direct changes to /etc/modules.conf will be overwritten.

Having mounted the individual images, you can pass the parent directory of their mount points to jigdo-lite for scanning. For example, if the images are mounted under /mnt/myloopmounts/image1/ etc., pass "/mnt/myloopmounts" as the path to scan. If passing the parent directory is inconvenient, you can also create a directory and fill it with symlinks to the mount points.


6.13. Jigdo-lite is too verbose. How can I supress some or all of its messages?

Jigdo-lite uses wget, and wget's output can be quite verbose. If this is unsettling, you can make wget more quiet by adding --non-verbose to the wgetOpts switch in your ~/.jigdo-lite file. If you want wget to print no messages at all, use --quiet in the wgetOpts switch.


6.14. Can I use jigdo on platforms other than Linux?

Certainly. If you're interested in Potato or Woody under Microsoft Windows, old SunOS, HP-UX and IRIX you can use jigdo-easy. See Section 7.1 and Section 7.4.

If you want to download Potato, Woody, Sarge or Sid under Microsoft Windows, jigdo-lite has been ported to that platform and can be downloaded from the main jigdo site (Section 7.4).


6.15. On MS Windows, why do I get a "No such file or directory" error message?

You might find that under MS Windows, jigdo-lite will download some files but then fail to read their contents, which will produce a "No such file or directory" error message.

It seems that this occurs if the length of the filenames that jigdo processes exceeds a certain limit. The solution is to move the half-finished download up in the directory hierarchy, closer to the top-level directory of the drive.


6.16. On MS Windows, why won't my image grow larger than 2GB?

You're using an old version of jigdo. Please upgrade to jigdo-win-0.7.1a or newer. See Section 6.8.


6.17. On MS Windows, jigdo-lite.bat fails with an error message saying "sh" was not found.

This means that the PATH command in the .bat file failed. For some reason, this is the case if you unpacked jigdo on a Windows network share using a path like "\\SomeServer\Files\jigdo". Solution: Use "Map network drive" (in the explorer "tools" menu) to assign a drive letter like "Z:", then double-click on the .bat file inside "Z:\jigdo". Alternatively, a workaround is to move everything in the jigdo-bin subdirectory up to where the .bat file is.


6.18. Can I run multiple instances of jigdo-lite to download images in parallel?

Absolutely. However, to avoid filename clashing, you should run each jigdo-lite instance in its own separate directory. You can start as many instances as you want, go to bed, and when you wake up, all the ISO images will be waiting for you on your hard drive. Be aware that jigdo-lite is bandwidth and CPU intensive, so you won't want to use your computer with multiple instances running in tandem.


6.19. Is there a GUI interface available?

A GTK+ interface to jigdo is being worked on. Both Linux and Microsoft Windows GUI clients are planned. Unfortunately, it's been 80% done for over 1.5 years, so don't hold your breath for its release.


7. Errata

7.1. jigdo-easy

Jigdo-easy, by Anne Bezemer, is a fork of jigdo-lite which is portable to a wider range of systems, including Microsoft Windows, old SunOS, HP-UX and IRIX). It's also easier to use than jigdo-lite but because of changes made to Jigdo, will only work with Potato and Woody. Jigdo-easy will not be able to download Sarge and Sid. See Section 7.4 and Section 6.14.


7.2. More About Scan Sources

By now you know that when jigdo-lite asks for files to scan, you can use 3 sources:

  • A mounted copy of an outdated CD or DVD that you wish to update.

  • A loop-mounted copy of an outdated ISO image file on your hard drive.

  • A loop-mounted copy of the temporary .iso.tmp file, when a previous jigdo-lite run aborted.

As Jens Seidel points out, there is another, rather crafty, source you should use for a scanning source: your apt cache. Apt uses the directory /var/cache/apt/archives for cache. There will be many Debian packages sitting in this directory, and they can be used for a scan source for jigdo-lite! So when you're asked for a directory to scan, by all means, use this directory too.

If you're editing the ~/.jigdo-lite file by hand, be aware that multiple scan directories are space separated, for example:

      scanMenu='/var/cache/apt/archives/ /cdrom/'
      

7.3. jigdo-file-cache.db

The cache contains the md5sums of files read when you supply a directory at the Files to scan: prompt. If you have jigdo-file scan the same directory a second time, the scan will be very fast.

This could be useful in the following case: rev0 gets updated to rev1. With the rev1 CD images, some packages may have been pushed from CD n to CD n+1, or vice versa. If you had a particularly slow link (e.g. modem), you'd try to avoid downloading these packages again. For this reason, when downloading the new version of CD n, you'd let jigdo-lite scan the three CDs n-1, n and n+1 (or even all 8 CDs if you want to be 100% sure).

If you have jigdo-lite scan the same CDs over and over again while updating each of the 8 CD images, the cache will prevent all the data on the CDs from being read multiple times.

The cache is much more important when generating jigdo files, because you don't want jigdo-file to read in your whole 50GB Debian mirror for every generated jigdo file.


7.4. Resources

This HOWTO is winding down to a close, but I thought I'd leave you with a few links and references to learn more about the jigdo tools and how they work.

http://atterer.net/jigdo

This is the jigdo home site. You should definitely browse this site; lots of information about ports, GUI clients and everything under the sun relating to jigdo.

http://cdimage.debian.org/~costar/jigdo

The Debian page for jigdo-easy (Section 7.1).

http://www.debian.org/CD/jigdo-cd

The main Debian page for jigdo.

http://packages.debian.org/testing/utils/jigdo-file.html

The official webpage for the Debian jigdo-file package.

http://lists.debian.org/search.html

You can use this page to search the debian-cd mailing list archives.

http://www.debian.org/MailingLists/subscribe

The subscription page for the debian-cd mailing list.

https://lists.berlios.de/mailman/listinfo/jigdo-user

The subscription page for the official Jigdo mailing list.

jigdo-0.7.3/doc/debian-jigdo-mini-howto.sgml0000644000175000017500000020037210431672523020473 0ustar richardrichard
Debian Jigdo mini-HOWTO DJ-HOWTO Peter Jay Salzman
p@dirac.orgZZZ
2005-12-05 ver 1.8 2001 Peter Jay Salzman p@dirac.orgZZZ / www.dirac.org/p. Distributed subject to the Open Software License, version 1.1. Abstract Getting Debian ISOs has always been a painful, slow and supremely inefficient process. Jigdo is a tool for distributing and obtaining Debian ISOs in an easy, fast and very efficient manner. This HOWTO describes why you should use jigdo, a little bit about how it works and how you use it to get and update Debian ISOs. Jigdo is a very general tool, and isn't tied specifically to Debian ISOs. The jigdo tools can be used to make any ISO available for download in the same easy, fast and efficient manner they're used for Debian ISOs. This HOWTO will cover this as well, but we'll focus primarily on downloading Debian ISOs.
Administrata Authorship and Copyright This document is copyright (c) 2001 Peter Jay Salzman, p@dirac.orgZZZ. Permission is granted to copy, distribute and/or modify this document under the terms of the Open Software License (OSL), version 1.1. I hate HOWTO's that include the license; it's a tree killer. You can read the OSL at http://opensource.org/licenses/osl-1.1.txt. If you want to create a derivative work or publish this HOWTO for commercial purposes, I'd appreciate it if you contact me first. This will give me a chance to give you the most recent version. I'd also appreciate either a copy of whatever it is you're doing or a spinach, garlic, mushroom, feta cheese and artichoke heart pizza. Acknowledgements I would like to thank the author of jigdo, Richard Atterer, simply for writing jigdo. Anyone who has obtained Debian ISOs by other means will know why. This HOWTO started out as some webpages I wrote about my experience with jigdo. Richard took the time to email me extensive corrections, clarifications and answers to questions I had about jigdo. Since then, he has read my work many times. Richard is a developer who not only cares about his work, but also about the people who use it. Sadly, this is becoming less common in this busy world we live in. Thanks, Richard! I'd also like to thank Conrad Wood, Elcio Mello, Marcelo Ramos, Yufeng Wang, Tsukasa Yamaguchi, Yuri Kozlov, and Oguz Yarimtepe for translating this mini-HOWTO into languages other than English. I feel totally honored that they have found my words worthy of their time and effort. Thanks, guys! Lastly, I'd like to thank Mark van Lent, Gordon Huff, David Anselmi, Thierry Cabuzel, Russell L. Harris, and Jens Seidel for kind words and corrections. Comments and Corrections I care a great deal about the people who use this document. Even mini-HOWTOs take a long time to write, and I wouldn't have invested so much effort into something people don't understand. If you have comments, corrections or suggestions, even in matters like writing style, don't hesitate to email me. As long as I'm not totally swamped by my PhD dissertation and the book I'm writing on debugging code with GDB/DDD for No Starch Press, I'll do my best to respond to each email I receive about this mini-HOWTO. News flash: I've completed my Ph.D.; now I'm swamped with job hunting. Does anyone need to hire a theoretical physicist? Latest Version And Translations German: Conrad Wood cnw@conradwood.netZZZ. Portuguese Elcio Mello. Spanish Marcelo Ramos mramos@montevideo.com.uyZZZ. Chinese Yufeng Wang Japanese Tsukasa Yamaguchi. Available at http://www.linux.or.jp/JF/JFdocs/Debian-Jigdo. Russian Yuri Kozlov kozlov.y@gmail.comZZZ. Available at http://alioth.debian.org/project/showfiles.php?group_id=30279. Turkish Oguz Yarimtepe oguzy@comu.edu.trZZZ. Available at http://docs.comu.edu.tr/howto/debian-jigdo.html. In addition to the URLs given above, all the translations (as well as the English version) are available at my website: http://www.dirac.org/linux/debian/jigdo. If you'd like to translate this mini-HOWTO to another language, please contact me at p@dirac.orgZZZ. The English version of this HOWTO can also be found at The Linux Documentation Project: http://tldp.org/docs.html. Why jigdo? How Does One Get A Debian ISO Image Set? If you want a set of Debian CDs there are many ways of getting them. One way is to buy them from vendors who sell Debian CDs. This definitely has merit since some of the vendors donate money back to the Debian project. Your donations help make sure that Debian is around for a long time. Another way of getting a set of Debian CDs is to burn your own set. This first entails obtaining an ISO image and then burning that ISO image to a blank CD. Before jigdo, there were two ways of creating Debian CDs: Downloading the entire ISO Using the pseudo-image kit (PIK) This document is about the newer and better way of obtaining Debian ISO images, using a tool called jigdo. In fact, the PIK is now officially dead and all further references to it have been removed from this document. The canonical method of getting Debian ISO images is with jigdo. Why Not Download The Whole ISO Image? There are mirrors which offer http and ftp downloads of Debian ISOs. The problem is that there are very few mirror sites, and their bandwidth can't support everyone who wants Debian ISOs. For example, fsn.hu has reportedly saturated the connection of its provider. The outgoing traffic reaches a few terabytes per month! In addition, Debian testing and unstable get updated often. Your ISOs may become outdated the same day you download them unless you find some sneaky way of updating them like mounting the ISO on a loopback device and using rsync (which is what the PIK did). So if you want up-to-date ISO images, you must download a new set of ISO images every day. Clearly, this is not the way you want to obtain Debian ISOs! Even if you want to download the stable ISO images, they still get updated every few months. Downloading the ISO images will give you up-to-date images for a few months, but every time a new revision of Debian stable is released, you'll need to go through the painful process of downloading the entire ISO set from scratch. This is not a good use of your time and the mirror's resources. What Is Jigdo? Jigdo (which stands for "Jigsaw Download") was written by Richard Atterer and is released under the GNU GPL. It's a tool that allows efficient downloading and updating of an ISO image. Any ISO image. Jigdo is not Debian specific, however Debian has chosen it to be the official method of downloading ISO images. A common misconception is that jigdo creates ISO images; it doesn't. Let's discuss the overall process of how jigdo allows you to obtain an ISO image. Let Adam (a Debian release manager) be the person offering the ISO image. Let Betty (a Debian user) be the person who wants to download the ISO image. Adam first creates an ISO image suitable for burning a CD. He might use a utility like mkisofs or debian-cd to create the ISO image. He also creates two small files associated with his newly created image: a .jigdo file and a .template file. He makes these two files available for download to anyone who wants to obtain his ISO image. Betty then downloads the .jigdo and .template files. She uses jigdo-lite along with these two files to download Adam's ISO image. When Debian gets updated, Adam creates a new version of the ISO and generates new .jigdo and .template files. When Betty wants to update her CDs, she downloads the new .jigdo and .template files and uses them with jigdo-light to update her copy of the ISO images. The important thing here is that she only downloads the differences between her old ISO and Adam's new ISO. She does not have to re-download the parts that are unchanged. Jigdo comes with two utilities: jigdo-file (used by Adam) which creates the .jigdo and .template files, and jigdo-lite (used by Betty) which uses these two files to download or update the ISO. If all you want to do is obtain/update Debian ISOs, you'll only use jigdo-lite. You can forget that jigdo-file even exists.   :-) Jigdo addresses all the problems with the other methods of obtaining Debian ISO images: It's much faster than downloading the entire ISO image. Unlike downloading the entire ISO image, it can take an outdated CD (or a loop mounted outdated ISO image), download only the files that have changed since the CD (or ISO image) was created and create a new updated ISO. Very similar to how you use cvs to update source code. jigdo-lite uses wget which, by default, uses http to transfer files. Unlike rsync, http is never blocked by firewalls (except the ones behind which you shouldn't be using jigdo to begin with). Jigdo is very kind to the bandwidth of the servers offering the Debian images. The Debian mirrors can handle a bigger load of people using jigdo to download Debian images than with other methods of getting them. Clearly, jigdo is the best method of obtaining Debian ISO images. How Jigdo Works (optional) You don't need to know this material to download Debian ISOs, but it may help demystify how jigdo works. If you're not interested in the details, simply fast forward to , "How Do I Use Jigdo". Preparing The ISO For Download A CD image is a filesystem called iso9660, but for this discussion, we can safely talk about a CD image as being a big file called an "ISO image" (about 650MB) that contains files at various offsets. For instance, if a CD contains a 567 byte file named README, the ISO image might contain the README file's contents between offsets 20480000 and 20480567. You can visualize a CD image as: -------------------------------------------------------- ISO Image: |xxxx| file-0 |xx| file-1 |xxx| file-2 |x| file-3 |xxxx| -------------------------------------------------------- The "x" areas of the image contain things like directory information, zero padding, disk name, boot block, etc. jigdo-file takes two things as input: the complete CD image (so the ISO already needs to have been made) and a set of files which may or may not be in the image. Here's a visualization of jigdo-file's input: -------------------------------------------------------- ISO Image: |xxxx| file-0 |xx| file-1 |xxx| file-2 |x| file-3 |xxxx| -------------------------------------------------------- ---------- ---------- ---------- ---------- Loose Files: | file-0 | | file-1 | | file-3 | | file-4 | ---------- ---------- ---------- ---------- Through magic, jigdo-file finds out which of the loose files are contained in the ISO image and their offsets within the ISO file. It outputs two files: a ".template" file and a ".jigdo" file. The .template File Given an input of an ISO image and a set of files which may or may not be in the ISO image, jigdo-file outputs a .template file for that ISO image. Here's what the .template file looks like: -------------------------------------------------------- .template: |xxxx| md5-0 |xx| md5-1 |xxx|cccccccc|x| md5-3 |xxxx| -------------------------------------------------------- jigdo-file found that the files file-0, file-1 and file-3 were contained in the ISO image. It removed the contents of the these files and replaced them with each file's md5 checksum (the md5-0, md5-1, etc). The "x" data (directory information, zero padding, etc) within the ISO image is compressed and written to the .template file. Finally, any files within the ISO image that weren't supplied as loose files (like file-2) are also compressed and written to the .template file. This is shown as "c" data in the .template file visualization. Loose files which were supplied to jigdo-file that aren't found in the ISO image (like file-4) are ignored. The .jigdo File Given an input of an ISO image and a set of loose files which may or may not be in the ISO image, jigdo-file outputs a .jigdo file for that ISO image. The Debian .jigdo files are gzipped, so you need to use zcat or zless to view them. Here's what a .jigdo file looks like when you gunzip it: md5-0=http://somemirror.org/file-0 md5-1=http://somemirror.org/file-1 md5-2=http://somemirror.org/file-2 md5-3=http://somemirror.org/file-3 The .jigdo file simply provides a mapping between the md5sum of a file within the ISO image and the download URL of that file. There are some other things within the .jigdo file, and if you look through it, you'll see the .jigdo file has the same format as a ".ini" file. It should be self explanatory, but if you want the nitty-gritty details, see the jigdo documentation. The format shown above is not quite what you'd see in a typical .jigdo file, but it's very similar. If you look at the [Servers] section at the bottom of the .jigdo file, you'll see exactly what the difference is between what I showed above and an actual .jigdo file. Downloading The Image Once you use jigdo-file to generate a .jigdo and .template file for an ISO image, anyone can use jigdo-lite to download that image. jigdo-lite downloads all the files of a Debian ISO using wget, assembles them and forms a copy of the original ISO image on the fly. Downloading Your First Image (In 5 Easy Steps) We'll assume that you're starting from scratch and don't have any Debian ISOs on hand. Once you burn your set of ISOs, you can use jigdo-lite later to update them. We'll cover updating your ISOs in the next section. Install Jigdo First install the jigdo-file package: # apt-get install jigdo-file Jigdo is under aggressive development. Bug fixes and enhancements are constant, so if you're using stable or testing, download jigdo-file from unstable at http://packages.debian.org/unstable/utils/jigdo-file.html. As of 28 Nov 2005 it's at version 0.7.2-2. Download The .template And .jigdo Files For each ISO image you want to download, you'll need both the .jigdo and .template file for that image. Both files follow the same naming convention: distro-arch-n.jigdo distro-arch-n.template where distro is the name of the distro (like "sarge"), arch is the architecture (like "i386") and n is the disk number (like "1"). For example, sarge has 8 images, so you need to download 8 .jigdo files and 8 .template files. They can be downloaded from http://www.debian.org/CD/jigdo-cd/. The first .jigdo and .template file are named sarge-i386-1.jigdo and sarge-i386-1.template respectively. Run jigdo-lite Run jigdo-lite and give it the .jigdo file of the image you want to download. Using Sarge as an example: lucifer$ ls sarge-i386-1.jigdo sarge-i386-1.template lucifer$ jigdo-lite sarge-i386-1.jigdo Jigsaw Download "lite" Copyright 2001-2003 by Richard Atterer <jigdo@atterer.net> Getting mirror information from /etc/apt/sources.list ----------------------------------------------------------------- Images offered by `sarge-i386-1.jigdo': 1: 'Debian GNU/Linux testing "Sarge" - Official Snapshot i386 Binary-1 CD' (sarge-i386-1.iso) Further information about `sarge-i386-1.iso': Generated on Fri, 7 Feb 2003 20:31:28 -0700 ----------------------------------------------------------------- If you already have a previous version of the CD you are downloading, jigdo can re-use files on the old CD that are also present in the new image, and you do not need to download them again. Mount the old CD ROM and enter the path it is mounted under (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want to start downloading the remaining files. Files to scan: If you suspended jigdo-lite with controlz (don't do this; I'll tell you what you'd see) and looked at the output of ls, you'd find a new file in the directory named sarge-i386-1.jigdo.unpacked. It turns out that .jigdo files are gzip'ed. This file is simply a gunzip'ed version of the .jigdo file. Right now, jigdo-lite is telling us that if we have an outdated version of first CD of sarge, we should give the pathname to the CD. This is how you update your ISO images (or complete your incomplete downloads). Since we're assuming that you're starting from scratch and have no Debian ISOs yet, we have nothing to scan. We'll cover this in , so just press ENTER. See also , "More About Scan Sources". Specify A Mirror You'll see: ----------------------------------------------------------------- The jigdo file refers to files stored on Debian mirrors. Please choose a Debian mirror as follows: Either enter a complete URL pointing to a mirror (in the form `ftp://ftp.debian.org/debian/'), or enter any regular expression for searching through the list of mirrors: Try a two-letter country code such as `de', or a country name like `United States', or a server name like `sunsite'. Debian mirror [http://linux.csua.berkeley.edu/debian/]: By default, jigdo-lite pulls the mirror from your /etc/apt/sources.list. If you want to use a different mirror, you would specify a different mirror here. If this is the mirror you want to use, press ENTER. Jigdo-lite will then write a .jigdo-lite file in your home directory. Next, if the .jigdo file you're using references a package which needs to be downloaded from a Non-US server, jigdo-lite will prompt you for a Debian Non-US mirror. The message displayed (and your response) will be very similar to the mirror dialog in the previous paragraph. ----------------------------------------------------------------- The jigdo file also refers to the Non-US section of the Debian archive. Please repeat the mirror selection for Non-US. Do not simply copy the URL you entered above; this does not work because the path on the servers differs! Debian non-US mirror [http://linux.csua.berkeley.edu/debian-non-US//]: Jigdo-lite will write your choice to ~/.jigdo-lite. However, if the image you're about to download doesn't contain Non-US software you won't see this dialog. If you want to change the default mirrors you use with jigdo at any time in the future, you can modify these two lines in ~/.jigdo-lite: debianMirror='http://some-mirror-to-use/debian/' nonusMirror='http://some-other-mirror/debian-non-US/' Downloading Of The ISO After you specify the mirror(s), jigdo-lite will begin downloading files to assemble the ISO image: Not downloading .template file - `sarge-i386-1.template' already present ----------------------------------------------------------------- Merging parts from `file:' URIs, if any... Found 0 of the 826 files required by the template Will not create image or temporary file - try again with different input files --09:35:12-- http://mirror/debian/pool/main/p/pack/pack_3.10-1_i386.deb => `sarge-i386-1.iso.tmpdir/mirror/debian/pool/main/p/pack/pack_3.10-1_i386.deb Resolving linux.csua.berkeley.edu... done. Connecting to linux.csua.berkeley.edu[128.32.112.231]:80... connected. HTTP request sent, awaiting response... 200 OK Length: 1,911,624 [application/x-debian-package] 19% [======> ] 378,304 149.87K/s ETA 00:09 There'll be a lot of messages flying across your screen; if this is confusing to you, see . While jigdo-lite is downloading the packages, switch to another console (or open another xterm) and do an ls in the directory you're running jigdo-lite in. Now there should be 6 files in the directory: sarge-i386-1.iso.list sarge-i386-1.iso.tmp jigdo-file-cache.db sarge-i386-1.iso.tmpdir/ sarge-i386-1.jigdo sarge-i386-1.jigdo.unpacked sarge-i386-1.template The sarge-i386-1.iso.tmpdir/ directory contains all the Debian packages that jigdo-lite downloads. Every so often, the directory gets flushed and the files get written to sarge-i386-1.iso.tmp, which is an temporarily incomplete version of the ISO image you want. Note that sarge-i386-1.iso.tmp won't appear until the first time sarge-i386-1.iso.tmpdir/ gets flushed. jigdo-file-cache.db is a Berekeley DB file containing md5sums of any files read in when you specify a directory at the Files to scan: prompt. It's described in . At this point, go play some Quake III because this will take some time (you may want to play on a different machine because jigdo is very disk intensive when it flushes files to the .iso.tmp file). At some point, the download will finish and you'll be staring at: FINISHED --13:32:58-- Downloaded: 7,469,872 bytes in 9 files Found 9 of the 9 files required by the template Successfully created `sarge-i386-3.raw' ----------------------------------------------------------------- Finished! The fact that you got this far is a strong indication that `sarge-i386-3.raw' was generated correctly. I will perform an additional, final check, which you can interrupt safely with Ctrl-C if you do not want to wait. OK: Checksums match, image is good! Updating Your Image Presumably, you've read the last section, followed the instructions, burned your newly created ISO files onto CD and are feeling warm and fuzzy. Sooner or later, some packages will get updated and now you want to donate your old CDs to some newbie at your local LUG's installfest and burn yourself a set of updated CDs. Since you're well on the way to becoming a jigdo-guru, we won't go into as much painful detail as we did in the last section. The first step is to download the .jigdo and .template files, again, for the images you want to update. You may wonder why you need to download them a second time. The reason is because the updated image you want to download has changed. Files may have been added or deleted, but even if not, any updated packages or files will have a different checksum from the checksum listed in the .jigdo and .template files you used when you first downloaded the images. At this point, you're either holding an outdated Debian CD in your hand or you have the CD's outdated ISO image on your hard drive. Let's go through the steps of getting an updated ISO file. If you have a CD, put it in your CD drive and mount it: $ mount /cdrom On the other hand, if you have an ISO file you'd like to update, mount it as a loop device (you may need to be root to do this). Using Woody as an example: # mount -o loop woody-i386-1.iso /mnt Now run jigdo-lite with the .jigdo file as an argument. $ jigdo-lite woody-i386-1.jigdo ----------------------------------------------------------------- Jigsaw Download "lite" Copyright 2001-2002 by Richard Atterer <jigdo@atterer.net> Loading settings from `/home/p/.jigdo-lite' ----------------------------------------------------------------- Images offered by `woody-i386-1.jigdo': 1: Debian GNU/Linux 3.0 r0 Woody - Official i386 Binary-1 CD (debian-30r0-i386-binary-1.iso) Further information about `debian-30r0-i386-binary-1.iso': Generated on Thu, 18 Jul 2002 14:34:12 +0100 ----------------------------------------------------------------- If you already have a previous version of the CD you are downloading, jigdo can re-use files on the old CD that are also present on the new image, and you do not need to download them again. You found the secret message; you're a very careful reader. Mount the old CD ROM and enter the path it is mounted under (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want to start the download of any remaining files. You can also enter a single digit from the list below to select the respective entry for scanning: 1: /mnt Files to scan: jigdo-lite is asking us to give it the location of your mounted CD (if you're updating a CD) or your loop mounted ISO file (if you're using the ISO file). I'm using an ISO file loop mounted on /mnt, so I'll enter /mnt. If you're updating a CD, enter the mount directory of your CD, which is most likely /cdrom. In either case, jigdo-lite will scan the directory of your mounted media, determine which files need updating and re-use the files which don't need updating. See also , "More About Scan Sources". You may see something like: Files to scan: /mnt/other Not downloading .template file - `woody-i386-1.template' already present jigdo-file: Output file `debian-30r0-i386-binary-1.iso' already exists - delete it or use --force jigdo-file failed with code 3 - aborting. What happened? Actually, I wanted to show you this because you'll bump into it sooner or later. I'm updating an ISO file, but the outdated image file is in the same directory I'm working in. Jigdo-lite wants to generate a file called woody-i386-1.iso but there's already a file by that name in the current directory (the outdated image). Jigdo-lite doesn't want to destroy that file, so it bails and lets me know that I can either delete that file or use --force to overwrite the file. You could also rename or move the file too, but I guess jigdo-lite assumes we already know this.   :-) Don't be timid about moving or renaming the image file just because it's loop mounted. The filesystem uses inodes under the hood, and even if you move or rename the file, the inode stays the same. You won't hurt the filesystem mounted under /mnt. As for deleting the ISO file, that won't hurt the mounted filesystem either. A file's inode gets deallocated only when the inode's reference count drops to zero. Mounting the ISO image bumps the reference count up, so the file really gets deleted only after you rm the file and umount the loop device. All you people who are updating the CD don't have to worry about any of this. :-) I'll rename the ISO file to woody-i386-1.iso.old and run jigdo-lite again. Let's try again: $ jigdo-lite woody-i386-1.jigdo ----------------------------------------------------------------- Jigsaw Download "lite" Copyright 2001-2002 by Richard Atterer <jigdo@atterer.net> Loading settings from `/home/p/.jigdo-lite' ----------------------------------------------------------------- Images offered by `woody-i386-1.jigdo': 1: Debian GNU/Linux 3.0 r0 Woody - Official i386 Binary-1 CD (debian-30r0-i386-binary-1.iso) Further information about `debian-30r0-i386-binary-1.iso': Generated on Thu, 18 Jul 2002 14:34:12 +0100 ----------------------------------------------------------------- If you already have a previous version of the image you are downloading, jigdo can re-use files on the old image that are also present on the new image, and you do not need to download them again. Mount the old CD ROM and enter the path it is mounted under (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want to start the download of any remaining files. You can also enter a single digit from the list below to select the respective entry for scanning: 1: /mnt Files to scan: /mnt Not downloading .template file - `woody-i386-1.template' already present ... Found 1200 of the 1224 files required by the template ... jigdo-lite remembers that I wanted to scan /mnt and tells me I can either type 1 to scan that directory or type the directory in again. Since I'm a perverse person, I type the name of the directory again. The ellipsis represent some text that changes rapidly. The first ellipsis is a dynamic list of what files jigdo-lite is scanning. The second ellipses denotes progress in writing woody-i386-1.iso.tmp. Once jigdo-lite finishes scanning the files and writing the temporary ISO file, it prints: Copied input files to temporary file `woody-i386-1.iso.tmp' - repeat command and supply more files to continue ----------------------------------------------------------------- If you already have a previous version of the image you are downloading, jigdo can re-use files on the old image that are also present on the new image, and you do not need to download them again. Mount the old CD ROM and enter the path it is mounted under (e.g. `/mnt/cdrom'). Alternatively, just press enter if you want to start the download of any remaining files. You can also enter a single digit from the list below to select the respective entry for scanning: 1: /mnt Files to scan: Since you normally don't have another source of files to scan other than your loop mounted ISO file (or your CD), press ENTER. Jigdo-lite will then ask you about which mirrors you want to use, just like it did when you downloaded your ISO for the first time. You've already answered these questions before, but if you truly don't remember, you might want to re-read . At this point, you'll see jigdo-lite working its magic. Now wasn't that easy? Frequently Asked Questions Questions prepended with a date indicate a time sensitive question (a question that relates to a temporary situation). If you see one of these questions and know that the temporary situation has changed, please contact me and let me know so I can remove the question from the mini-HOWTO. Why does jidgo ask <emphasis>twice</emphasis> for scanning for existing files? Is it enough to say yes once ? It keeps asking this as long as you enter a path to scan. The idea is that you may want to scan several old CDs, so you can insert one after the other into the drive and keep supplying the path "D:\" (or whatever). See also , "More About Scan Sources". Jigdo Has Problems Downloading Certain Filenames. When downloading Debian images under Windows, jigdo-lite may appear to have trouble downloading one or more of the following files: libbusiness-onlinepayment-bankofamerica-perl_xxx_all.deb libbusiness-onlinepayment-authorizenet-perl_xxx_all.deb libbusiness-onlinepayment-payconnect-perl_xxx_all.deb libmasonx-request-withapachesession-perl_xxx_all.deb libtemplate-plugin-calendar-simple-perl_xxx_all.deb Move the jigdo download directory up by as many directories as possible, closer to the drives's root directory. The NTFS filesystem has a 255 character limit on a file's pathname. When jigdo-lite downloads files from the internet, it makes a copy of the server directory structure in its download directory. With their very long names, the above Debian packages may exceed the allowed path length, which leads to error messages like "Cannot write to `[very long pathname]' (No such file or directory)". Some people may now wonder: Why does jigdo-lite use wget's "--force-directories" switch, which creates these problematic directory hierarchies? Early versions of jigdo-lite did not use it, but then some folks requested that jigdo-lite always use the "--continue" switch to avoid half-downloaded .deb files being ignored and deleted when you interrupt and restart jigdo-lite. Unfortunately, it turned out that this led to problems: The Debian servers contained several identically named files (e.g. "root.bin") in different directories, and if you interrupted jigdo-lite at roughly the right time during the download, the chances were high that the resumed download would append data to the wrong half-downloaded file, corrupting it and making the entire jigdo download fail. How do I make jigdo use my proxy? Edit ~/.jigdo-lite (or jigdo-lite-settings.txt for the Microsoft Windows version) into a text editor and find the line that starts with "wgetOpts". The following switches can be added to that line: -e ftp_proxy=http://LOCAL-PROXY:PORT/ -e http_proxy=http://LOCAL-PROXY:PORT/ --proxy-user=USER --proxy-passwd=PASSWORD Of course, substitute the correct values for your proxy server. The last two options are only necessary if your proxy uses password authentication. The switches need to be added to the end of the wgetOpts line before the final ' character. All options must be on one line. Alternatively, under Linux you can also set up the ftp_proxy and http_proxy environment variables, for example in the file /etc/environment or ~/.bashrc. Jigdo-lite fails with an error - have I downloaded all those MBs in vain? If jigdo-file aborts after downloading a considerable chunk of the ISO contents, you'll have a large ".iso.tmp" file. There are several things to try to salvage your download: Restart the download by pressing RETURN. Maybe some of the files could not be downloaded because of timeouts or other transient errors. Try to download the missing files again. Try a different mirror. Some Debian mirrors are slightly out of sync -- maybe a different mirror still holds files that were deleted from the one you specified, or it has already been updated with files that are not yet present on your mirror. This has happened quite a few times with me. Retrieve the missing parts of the image using rsync. First, you need to find out the correct rsync URL of the image you are downloading: Choose a server that offers rsync access to the stable or testing images, then determine the correct path and filename. Directory listings can be obtained with commands like rsync rsync://cdimage.debian.org/debian-cd/. Next, remove the ".tmp" extension from jigdo-lite's temporary file by renaming it, and pass both the remote URL and the local filename to rsync: rsync rsync://server.org/path/binary-i386-1.iso binary-i386-1.iso You may want to use rsync's --verbose and --progress switches to get status messages, and --block-size=8192 to increase its speed. Under Linux, you can loop-mount the .tmp file to access the packages that were already downloaded, and reuse them for generating an image from a newer .jigdo file. To do this, first issue the following commands as root in the directory with the broken download: mkdir mnt; mount -t iso9660 -o loop *.tmp mnt. Next, start a new download in a different directory, and enter the path of the mnt directory at the "Files to scan" prompt. Under Microsoft Windows you can do the same thing by loop mounting the temporary ISO image using "virtual drive" software. Daemon tools and Nero Image Drive are both very popular. See also http://tinyurl.com/c39zr for more options. [11 Aug 2002]: Why aren't the translations of this HOWTO on LDP? I've been having trouble getting the translations of this HOWTO submitted to the non-English LDP editors. The German LDP editor, Marco Budde Budde@tu-harburg.de refuses to accept the German translation because it was written in Docbook and not Linuxdoc, even though Docbook is the preferred SGML language for the LDP. It's a shame that we have people within the open source community who would sabotage our community from the inside. The Portuguese LDP editor, Alfredo Carvalho ajpc@poli.org, has completely ignored my submission of the Portuguese translation. If you care about having LDP documents in these languages, I urge you to write to these editors and ask them to please be more responsible about accepting translated documents. For the time being, you can download these translations from my personal website, http://www.dirac.org/linux/debian/jigdo. Shame on you, Marco Budde Budde@tu-harburg.de. Shame on you, Alfredo Carvalho ajpc@poli.org. What do I do if my jigdo download gets interrupted? If your download gets interrupted, all you need to do is restart jigdo-lite and hit ENTER at all the question prompts. Jigdo-lite will pick up where it left off. My jigdo download won't complete because the .jigdo file is broken. When I download a new, fixed .jigdo file, do I need to download all the data over again? You may find that the .jigdo file you downloaded is broken. It's uncommon, but it does happen from time to time with moving targets like Debian testing or unstable. If you find that .jigdo is broken, you'll need to download a new .jigdo file (when a fixed one becomes available), but you won't need to download all the ISO data again. You can use the same loop mounting trick we use when updating an ISO image. The difference is that there's no finished .iso file to start with, but the .iso.tmp file is an ISO image too and can be used to finish the download without having to re-download all the data that was downloaded before the broken .jigdo file caused jigdo-lite to halt. Simply loop mount the .iso.tmp file on /mnt and when you re-run jigdo-lite with the fixed .jigdo file, tell jigdo-lite to scan /mnt. Don't forget to rename or move the .iso.tmp file so it doesn't interfere with jigdo-lite which will want to create a new .iso.tmp file. Can I use jigdo to download images for DVD? Absolutely; the process is identical to downloading CD images. The only thing you need to do differently is to download the .jigdo and .template files for DVDs instead of CDs. You can find the DVD .jigdo and .template files at http://www.debian.org/CD/jigdo-cd/. On Linux, you need kernel 2.4 or later to create DVD-sized files. Under MS Windows, you need to use jigdo-win-0.7.1a (released 21 July 2004) or later to create DVD-sized images. This is because of a bug in the large file support of Mingw32, the compiler used to create the MS Windows executables. The bug got fixed on this date, and jigdo-win-0.7.1a was released. Can I burn the <filename>.iso.tmp</filename> file to CD? Thanks to Gordon Huff and David Anselmi, we now know the answer is "yes you can". But more importantly, Gordon gave a good reason why you'd want to do this in the first place. Paraphrasing Gordon:
My friend's Win98 has a *nice* cable connection. I arrive in the morning, start jigdo (more than one, actually) and then we go to the store, tie back the kiwi plant, put up the Christmas lights and Christmas tree, trim the tree, order and split a pizza and fire up the satellite dish. I leave my friends place with several iso.tmp's on CDRWs. When I get home, I use the iso's that didn't finish to update my jigdo setup at home which is a dial-up.
Jigdo-lite is broken! It downloads packages and deletes them. I know it doesn't write them to the <filename>iso.tmp</filename> file because the file size doesn't change! Jigdo works just fine -- the .iso.tmp file is created at the beginning with its final size, but filled with zero bytes. Later, parts of it are overwritten with the downloaded data. You can tell that jigdo is making progress by looking at the messages "Found X of the Y files required by the template" that are printed from time to time. The first value "X" should increase. When X equals Y, the download is finished. I'm having trouble getting jigdo-easy to work. See . For image updates, I want jigdo-lite to scan 14 loop-mounted images in one go. How can I do this? When updating CD images, it's tiresome to keep loop-mounting and unmounting images. However, by default the Linux kernel only supports eight loop devices, and jigdo-lite's menu of previously entered paths only has five entries. To scan many loop-mounted images, you must first tell the Linux kernel to support more than the default eight devices. This is done by giving the "max_loop" parameter to the module when loading it, e.g. with "modprobe loop max_loop=16" on the command line or by adding the line "options loop max_loop=16" to /etc/modules.conf. In Debian, you must put this line into a file named e.g. /etc/modutils/local-loop and then run update-modules because direct changes to /etc/modules.conf will be overwritten. Having mounted the individual images, you can pass the parent directory of their mount points to jigdo-lite for scanning. For example, if the images are mounted under /mnt/myloopmounts/image1/ etc., pass "/mnt/myloopmounts" as the path to scan. If passing the parent directory is inconvenient, you can also create a directory and fill it with symlinks to the mount points. Jigdo-lite is too verbose. How can I supress some or all of its messages? Jigdo-lite uses wget, and wget's output can be quite verbose. If this is unsettling, you can make wget more quiet by adding --non-verbose to the wgetOpts switch in your ~/.jigdo-lite file. If you want wget to print no messages at all, use --quiet in the wgetOpts switch. Can I use jigdo on platforms other than Linux? Certainly. If you're interested in Potato or Woody under Microsoft Windows, old SunOS, HP-UX and IRIX you can use jigdo-easy. See and . If you want to download Potato, Woody, Sarge or Sid under Microsoft Windows, jigdo-lite has been ported to that platform and can be downloaded from the main jigdo site (). On MS Windows, why do I get a "<literal>No such file or directory</literal>" error message? You might find that under MS Windows, jigdo-lite will download some files but then fail to read their contents, which will produce a "No such file or directory" error message. It seems that this occurs if the length of the filenames that jigdo processes exceeds a certain limit. The solution is to move the half-finished download up in the directory hierarchy, closer to the top-level directory of the drive. On MS Windows, why won't my image grow larger than 2GB? You're using an old version of jigdo. Please upgrade to jigdo-win-0.7.1a or newer. See . On MS Windows, <filename>jigdo-lite.bat</filename> fails with an error message saying "sh" was not found. This means that the PATH command in the .bat file failed. For some reason, this is the case if you unpacked jigdo on a Windows network share using a path like "\\SomeServer\Files\jigdo". Solution: Use "Map network drive" (in the explorer "tools" menu) to assign a drive letter like "Z:", then double-click on the .bat file inside "Z:\jigdo". Alternatively, a workaround is to move everything in the jigdo-bin subdirectory up to where the .bat file is. Can I run multiple instances of jigdo-lite to download images in parallel? Absolutely. However, to avoid filename clashing, you should run each jigdo-lite instance in its own separate directory. You can start as many instances as you want, go to bed, and when you wake up, all the ISO images will be waiting for you on your hard drive. Be aware that jigdo-lite is bandwidth and CPU intensive, so you won't want to use your computer with multiple instances running in tandem. Is there a GUI interface available? A GTK+ interface to jigdo is being worked on. Both Linux and Microsoft Windows GUI clients are planned. Unfortunately, it's been 80% done for over 1.5 years, so don't hold your breath for its release.
Errata jigdo-easy Jigdo-easy, by Anne Bezemer, is a fork of jigdo-lite which is portable to a wider range of systems, including Microsoft Windows, old SunOS, HP-UX and IRIX). It's also easier to use than jigdo-lite but because of changes made to Jigdo, will only work with Potato and Woody. Jigdo-easy will not be able to download Sarge and Sid. See and . More About Scan Sources By now you know that when jigdo-lite asks for files to scan, you can use 3 sources: A mounted copy of an outdated CD or DVD that you wish to update. A loop-mounted copy of an outdated ISO image file on your hard drive. A loop-mounted copy of the temporary .iso.tmp file, when a previous jigdo-lite run aborted. As Jens Seidel points out, there is another, rather crafty, source you should use for a scanning source: your apt cache. Apt uses the directory /var/cache/apt/archives for cache. There will be many Debian packages sitting in this directory, and they can be used for a scan source for jigdo-lite! So when you're asked for a directory to scan, by all means, use this directory too. If you're editing the ~/.jigdo-lite file by hand, be aware that multiple scan directories are space separated, for example: scanMenu='/var/cache/apt/archives/ /cdrom/' jigdo-file-cache.db The cache contains the md5sums of files read when you supply a directory at the Files to scan: prompt. If you have jigdo-file scan the same directory a second time, the scan will be very fast. This could be useful in the following case: rev0 gets updated to rev1. With the rev1 CD images, some packages may have been pushed from CD n to CD n+1, or vice versa. If you had a particularly slow link (e.g. modem), you'd try to avoid downloading these packages again. For this reason, when downloading the new version of CD n, you'd let jigdo-lite scan the three CDs n-1, n and n+1 (or even all 8 CDs if you want to be 100% sure). If you have jigdo-lite scan the same CDs over and over again while updating each of the 8 CD images, the cache will prevent all the data on the CDs from being read multiple times. The cache is much more important when generating jigdo files, because you don't want jigdo-file to read in your whole 50GB Debian mirror for every generated jigdo file. Resources This HOWTO is winding down to a close, but I thought I'd leave you with a few links and references to learn more about the jigdo tools and how they work. http://atterer.net/jigdo This is the jigdo home site. You should definitely browse this site; lots of information about ports, GUI clients and everything under the sun relating to jigdo. http://cdimage.debian.org/~costar/jigdo The Debian page for jigdo-easy (). http://www.debian.org/CD/jigdo-cd The main Debian page for jigdo. http://packages.debian.org/testing/utils/jigdo-file.html The official webpage for the Debian jigdo-file package. http://lists.debian.org/search.html You can use this page to search the debian-cd mailing list archives. http://www.debian.org/MailingLists/subscribe The subscription page for the debian-cd mailing list. https://lists.berlios.de/mailman/listinfo/jigdo-user The subscription page for the official Jigdo mailing list.
jigdo-0.7.3/doc/jigdo-file.10000644000175000017500000013011310433432647015274 0ustar richardrichard.\" This manpage has been automatically generated by docbook2man .\" from a DocBook document. This tool can be found at: .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . .TH "JIGDO-FILE" "1" "19 May 2006" "" "" .SH NAME jigdo-file \- Prepare files for Jigsaw Download (distribution of huge files, e.g. CD images). .SH SYNOPSIS \fBjigdo-file\fR \fB \fI COMMAND\fB \fR [ \fB--image=\fIcdrom.iso\fB\fR ] [ \fB--jigdo=\fIcdrom.jigdo\fB\fR ] [ \fB--template=\fIcdrom.template\fB\fR ] [ \fB--force\fR ] [ \fBMORE OPTIONS\fR ] [ \fB\fIFILES\fB\fR\fI ...\fR | \fB--files-from=\fIf\fB\fR ] \fBCommon COMMANDs: make-template, make-image, verify\fR .SH "DESCRIPTION" .PP Jigsaw Download, or short jigdo, is a scheme developed primarily to make it easy to distribute huge filesystem images (e.g. CD (ISO9660) or DVD (UDF) images) over the internet, but it could also be used for other data which is awkward to handle due to its size, like audio/video files or large software packages. .PP jigdo tries to ensure that the large file (always called \fIimage\fR from now on) is downloaded in small parts which can be stored on different servers. People who want to download the image do so by telling the \fBjigdo\fR(1) \fB(NOT IMPLEMENTED YET)\fR download tool to process one `\fI\&.jigdo\fR\&' file; using it, \fBjigdo\fR downloads the parts and reassembles the image. \fBjigdo-file\fR is used to prepare the files for download. .PP What makes jigdo special is that the parts that are used to reconstruct the image can have any size and content - they only need to be contained in a contiguous region anywhere in the image. .PP For example, if you wish to distribute an ISO9660 image which contains a snapshot of an FTP server, you can instruct \fBjigdo-file\fR to prepare the download data in such a way that when people use \fBjigdo\fR to download the image, \fBjigdo\fR actually fetches the individual files from the FTP server and assembles them into an exact copy of your image - during the download! (If the image is not a filesystem dump, you can use \fBsplit\fR(1) to create the small parts that the image will be reassembled from.) .PP You are completely free to choose where the individual parts of the image are stored: They may be in entirely different directories on different servers (e.g. because of storage/bandwidth constraints), but this is invisible to the people downloading your image. The information about available servers only needs to be added to the `\fI\&.jigdo\fR\&' file by you before distributing it. .PP The `DETAILS' section below contains technical details on how jigdo works. The `EXAMPLES' section lists a number of common scenarios and may help you to get an idea of what jigdo is useful for. .SH "OPTIONS" .PP Many options are specific to a particular \fICOMMAND\fR; the ones below are general or used by several commands. Further options are listed below with the individual commands. All options are silently ignored if they are not applicable to the current command. For any \fIBYTES\fR parameters to options, you can append one of the letters `k', `M' or `G' to the amount you specify, to indicate kilobytes, megabytes or gigabytes. .TP \fB-h --help\fR Output short summary of commands and options. .TP \fB-H --help-all\fR Output complete summary of commands and options. .TP \fB-v --version\fR Output program version. .TP \fB-i --image=\fIcdrom.iso\fB\fR Specify location of the file containing the image. The image is the large file that you want to distribute. .TP \fB-j --jigdo=\fIcdrom.jigdo\fB\fR Specify location of the Jigsaw Download description file. The jigdo file is a human-readable file generated by \fBjigdo-file\fR, to which you add information about all the servers you are going to upload the files to. \fBjigdo\fR will download this file as the first step of retrieving the image. .TP \fB-t --template=\fIcdrom.template\fB\fR Specify location of the image `template' file. The template file is a binary file generated by \fBjigdo-file\fR, it contains information on how to reassemble the image and also (in compressed form) all the data from the image which was not found in any of the parts. Depending on the command, each of these three files is used sometimes for input, sometimes for output. If the file is to be used for output for a particular command and the output file already exists, \fBjigdo-file\fR exits with an error, unless \fB--force\fR is present. In most cases, you will only need to specify one out of \fB-i\fR \fB-j\fR \fB-t\fR, because any missing filenames will be deduced from the one you specify. This is done by first stripping any extension from the supplied name and then appending nothing (if deducing \fB--image\fR), `\fI\&.jigdo\fR\&' or `\fI\&.template\fR\&'. .TP \fB-r --report=default|noprogress|quiet|grep\fR Control how verbose the program is, and what format the output has: \fBnoprogress\fR is the same as \fBdefault\fR except that no `x% done\&' progress messages are printed. \fBquiet\fR restricts the output to what is absolutely necessary, mostly error messages. \fBgrep\fR is only different from \fBdefault\fR for the \fBmake-template\fR command: It enables output in a simple `\fI \fR\&' format which is useful when searching for binary files in other binary files. .TP \fB-f --force\fR Overwrite existent output files without complaining. .TP \fB--no-force\fR \fBThis is the default.\fR Refuse to overwrite existent output files. .TP \fB-c --cache=\fIjigdo-cache.db\fB\fR \fBjigdo-file\fR usually needs to read the entire contents of all the \fIFILES\fR you specify. If you use it repeatedly (e.g. because you make a new CD image available daily), caching the file information will increase the program's speed significantly. The cache file is automatically created if it is not yet present. Data is usually both read from and written to it. .TP \fB--no-cache\fR \fBThis is the default.\fR Do not use a cache. .TP \fB--cache-expiry=\fISECONDS\fB\fR Set maximum age of cache entries. Any entries older than this will be removed from the cache. The default is 30 days. You can append one of the letters `h', `d', `w', `m', `y' to denote hours, days, weeks, months or years, respectively. A value of `0' or `off' disables expiry, so that all entries will stay in the cache forever. See the section `CACHE FILES' below for more information. .TP \fB--readbuffer=\fIBYTES\fB\fR Set size of internal buffers. The default is 128k - if you have a fast disc, increasing this value may make \fBjigdo-file\fR faster, but in general, changing it is not necessary. .TP \fB--md5-block-size=\fIBYTES\fB\fR \fBUninteresting internal parameter.\fR Set size of blocks into which files are subdivided. The default is 128k. If you change it, any cache file will have to be regenerated. Internally, \fBjigdo-file\fR may choose to use a slightly larger or smaller value. .TP \fB-T --files-from=\fIfile\fB\fR Read file and directory names from the specified file. If \fIfile\fR is `-', read names from standard input. Each line in the file is taken as a name, so the names may contain spaces, but not newline characters. An empty line causes \fBjigdo-file\fR to stop reading from the file. \fBfind\fR(1) is a powerful tool for generating file lists, but make sure to use `\fBfind -type f\fR\&' if possible - otherwise, if you instruct \fBfind\fR to output both a filename and a symlink to that filename, \fBjigdo-file\fR will read the file contents twice. .TP \fB--hex\fR Output checksums in hexadecimal instead of Base64-like format. This should not be used with the \fBmake-template\fR command, because the resulting `\fI\&.jigdo\fR\&' file violates the `\fI\&.jigdo\fR\&' file format. Its intended use is to make \fBjigdo-file\fR more interoperable with other Unix shell utilities like \fBmd5sum\fR(1)\&. .TP \fB--no-hex\fR \fBThis is the default.\fR Use jigdo's own Base64-like encoding of checksums. .TP \fB--debug[=help|=all|=\fIUNIT,~UNIT...\fB ]\fR Switch on or off debugging output. Just `--debug' is equivalent to `--debug=all'. The argument is a comma-separated list of unit names for which debugging output is to be enabled, or disabled if the name is preceded by `~'. The special name `all' means all units. By default, debugging output is switched off except for the units `assert' and `general'. The exact list of available units for which debugging can be switched on depends on whether jigdo was compiled with debugging support - the list can be printed with `--debug=help'. .TP \fB\fIFILES\fB\fR Names of files or directories to use as input. These are the parts that are contained in the image. In case one of the names is a directory, the program recursively scans the directory and adds all files contained in it. While doing this, it follows symbolic links, but avoids symlink loops. If one of the filenames starts with the character `-', you must precede the list of files with `--'. A value of `-' has \fBno\fR special meaning in this list, it stands for a file whose name is a single hyphen. .SH "COMMANDS" .PP The command name is the first non-option argument passed to \fBjigdo-file\fR\&. Most commands have short abbreviations as well as long names. \fBThe short command names should not be used in scripts - there may be incompatible changes to them in the future!\fR .SS "MAKE-TEMPLATE, MT" .PP Reads \fIimage\fR and \fIFILES\fR, creates `\fI\&.jigdo\fR\&' and `\fI\&.template\fR\&'. This is the main functionality of \fBjigdo-file\fR\&. .PP It is possible to specify both \fB--image=-\fR and \fB--files-from=-\fR\&. In this case, first the list of files is read from standard input until an empty line is encountered. Everything following it is assumed to be the image data. This can be useful if you use \fBmkisofs\fR(1) or similar programs that can output the complete image on their standard output, because there is no need to store the image on disc temporarily. .PP If a \fIFILES\fR argument contains the characters `//\&' (Unix) or `\\.\\\&' (Windows), this has special meaning. In the final jigdo file that users will download, each of the parts is referenced in the `[Parts]\&' section with a URI of the form `Label:some/filename'. (See `FORMAT OF .JIGDO FILES' below for a detailed description.) The `[Servers]\&' section gives a mapping of labels to servers on the internet, with lines like `Label=http://myserver.org/jigdofiles/'. Using this information, \fBjigdo\fR will create the final download URI for the part, `http://myserver.org/jigdofiles/some/filename'. Specifying `//\&' (or `\\.\\\&') in a file or directory name serves to `cut off' the names at the right directory level. For example, if the Unix path of one of your \fIFILES\fR is `/path/some/filename', you can tell \fBjigdo-file\fR to cut off after the `/path' by passing it the argument `/path//some/filename', or `/path//' if you want the whole directory scanned. The path names need not be absolute; `somedirectory//' is also possible. .TP \fB--label \fILabel=/path\fB\fR Specify a name to use as the label name for a path on disc. (Influences the output jigdo file.) If you used `//\&' in the \fIFILES\fR arguments as described above, \fBjigdo-file\fR will by default pick label names automatically (`A', `B' etc.). With this option, you can give labels more meaningful names. Note that the label name will only be used if one or more \fIFILES\fR begin with `/path//'. Try to use label names that start with uppercase characters, to disambiguate them clearly from protocol names like `http', `ftp'. .TP \fB--uri \fILabel=http://some.server.org/\fB\fR By default, using \fB--label\fR as described above will cause lines of the form `Label=file:/path/' to be written to the `[Servers]\&' section of the output jigdo file. If you want to override the `file:' URI so that the line reads `Label=http://some.server.org/', you can do so by specifying \fB--uri\fR along with \fB--label\fR\&. Giving just \fB--uri \fILabel=...\fB\fR without the corresponding \fB--label \fILabel=...\fB\fR has no effect, and even if you specify both, an entry is only added to the `[Servers]\&' section if the label is referenced by at least one `[Parts]\&' entry. The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\\\\\ or \&'\\\\' to get a single backslash in the URI.) The mapping specified with an \fB--uri\fR option is ignored if it is already present in the output jigdo file. Users of the Windows version may notice that the `\\\&' directory separators are converted into `/\&' in the `file:' URIs that are generated by default. This is done to increase cross-platform compatibility of `file:' - the \fBprint-missing\fR command of the Windows version will automatically re-convert the characters when it prints the URIs. In case you supply your own `file:' URIs under Windows using \fB--uri\fR, you must also exchange `/\&' and `\\\&'. .TP \fB-0 to -9\fR Set amount of compression in the output template file, from \fB-0\fR (no compression) to \fB-9\fR (maximum compression). The default is \fB-9\fR, which can make the template generation quite slow. By default, the compression algorithm used is the same as for \fBgzip\fR(1)\&. .TP \fB--gzip and --bzip2\fR Choose between the gzip and bzip2 compression algorithms. The default is gzip. Bzip2 usually gives a better compression ratio, but compression is significantly slower than with gzip. .TP \fB--min-length=\fIBYTES\fB\fR Set minimum length of a part for \fBjigdo-file\fR to look for it in the image. The default is 1k. Parts smaller than this will never be found in the image, so their data will be included in the template file. The search algorithm used requires such a minimum length, otherwise template generation could become extremely slow. If you know for sure that all your \fIFILES\fR are larger than a certain amount, you can increase \fBjigdo-file\fR\&'s speed slightly by specifying the amount with this option. There is a hard-wired absolute minimum of 256 bytes - anything lower will silently be set to 256. .TP \fB--merge=\fIFILE\fB\fR Include the contents of \fIFILE\fR in the output `\fI\&.jigdo\fR\&' file. The file can contain data which you want added to the output (for example, a `[Servers]\&' section with a list of your servers as entries), or it can be the jigdo file output by an earlier run of \fBjigdo-file\fR\&. It is possible to specify the same file for input with \fB--merge\fR and for output with \fB--jigdo\fR\&. However, you will also need to use \fB--force\fR to make the program overwrite the old version of the jigdo file with the new one. \fIFILE\fR can be `-' for standard input. When \fBadding\fR new information to the supplied file, \fBjigdo-file\fR will not insert new lines into the `[Parts]\&' section if an entry for the same MD5 checksum (but not necessarily with the same URI!) already exists, and it will not insert new lines into the `[Servers]\&' section if a completely identical entry already exists. When \fBreading in\fR the existing \fIFILE\fR, the behaviour is slightly different: The program \fBpreserves\fR entries in the `[Parts]\&' section with identical checksum, but different URIs. For completely identical entries (same checksum and URI), only one entry is preserved and the duplicates are removed. The `[Servers]\&' section is left untouched. .TP \fB--image-section\fR \fBThis is the default.\fR Causes \fBjigdo-file\fR to add an `[Image]\&' section to the `\fI\&.jigdo\fR\&' file. As an exception, a new `[Image]\&' section is \fBnot\fR added if you use \fB--merge\fR and the file to merge contains an `[Image]\&' section with a line which reads `Template-MD5Sum=\&' (end of line after the `='). In this case, the generated template data's MD5 checksum value is just added after the `=' of the first line of this form in the file - no whole new `[Image]\&' section is appended. This behaviour is useful because it allows you to pass via \fB--merge\fR an `[Image]\&' section with arbitrary content and then have the MD5 checksum automatically added by \fBjigdo-file\fR\&. The section `FORMAT OF .JIGDO FILES' below explains the `[Image]\&' section contents in greater detail. .TP \fB--no-image-section\fR Do \fBnot\fR include an `[Image]\&' section in the `\fI\&.jigdo\fR\&' file. You need to add one yourself if you use this option. However, doing that is not easy (you also need to add a `Template-MD5Sum\&' line with the correct checksum, or \fBjigdo\fR will complain), so use of this option is discouraged. .TP \fB--servers-section\fR \fBThis is the default.\fR Causes \fBjigdo-file\fR to add a `[Servers]\&' section to the `\fI\&.jigdo\fR\&' file. This default section uses `file:' URIs, which allows for immediate reassembly of the image from the local filesystem, and is also useful if you want to edit the file manually and replace the `file:' URIs with other URIs. .TP \fB--no-servers-section\fR Do \fBnot\fR add a `[Servers]\&' section at the end of the `\fI\&.jigdo\fR\&' file. Useful e.g. if you are going to append the section with a script. .TP \fB--match-exec=\fISHELLCOMMAND\fB\fR Whenever a file is found in the image, execute the supplied command string by passing it to a shell. \fBjigdo-file\fR sets up a number of environment variables with information about the file match. For example, if the file `\fI/path//a/b/file\fR\&' was found in the image and `Label:a/b/file' is going to be written to the `\fI\&.jigdo\fR\&' file: .RS .TP 0.2i \(bu \fBLABEL\fR="Label" - Name of the label for the file. The example assumes that `\fB--label\fR Label=/path\&' was specified by you. In the absence of such an option, \fBLABEL\fR will be set but empty. .TP 0.2i \(bu \fBLABELPATH\fR="/path/" - The path corresponding to the label, or in other words, the prefix of the matched file's path that will \fBnot\fR appear in the output `\fI\&.jigdo\fR\&' file. Is set even without any `\fB--label\fR\&' option present. Ends with a slash. .TP 0.2i \(bu \fBMATCHPATH\fR="a/b/" - The rest of the path, without the leafname of the matched file. Is either empty or ends with a slash. .TP 0.2i \(bu \fBLEAF\fR="file" - The leafname of the matched file. .TP 0.2i \(bu \fBMD5SUM\fR="lNVdUSqbo2yqm33webrhnw" - The md5sum of the matched file, in Base64-like format. .TP 0.2i \(bu \fBFILE\fR="/path//a/b/file" - For convenience, the complete path of the file. The variable is always set to $LABELPATH$MATCHPATH$LEAF\&. .RE Please be careful to correctly quote the string passed to this option, otherwise your supplied command will not work with filenames that contain spaces. As an example, to create a backup of hard links to the matched files, use the following option: --match-exec='mkdir -p "${LABEL:-.}/$MATCHPATH" && ln -f "$FILE" "${LABEL:-.}/$MATCHPATH$LEAF"' By default, no command is executed. Use --match-exec="" to remove a command string which was set with an earlier use of this option. .TP \fB--greedy-matching\fR \fBThis is the default.\fR Imagine that your image contains a \fI\&.tar\fR file which in turn contains another file \fIx\fR, and that you provide both the \fI\&.tar\fR and the files inside it on the command line. When \fBjigdo-file\fR scans the image, it encounters the beginning of the \fI\&.tar\fR file, and then the file \fIx\fR\&. At this point, a decision must be made: Should the smaller file \fIx\fR be recorded as matched, or should it be ignored in favour of the larger (and thus better) match of the \fI\&.tar\fR file? Unfortunately, at this point it is not clear whether there will actually be a full match of the \fI\&.tar\fR, so by default, the program prefers the small match. .TP \fB--no-greedy-matching\fR In the case where a large partial match is present and a shorter match has been confirmed, ignore the small match. (See the option above.) .SS "MAKE-IMAGE, MI" .PP Reads `\fI\&.template\fR\&' and \fIFILES\fR, creates \fIimage\fR (or `\fIimagename.tmp\fR\&'). Provides a rudimentary way of reassembling images - \fBjigdo\fR is usually better suited for this task. However, in contrast to \fBjigdo\fR, no `\fI\&.jigdo\fR\&' file is required. .PP If the image is to be written to a file (and not to standard output), it is possible to create the image in several steps, with several invocations of `\fBjigdo-file make-image\fR\&', as follows: You first invoke \fBjigdo-file\fR, specifying as many files as are available at this time. The program scans the files, and those that are contained in the image are copied to a temporary file, whose name is formed by appending `\fI\&.tmp\fR\&' to the image filename. .PP For all further files which could be parts of the image, you repeat this process. As soon as all parts are present, the temporary file will be truncated slightly (to delete some administrative data that \fBjigdo-file\fR appends at the end) and renamed to the final image name. The possibility of reassembling the image in several steps is especially useful for gathering files from removable media, e.g. several older CDs. .PP Scripts using \fBmake-image\fR can detect whether image creation is complete by checking the exit status: 0 signals successful creation, whereas 1 means that more files need to be supplied. Other errors result in an exit status of 2 (`recoverable', e.g. file not found) or 3 (non-recoverable, e.g. write error). .TP \fB--check-files\fR \fBThis is the default.\fR Whenever any part is copied to the image, re-check its checksum against the checksum stored in the template. It is recommended that you leave this switched on, even if it slows down image creation a bit. .TP \fB--no-check-files\fR Do not check files' checksums when copying them to the image. This can be safely used when no cache file is used (which means that files will be written to the image immediately after being scanned) or the whole image is checked later with the \fBverify\fR command. .SS "PRINT-MISSING, PM" .PP Reads `\fI\&.jigdo\fR\&', `\fI\&.template\fR\&' and (if present) `\fIimagename.tmp\fR\&', outputs a list of URIs still needed to completely reassemble the image. .PP Together with the \fBmake-image\fR command, this provides most of the functionality of \fBjigdo\fR on the command line. .PP For each part that is not yet present in the temporary image file, the file checksum is looked up in the `[Parts]\&' section of the jigdo file. Any label in the corresponding entry is then expanded according to the label definitions in the `[Servers]\&' section and printed on standard output. \fBjigdo\fR allows you to specify several alternative locations for each label in this section, but \fBprint-missing\fR will only output the first one for each missing part. .PP If the checksum cannot be found in the `[Parts]\&' section (this Should Not Happen unless you deleted that section), a lookup is instead made for `MD5Sum:\fI\fR\&', just like with \fBjigdo\fR\&. (Thus, if you want to get rid of the `[Parts]\&' section, you can do so if you rename each part to its own checksum.) .TP \fB--uri \fILabel=http://some.server.org/\fB\fR Override the entries in the `\fI\&.jigdo\fR\&' file for any label with a URI of your choice. With the example above, a `[Parts]\&' entry of `Label:some/filename' will cause the line `http://some.server.org/some/filename' to be printed. The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\\\\\ or \&'\\\\' to get a single backslash in the URI.) .SS "PRINT-MISSING-ALL, PMA" .PP Just like \fBprint-missing\fR, this command outputs a list of URIs still needed to completely reassemble the image. However, \fBall\fR alternative download locations are printed instead of just one. In the output, the URIs for a file are separated from other files' URIs with blank lines. The \fB--uri\fR option has the same effect as for \fBprint-missing\fR\&. .SS "VERIFY, VER" .PP Reads \fIimage\fR (presumably generated with \fBmake-image\fR) and `\fI\&.template\fR\&', checks for correct checksum of image. .PP The template data does not only contain checksums of the individual parts, but also of the image as a whole. \fBmake-image\fR already performs a number of internal checks, but if you like, you can additionally check the image with this command. .SS "SCAN, SC" .PP Reads all the \fIFILES\fR and enters them into the cache, unless they are already cached. The \fB--cache\fR option must be present for this command. .TP \fB--no-scan-whole-file\fR \fBThis is the default.\fR This only causes the first \fB--md5-block-size\fR bytes of each file to be read. If the cache is used later by \fBjigdo-file make-image\fR, the rest of the file will be read once these first bytes are recognized in the input image. .TP \fB--scan-whole-file\fR Immediately read the entire file contents and store them in the cache. .SS "MD5SUM, MD5" .PP Reads all the \fIFILES\fR and prints out MD5 checksums of their contents. This command is quite similar to \fBmd5sum\fR(1), except that the checksum is output in the Base64-like encoding which is also used elsewhere by \fBjigdo-file\fR\&. .PP The \fIFILES\fR arguments are processed in the same way as with the other commands, which means that recursion automatically takes place for any arguments that are directories, and that symbolic links are not listed except when the file(s) they point to are not reachable directly. .PP In the checksum list printed on standard output, only the part of the filename following any `//\&' (or `\\.\\\&' on Windows) is printed. Any \fB--cache\fR will be used for querying files' MD5 checksums and/or writing the checksums of scanned files. .SS "LIST-TEMPLATE, LS" .PP Reads a `\fI\&.template\fR\&' file and outputs low-level information about the image and all parts contained in it, including offset, length and checksum. .PP You can also use this command with temporary image files (by specifying something like \fB--template=imagename.tmp\fR) - in that case, the output also distinguishes between parts that have been written to the image and parts that haven't. .PP The exact output format may change incompatibly between different jigdo releases. The following different types of lines can be output. `have-file' only occurs for `\fI\&.tmp\fR\&' files, indicating a file that has already been successfully written to the temporary file: .nf in-template \fIoffset-in-image length\fR need-file \fIoffset-in-image length file-md5sum filestart-rsyncsum\fR have-file \fIoffset-in-image length file-md5sum filestart-rsyncsum\fR image-info \fIimage-length image-md5sum rsyncsum-size\fR .fi .SH "DETAILS" .PP Jigsaw Download was created with the format of ISO9660 CD images in mind - however, the following also applies to many other filesystem formats, as well as to `tar' archives and uncompressed `zip' archives. A CD image contains both information for organizing the filesystem (header with disc name etc., ISO9660 directory data, data of extensions such as Joliet or RockRidge, zero padding) and the files contained on the CD. An important property that jigdo relies on is that each file is stored in one contiguous section of the image; it is not split into two or more parts. .PP When \fBjigdo-file\fR is given a number of files that might be contained in an image, it detects whether any of the files are present using a `rolling checksum' inspired by the one used by \fBrsync\fR(1)\&. The resulting data is written to the `\fI\&.template\fR\&' file: If a section of the image could not be matched (e.g. it was directory information), the data is compressed and written directly to the template. However, if a matching file was found, its data is omitted from the template. Instead, only a reference (an MD5 checksum of the file) is inserted in the template. .PP Note that the template data only contains binary data, it does not contain any filenames or URIs, since it cannot be easily edited in case any of these values need to be changed. All that information is stored in the `\fI\&.jigdo\fR\&' file, a text file to which you can add URLs for your server(s). The jigdo file provides a mapping for each MD5 checksum to one or more alternative download locations for the corresponding part. .PP Apart from the mapping of MD5 sums to URIs, the jigdo file also contains an URI pointing to a download location for the template file. This way, the \fBjigdo\fR download tool only needs to be given one URI (that of the `\fI\&.jigdo\fR\&' file) to be able to download and reassemble the complete image. .SH "FORMAT OF .JIGDO FILES" .PP The overall format of `\fI\&.jigdo\fR\&' files follows that of `\fI\&.ini\fR\&' files, as also used by the Gnome and KDE projects for some data. The file is organized into sections, each of which is preceded by a line reading `[Sectionname]\&'. Within each section, lines have the form `Label=Value'. Such lines are also called `entries' below. All `\fI\&.jigdo\fR\&' files use UTF-8 as their character encoding. .PP Comments are introduced with the `#\&' character and extend to the end of the line. Whitespace is ignored at line start and end as well as to the left and right of section names and the `=\&' in entries. Furthermore, the jigdo utilities split up the text of the entry value (i.e. the part after the `=\&') into whitespace-separated words, much like the Unix shell. Single \&'' and double "" quotes can be used to prevent that e.g. URIs containing whitespace are split apart. Similarly, characters with special meaning (the characters \&'"#\\ and space/tab) must be quoted with \\ to appear in the value. As with the shell, there is a difference between \&'\~\&' and "\~": Within \&'\~\&', the characters "#\\ and whitespace lose their special meaning and become ordinary characters, whereas within "\~", only the characters \&'# and whitespace lose their special meaning - in other words, backslash escapes still work inside "\~", but not \&'\~\&'\&. .PP `\fI\&.jigdo\fR\&' files can optionally be compressed with \fBgzip\fR(1)\&. \fBjigdo-file\fR always outputs uncompressed files, which you can compress yourself. \fBjigdo-lite\fR supports single uncompressed and compressed files. .PP (Behaviour which may change in the future and which should not be relied upon: \fBjigdo\fR additionally supports any number of concatenated plaintext and gzipped parts in the files - for example, you can compress a `\fI\&.jigdo\fR\&' file and then add a couple of lines of uncompressed data to the end.) .PP In all cases, the `\fI\&.gz\fR\&' extension should be removed from the filename - the tools will determine automatically from the file contents whether a file is compressed or not. .PP Below is a description of the individual section names used by jigdo. .SS "JIGDO SECTION" .nf [Jigdo] Version=1.1 Generator=jigdo-file/1.0.0 .fi .PP Information about the version of the jigdo file format used, and the program that generated it. There should be one such section per `\fI\&.jigdo\fR\&' file. .SS "IMAGE SECTION" .nf [Image] Filename=\fI"filename for saving on user's disc"\fR Template=\fI"URI where to fetch template file"\fR Template-MD5Sum=OQ8riqT1BuyzsrT9964A7g ShortInfo=\fIsingle-line description of the image (200 characters max.)\fR Info=\fIlong description (5000 characters max.)\fR .fi .PP The value for the `Template' entry can be either an URL (absolute or relative to the URL of the jigdo file) or a string of the form `\fILabel\fR:\fIpathname\fR\&' (\fBUNIMPLEMENTED\fR), as described below. .PP The `Template-MD5Sum' entry is added by \fBjigdo-file\fR and specifies the MD5 checksum of the generated `\fI\&.template\fR\&' file. It is used by \fBjigdo\fR to detect cases where the downloaded template data is corrupted or belongs to a different image. .PP Unlike other entry values, the values of the `ShortInfo\&' and `Info\&' entries are \fBnot\fR split up into words, instead all quoting is preserved. .PP The value of the `Info\&' entry is special in that \fBjigdo\fR(1) can optionally parse XML markup it contains. If the markup has errors such as unbalanced/unsupported tags, the string is displayed literally, without XML parsing. Supported tags are (bold), (italic), (typewriter font), (underline), (larger font), (smaller font) and
(linebreak). Supported entities include < (`<\&'), > (`>\&') and & (`&\&'). Note that the whole `Info\&' entry must be on one line in the jigdo file. .PP This section may occur multiple times, but all except the first one will be ignored. This is useful e.g. when creating a `\fI\&.jigdo\fR\&' file for a DVD image when you already have `\fI\&.jigdo\fR\&' files for CDs with the same content: You can simply `[Include]\&' (see below) the CDs' jigdo files at the end of the DVD jigdo file, after its `[Image]\&' section. .SS "PARTS SECTION" .nf [Parts] xJNkjrq8NYMraeGavUpllw=LabelA:part0 GoTResP2EC6Lb_2wTsqOoQ=LabelA:part1 kyfebwu6clbYqqWUdFIyaw=LabelB:some/path/part2 -J9UAimo0Bqg9c0oOXI1mQ=http://some.where.com/part3 .fi .PP All lines in the section, which provides the mapping from MD5 checksums to URIs, have the same format: On the left side of the `=\&' the checksum (encoded with a Base64-like encoding) is given, and on the right a string corresponding to the part with this checksum; either a complete URI or a string of the form `\fILabel\fR:\fIpathname\fR\&', which is expanded into one or more URIs by looking up the definition(s) for the \fILabel\fR in the `[Servers]\&' section. .PP In case a particular MD5 checksum cannot be found in any `[Parts]\&' section by \fBjigdo\fR, the program will perform a lookup for `MD5Sum:\fI\fR\&', e.g. for `MD5Sum:xJNkjrq8NYMraeGavUpllw\&' if you deleted the line for `part0' above. .PP A checksum appearing multiple times in this section indicates alternative download locations for the part. .PP There may be any number of `[Parts]\&' sections in the file; they are all considered when looking up MD5 checksums. .PP \fBjigdo-file\fR always puts the `[Parts]\&' section at the end of the file, and it even rearranges any file specified with \fB--merge\fR to have only one such section at the end. This is done to allow \fBjigdo\fR to display the information from the `[Image]\&' section while the rest of that file is still being downloaded. .SS "SERVERS SECTION" .nf [Servers] LabelA=http://myserver.org/ LabelA=ftp://mirror.myserver.org/ LabelB=LabelC:subdirectory/ LabelC=http://some.where.com/jigdo/ .fi .PP All lines in the section, which provides the mapping from server labels to server locations, have the same format: On the left side of the `=\&' the label name is given, and on the right the value to expand the label name to. .PP A label name appearing multiple times in this section indicates alternative download locations for the parts that use the label in the `[Parts]\&' section. This notation makes it very easy to add mirrors to the jigdo file. .PP As shown by the example above, the label values may themselves reference other labels. In this case, the entry `LabelB:some/path/part2' in the `[Parts]\&' section will expand to `http://some.where.com/jigdo/subdirectory/some/path/part2'. Loops in the label definitions result in undefined behaviour and must be avoided. .PP There may be any number of `[Servers]\&' sections in the file; they are all considered when looking up labels. Either of `[Parts]\&' or `[Servers]\&', but not both, can be omitted from the jigdo file. .SS "COMMENT SECTION" .nf [Comment] Any text, except that lines must not begin with `['. .fi .PP All text following a `[Comment]\&' or `[comment]\&' line is ignored, up to the next line with a section label. .SS "INCLUDE DIRECTIVE" .nf [Include http://some.url/file.jigdo] .fi .PP Lines of this form cause the content of the specified jigdo file to be downloaded and parsed just like the main jigdo file. The effect will be the same as copying the included file's contents into the file which contains the include directive. (Exception: Any relative URLs are always resolved using the URL of the `\fI\&.jigdo\fR\&' file that contains that relative URL.) .PP The URL argument can be an absolute or relative URL. Relative URLs are assumed to be relative to the URL of the jigdo file which contains the include directive. Includes can be nested, but it is an error to create a loop of include directives. It is \fBnot\fR possible to use URLs of the form `\fILabel\fR:\fIpathname\fR\&'. .PP The URL cannot be quoted with "". Any `]\&' characters in the argument must be escaped as `%5D\&', and any spaces as `%20\&'. .PP Include directives are only supported by \fBjigdo\fR, they are ignored by \fBjigdo-lite\fR\&. .PP An include directive terminates any previous section, but it does not start a new one. In other words, a new section must always be started after the include line, \fBjigdo\fR does not allow normal entries to appear below the `[Include]\&'. .SH "CACHE FILES" .PP Any file specified with the \fB--cache\fR option is used to store information about the \fIFILES\fR presented to \fBjigdo-file\fR\&. When querying the cache, a file is considered unchanged (and the cached data is used) only if filename, file size and last modification time (mtime) match exactly. For the filename match, not the entire file name is used, but only the part following any `//\&', so that any changes to the part before the `//\&' will not invalidate the cache. .PP Old cache entries are removed from the cache if they have not been read from or written to for the amount of time specified with \fB--cache-expiry\fR\&. Entries are \fBnot\fR immediately removed from the cache if the file they refer to no longer exists - this makes it possible to cache information about files on removable media. .PP Cache expiry only takes place \fBafter\fR \fBjigdo-file\fR has done its main work - if any old entries are accessed before expiry takes place, they will be kept. For example, if the program is run using the default expiry time of 30 days, but accesses a cache file with entries generated 2 months ago, then entries in that cache \fBwill\fR be considered, and only those cache entries that were not needed during the program run will be expired. .PP Due to a peculiarity of the underlying database library (libdb3), cache files never shrink, they only grow. If a large number of entries was expired from your cache file and you want it to shrink, you can either just delete it (of course then everything will have to be regenerated) or use the utilities accompanying libdb3 to dump and restore the database, with a command like `\fBdb3_dump \fIold-cache.db\fB | db3_load \fInew-cache.db\fB\fR\&'. For Debian, these programs are supplied in the package `libdb3-util'. .PP If a different \fB--md5-block-size\fR is specified, the entire file needs to be re-read to update its cache entry. If a different \fB--min-length\fR is specified, only the first `md5-block-size' bytes of the file need to be re-read. .SH "EXAMPLES" .SS "PREPARING YOUR CD IMAGE FOR DISTRIBUTION" .PP You have created a CD image `\fIimage.iso\fR\&' from some of the files stored in the directory `\fI/home/ftp\fR\&' on your harddisc, which is also available online as `ftp://mysite.org'. As you don't want to waste space by effectively hosting the same data twice (once as files on the FTP server, once inside the image), and you are fed up with users' downloads aborting after 200MB and their restarting the download dozens of times, you decide to use jigdo. How do you prepare the image for download? .PP In fact, only one command is necessary: .sp .RS .PP \fBjigdo-file make-template --image=image.iso --jigdo=/home/ftp/image.jigdo --template=/home/ftp/image.template /home/ftp// --label Mysite=/home/ftp --uri Mysite=ftp://mysite.org/\fR .RE .PP People can now point \fBjigdo\fR at `ftp://mysite.org/image.jigdo' to download your image. The template file needs to be accessible as `ftp://mysite.org/image.template'. .PP Note that nothing prevents you from doing the same for an FTP server that isn't administrated by you - in that case, you only need to host the `\fI\&.jigdo\fR\&' and `\fI\&.template\fR\&' files on your own server/homepage. .SS "PREPARING AN ARBITRARY LARGE FILE FOR DISTRIBUTION" .PP We assume that you have a large file that is not a filesystem, e.g. `\fImovie.mpeg\fR\&'. Because of space problems, you want to distribute the data on two servers. .PP In this case, the parts of the image need to be generated artificially with the \fBsplit\fR command. For example, to create chunks of 4MB each, use `\fBsplit -b 4m movie.mpeg part\fR\&'. Copy the resulting files `\fIpartXX\fR\&' into two directories `\fI1\fR\&' and `\fI2\fR\&' that you create, according to how you want the files distributed between the servers. Next, create the jigdo and template files with `\fBjigdo-file make-template --image=movie.mpeg 1// 2//\fR\&'. You will need to edit the `\fI\&.jigdo\fR\&' file and provide the right URIs for the two servers that you are going to upload the `\fIpartXX\fR\&' files to. .SS "CUSTOMIZED VERSIONS OF IMAGES" .PP Because it is possible to assign a different URI for each part of an image if necessary, jigdo is very flexible. Only one example is the possibility of customized versions of images: Suppose that someone is distributing a CD image, and that you want to make a few small changes to it and redistribute your own version. You download the `\fIofficial.iso\fR\&' CD image with \fBjigdo\fR (passing it the URL of `\fIofficial.jigdo\fR\&'), write it to CD-R, make your changes (say, adding files from the `\fImyfiles\fR\&' directory on your harddisc) and produce your own version, `\fImyversion.iso\fR\&'. Next, you instruct \fBjigdo-file\fR to create the jigdo and template files for your modified image, using the command .sp .RS .PP \fBjigdo-file make-template --image=myversion.iso /mnt/cdrom/ myfiles// --label My=myfiles/ --uri My=http://my.homepage.net/ --merge=official.jigdo\fR .RE while `\fIofficial.iso\fR\&' is mounted under `\fI/mnt/cdrom\fR\&'. By using \fB--merge\fR, you have told \fBjigdo-file\fR to take the contents of `\fIofficial.jigdo\fR\&', add to it a new `[Image]\&' section for `\fImyversion.iso\fR\&' and write the resulting jigdo file to `\fImyversion.jigdo\fR\&' - so now `\fImyversion.jigdo\fR\&' offers two images for download, the original version and your modified version. (If you do not want it to offer the official version, edit it and remove the `[Image]\&' section that lists `\fIofficial.iso\fR\&'.) .PP Now you can upload the `\fI\&.jigdo\fR\&' file, the `\fI\&.template\fR\&' file and also the files in `\fImyfiles\fR\&' to `http://my.homepage.net/'. Thus, for people to download your modified image, you do \fBnot\fR need to upload the complete image contents to your web space, but only the changes you made! .PP (In case you only made very few changes, you could also omit the `myfiles' parameter in the command above, then all your changes end up in the new template file.) .SS "COMBINING MANY JIGDO-MANAGED IMAGES INTO ONE" .PP It is also no problem to combine data from several sources that use jigdo. For example, if of five different and unrelated servers each one distributes a different CD image via jigdo, you can create a customized DVD image that contains the data from all these CDs. When people use \fBjigdo\fR to download your image, the individual files on the DVD are fetched from the same sources as the original CDs. .PP Consequently, even though you will be distributing a 3.2GB file via your web space, the actual amount of data that is stored on your server will only be in the order of several MBs. .SH "BUGS" .PP For certain contents of one of the input files, most notably a sequence of zero bytes longer than \fB--min-length\fR at the start of the file and an area of zeros preceding the file data in the image, \fBjigdo-file make-template\fR may fail to find the file in the image. Unfortunately, this restriction cannot be avoided because the program could become very slow otherwise. If you use the \fB--debug\fR option, all instances of \fBjigdo-file\fR discarding possible matches are indicated by lines containing the word `DROPPED\&'. .PP In fact, not only all-zeroes files trigger this behaviour, but also files which contain at their start a long sequence of short identical strings. For example, both a file containing only `a\&' characters and one containing `abcabcabcabc\&...' are problematic. .SH "SEE ALSO" .PP \fBjigdo\fR(1) (NOT YET IMPLEMENTED), \fBjigdo-lite\fR(1), \fBjigdo-mirror\fR(1), \fBsplit\fR(1) (or `\fBinfo split\fR\&'), \fBfind\fR(1) (or `\fBinfo find\fR\&'), \fBmkisofs\fR(1), \fBmd5sum\fR(1) .SH "AUTHOR" .PP Jigsaw Download was written by Richard Atterer , to make downloading of CD ROM images for the Debian Linux distribution more convenient. jigdo-0.7.3/doc/jigdo-file.html0000644000175000017500000015562110433432655016112 0ustar richardrichard jigdo-file

jigdo-file

Name

jigdo-file -- Prepare files for Jigsaw Download (distribution of huge files, e.g. CD images).

Synopsis

jigdo-file { COMMAND } [--image=cdrom.iso] [--jigdo=cdrom.jigdo] [--template=cdrom.template] [--force] [MORE OPTIONS] [FILES | --files-from=f]
Common COMMANDs: make-template, make-image, verify

DESCRIPTION

Jigsaw Download, or short jigdo, is a scheme developed primarily to make it easy to distribute huge filesystem images (e.g. CD (ISO9660) or DVD (UDF) images) over the internet, but it could also be used for other data which is awkward to handle due to its size, like audio/video files or large software packages.

jigdo tries to ensure that the large file (always called image from now on) is downloaded in small parts which can be stored on different servers. People who want to download the image do so by telling the jigdo(1) (NOT IMPLEMENTED YET) download tool to process one `.jigdo' file; using it, jigdo downloads the parts and reassembles the image. jigdo-file is used to prepare the files for download.

What makes jigdo special is that the parts that are used to reconstruct the image can have any size and content - they only need to be contained in a contiguous region anywhere in the image.

For example, if you wish to distribute an ISO9660 image which contains a snapshot of an FTP server, you can instruct jigdo-file to prepare the download data in such a way that when people use jigdo to download the image, jigdo actually fetches the individual files from the FTP server and assembles them into an exact copy of your image - during the download! (If the image is not a filesystem dump, you can use split(1) to create the small parts that the image will be reassembled from.)

You are completely free to choose where the individual parts of the image are stored: They may be in entirely different directories on different servers (e.g. because of storage/bandwidth constraints), but this is invisible to the people downloading your image. The information about available servers only needs to be added to the `.jigdo' file by you before distributing it.

The `DETAILS' section below contains technical details on how jigdo works. The `EXAMPLES' section lists a number of common scenarios and may help you to get an idea of what jigdo is useful for.

OPTIONS

Many options are specific to a particular COMMAND; the ones below are general or used by several commands. Further options are listed below with the individual commands. All options are silently ignored if they are not applicable to the current command. For any BYTES parameters to options, you can append one of the letters `k', `M' or `G' to the amount you specify, to indicate kilobytes, megabytes or gigabytes.

-h --help

Output short summary of commands and options.

-H --help-all

Output complete summary of commands and options.

-v --version

Output program version.

-i --image=cdrom.iso

Specify location of the file containing the image. The image is the large file that you want to distribute.

-j --jigdo=cdrom.jigdo

Specify location of the Jigsaw Download description file. The jigdo file is a human-readable file generated by jigdo-file, to which you add information about all the servers you are going to upload the files to. jigdo will download this file as the first step of retrieving the image.

-t --template=cdrom.template

Specify location of the image `template' file. The template file is a binary file generated by jigdo-file, it contains information on how to reassemble the image and also (in compressed form) all the data from the image which was not found in any of the parts.

Depending on the command, each of these three files is used sometimes for input, sometimes for output. If the file is to be used for output for a particular command and the output file already exists, jigdo-file exits with an error, unless --force is present.

In most cases, you will only need to specify one out of -i -j -t, because any missing filenames will be deduced from the one you specify. This is done by first stripping any extension from the supplied name and then appending nothing (if deducing --image), `.jigdo' or `.template'.

-r --report=default|noprogress|quiet|grep

Control how verbose the program is, and what format the output has: noprogress is the same as default except that no `x% done' progress messages are printed. quiet restricts the output to what is absolutely necessary, mostly error messages. grep is only different from default for the make-template command: It enables output in a simple `<offset> <file>' format which is useful when searching for binary files in other binary files.

-f --force

Overwrite existent output files without complaining.

--no-force

This is the default. Refuse to overwrite existent output files.

-c --cache=jigdo-cache.db

jigdo-file usually needs to read the entire contents of all the FILES you specify. If you use it repeatedly (e.g. because you make a new CD image available daily), caching the file information will increase the program's speed significantly. The cache file is automatically created if it is not yet present. Data is usually both read from and written to it.

--no-cache

This is the default. Do not use a cache.

--cache-expiry=SECONDS

Set maximum age of cache entries. Any entries older than this will be removed from the cache. The default is 30 days. You can append one of the letters `h', `d', `w', `m', `y' to denote hours, days, weeks, months or years, respectively. A value of `0' or `off' disables expiry, so that all entries will stay in the cache forever. See the section `CACHE FILES' below for more information.

--readbuffer=BYTES

Set size of internal buffers. The default is 128k - if you have a fast disc, increasing this value may make jigdo-file faster, but in general, changing it is not necessary.

--md5-block-size=BYTES

Uninteresting internal parameter. Set size of blocks into which files are subdivided. The default is 128k. If you change it, any cache file will have to be regenerated. Internally, jigdo-file may choose to use a slightly larger or smaller value.

-T --files-from=file

Read file and directory names from the specified file. If file is `-', read names from standard input. Each line in the file is taken as a name, so the names may contain spaces, but not newline characters. An empty line causes jigdo-file to stop reading from the file.

find(1) is a powerful tool for generating file lists, but make sure to use `find -type f' if possible - otherwise, if you instruct find to output both a filename and a symlink to that filename, jigdo-file will read the file contents twice.

--hex

Output checksums in hexadecimal instead of Base64-like format. This should not be used with the make-template command, because the resulting `.jigdo' file violates the `.jigdo' file format. Its intended use is to make jigdo-file more interoperable with other Unix shell utilities like md5sum(1).

--no-hex

This is the default. Use jigdo's own Base64-like encoding of checksums.

--debug[=help|=all|=UNIT,~UNIT... ]

Switch on or off debugging output. Just `--debug' is equivalent to `--debug=all'. The argument is a comma-separated list of unit names for which debugging output is to be enabled, or disabled if the name is preceded by `~'. The special name `all' means all units. By default, debugging output is switched off except for the units `assert' and `general'. The exact list of available units for which debugging can be switched on depends on whether jigdo was compiled with debugging support - the list can be printed with `--debug=help'.

FILES

Names of files or directories to use as input. These are the parts that are contained in the image. In case one of the names is a directory, the program recursively scans the directory and adds all files contained in it. While doing this, it follows symbolic links, but avoids symlink loops.

If one of the filenames starts with the character `-', you must precede the list of files with `--'. A value of `-' has no special meaning in this list, it stands for a file whose name is a single hyphen.

COMMANDS

The command name is the first non-option argument passed to jigdo-file. Most commands have short abbreviations as well as long names. The short command names should not be used in scripts - there may be incompatible changes to them in the future!

make-template, mt

Reads image and FILES, creates `.jigdo' and `.template'. This is the main functionality of jigdo-file.

It is possible to specify both --image=- and --files-from=-. In this case, first the list of files is read from standard input until an empty line is encountered. Everything following it is assumed to be the image data. This can be useful if you use mkisofs(1) or similar programs that can output the complete image on their standard output, because there is no need to store the image on disc temporarily.

If a FILES argument contains the characters `//' (Unix) or `\.\' (Windows), this has special meaning. In the final jigdo file that users will download, each of the parts is referenced in the `[Parts]' section with a URI of the form `Label:some/filename'. (See `FORMAT OF .JIGDO FILES' below for a detailed description.) The `[Servers]' section gives a mapping of labels to servers on the internet, with lines like `Label=http://myserver.org/jigdofiles/'. Using this information, jigdo will create the final download URI for the part, `http://myserver.org/jigdofiles/some/filename'. Specifying `//' (or `\.\') in a file or directory name serves to `cut off' the names at the right directory level. For example, if the Unix path of one of your FILES is `/path/some/filename', you can tell jigdo-file to cut off after the `/path' by passing it the argument `/path//some/filename', or `/path//' if you want the whole directory scanned. The path names need not be absolute; `somedirectory//' is also possible.

--label Label=/path

Specify a name to use as the label name for a path on disc. (Influences the output jigdo file.) If you used `//' in the FILES arguments as described above, jigdo-file will by default pick label names automatically (`A', `B' etc.). With this option, you can give labels more meaningful names. Note that the label name will only be used if one or more FILES begin with `/path//'.

Try to use label names that start with uppercase characters, to disambiguate them clearly from protocol names like `http', `ftp'.

--uri Label=http://some.server.org/

By default, using --label as described above will cause lines of the form `Label=file:/path/' to be written to the `[Servers]' section of the output jigdo file. If you want to override the `file:' URI so that the line reads `Label=http://some.server.org/', you can do so by specifying --uri along with --label. Giving just --uri Label=... without the corresponding --label Label=... has no effect, and even if you specify both, an entry is only added to the `[Servers]' section if the label is referenced by at least one `[Parts]' entry.

The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\ or '\\' to get a single backslash in the URI.)

The mapping specified with an --uri option is ignored if it is already present in the output jigdo file.

Users of the Windows version may notice that the `\' directory separators are converted into `/' in the `file:' URIs that are generated by default. This is done to increase cross-platform compatibility of `file:' - the print-missing command of the Windows version will automatically re-convert the characters when it prints the URIs. In case you supply your own `file:' URIs under Windows using --uri, you must also exchange `/' and `\'.

-0 to -9

Set amount of compression in the output template file, from -0 (no compression) to -9 (maximum compression). The default is -9, which can make the template generation quite slow. By default, the compression algorithm used is the same as for gzip(1).

--gzip and --bzip2

Choose between the gzip and bzip2 compression algorithms. The default is gzip. Bzip2 usually gives a better compression ratio, but compression is significantly slower than with gzip.

--min-length=BYTES

Set minimum length of a part for jigdo-file to look for it in the image. The default is 1k. Parts smaller than this will never be found in the image, so their data will be included in the template file. The search algorithm used requires such a minimum length, otherwise template generation could become extremely slow. If you know for sure that all your FILES are larger than a certain amount, you can increase jigdo-file's speed slightly by specifying the amount with this option. There is a hard-wired absolute minimum of 256 bytes - anything lower will silently be set to 256.

--merge=FILE

Include the contents of FILE in the output `.jigdo' file. The file can contain data which you want added to the output (for example, a `[Servers]' section with a list of your servers as entries), or it can be the jigdo file output by an earlier run of jigdo-file.

It is possible to specify the same file for input with --merge and for output with --jigdo. However, you will also need to use --force to make the program overwrite the old version of the jigdo file with the new one. FILE can be `-' for standard input.

When adding new information to the supplied file, jigdo-file will not insert new lines into the `[Parts]' section if an entry for the same MD5 checksum (but not necessarily with the same URI!) already exists, and it will not insert new lines into the `[Servers]' section if a completely identical entry already exists.

When reading in the existing FILE, the behaviour is slightly different: The program preserves entries in the `[Parts]' section with identical checksum, but different URIs. For completely identical entries (same checksum and URI), only one entry is preserved and the duplicates are removed. The `[Servers]' section is left untouched.

--image-section

This is the default. Causes jigdo-file to add an `[Image]' section to the `.jigdo' file.

As an exception, a new `[Image]' section is not added if you use --merge and the file to merge contains an `[Image]' section with a line which reads `Template-MD5Sum=' (end of line after the `='). In this case, the generated template data's MD5 checksum value is just added after the `=' of the first line of this form in the file - no whole new `[Image]' section is appended. This behaviour is useful because it allows you to pass via --merge an `[Image]' section with arbitrary content and then have the MD5 checksum automatically added by jigdo-file. The section `FORMAT OF .JIGDO FILES' below explains the `[Image]' section contents in greater detail.

--no-image-section

Do not include an `[Image]' section in the `.jigdo' file. You need to add one yourself if you use this option. However, doing that is not easy (you also need to add a `Template-MD5Sum' line with the correct checksum, or jigdo will complain), so use of this option is discouraged.

--servers-section

This is the default. Causes jigdo-file to add a `[Servers]' section to the `.jigdo' file. This default section uses `file:' URIs, which allows for immediate reassembly of the image from the local filesystem, and is also useful if you want to edit the file manually and replace the `file:' URIs with other URIs.

--no-servers-section

Do not add a `[Servers]' section at the end of the `.jigdo' file. Useful e.g. if you are going to append the section with a script.

--match-exec=SHELLCOMMAND

Whenever a file is found in the image, execute the supplied command string by passing it to a shell. jigdo-file sets up a number of environment variables with information about the file match. For example, if the file `/path//a/b/file' was found in the image and `Label:a/b/file' is going to be written to the `.jigdo' file:

  • LABEL="Label" - Name of the label for the file. The example assumes that `--label Label=/path' was specified by you. In the absence of such an option, LABEL will be set but empty.

  • LABELPATH="/path/" - The path corresponding to the label, or in other words, the prefix of the matched file's path that will not appear in the output `.jigdo' file. Is set even without any `--label' option present. Ends with a slash.

  • MATCHPATH="a/b/" - The rest of the path, without the leafname of the matched file. Is either empty or ends with a slash.

  • LEAF="file" - The leafname of the matched file.

  • MD5SUM="lNVdUSqbo2yqm33webrhnw" - The md5sum of the matched file, in Base64-like format.

  • FILE="/path//a/b/file" - For convenience, the complete path of the file. The variable is always set to $LABELPATH$MATCHPATH$LEAF.

Please be careful to correctly quote the string passed to this option, otherwise your supplied command will not work with filenames that contain spaces. As an example, to create a backup of hard links to the matched files, use the following option: --match-exec='mkdir -p "${LABEL:-.}/$MATCHPATH" && ln -f "$FILE" "${LABEL:-.}/$MATCHPATH$LEAF"'

By default, no command is executed. Use --match-exec="" to remove a command string which was set with an earlier use of this option.

--greedy-matching

This is the default. Imagine that your image contains a .tar file which in turn contains another file x, and that you provide both the .tar and the files inside it on the command line. When jigdo-file scans the image, it encounters the beginning of the .tar file, and then the file x.

At this point, a decision must be made: Should the smaller file x be recorded as matched, or should it be ignored in favour of the larger (and thus better) match of the .tar file? Unfortunately, at this point it is not clear whether there will actually be a full match of the .tar, so by default, the program prefers the small match.

--no-greedy-matching

In the case where a large partial match is present and a shorter match has been confirmed, ignore the small match. (See the option above.)

make-image, mi

Reads `.template' and FILES, creates image (or `imagename.tmp'). Provides a rudimentary way of reassembling images - jigdo is usually better suited for this task. However, in contrast to jigdo, no `.jigdo' file is required.

If the image is to be written to a file (and not to standard output), it is possible to create the image in several steps, with several invocations of `jigdo-file make-image', as follows: You first invoke jigdo-file, specifying as many files as are available at this time. The program scans the files, and those that are contained in the image are copied to a temporary file, whose name is formed by appending `.tmp' to the image filename.

For all further files which could be parts of the image, you repeat this process. As soon as all parts are present, the temporary file will be truncated slightly (to delete some administrative data that jigdo-file appends at the end) and renamed to the final image name. The possibility of reassembling the image in several steps is especially useful for gathering files from removable media, e.g. several older CDs.

Scripts using make-image can detect whether image creation is complete by checking the exit status: 0 signals successful creation, whereas 1 means that more files need to be supplied. Other errors result in an exit status of 2 (`recoverable', e.g. file not found) or 3 (non-recoverable, e.g. write error).

--check-files

This is the default. Whenever any part is copied to the image, re-check its checksum against the checksum stored in the template. It is recommended that you leave this switched on, even if it slows down image creation a bit.

--no-check-files

Do not check files' checksums when copying them to the image. This can be safely used when no cache file is used (which means that files will be written to the image immediately after being scanned) or the whole image is checked later with the verify command.

print-missing, pm

Reads `.jigdo', `.template' and (if present) `imagename.tmp', outputs a list of URIs still needed to completely reassemble the image.

Together with the make-image command, this provides most of the functionality of jigdo on the command line.

For each part that is not yet present in the temporary image file, the file checksum is looked up in the `[Parts]' section of the jigdo file. Any label in the corresponding entry is then expanded according to the label definitions in the `[Servers]' section and printed on standard output. jigdo allows you to specify several alternative locations for each label in this section, but print-missing will only output the first one for each missing part.

If the checksum cannot be found in the `[Parts]' section (this Should Not Happen unless you deleted that section), a lookup is instead made for `MD5Sum:<checksum>', just like with jigdo. (Thus, if you want to get rid of the `[Parts]' section, you can do so if you rename each part to its own checksum.)

--uri Label=http://some.server.org/

Override the entries in the `.jigdo' file for any label with a URI of your choice. With the example above, a `[Parts]' entry of `Label:some/filename' will cause the line `http://some.server.org/some/filename' to be printed.

The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\ or '\\' to get a single backslash in the URI.)

print-missing-all, pma

Just like print-missing, this command outputs a list of URIs still needed to completely reassemble the image. However, all alternative download locations are printed instead of just one. In the output, the URIs for a file are separated from other files' URIs with blank lines. The --uri option has the same effect as for print-missing.

verify, ver

Reads image (presumably generated with make-image) and `.template', checks for correct checksum of image.

The template data does not only contain checksums of the individual parts, but also of the image as a whole. make-image already performs a number of internal checks, but if you like, you can additionally check the image with this command.

scan, sc

Reads all the FILES and enters them into the cache, unless they are already cached. The --cache option must be present for this command.

--no-scan-whole-file

This is the default. This only causes the first --md5-block-size bytes of each file to be read. If the cache is used later by jigdo-file make-image, the rest of the file will be read once these first bytes are recognized in the input image.

--scan-whole-file

Immediately read the entire file contents and store them in the cache.

md5sum, md5

Reads all the FILES and prints out MD5 checksums of their contents. This command is quite similar to md5sum(1), except that the checksum is output in the Base64-like encoding which is also used elsewhere by jigdo-file.

The FILES arguments are processed in the same way as with the other commands, which means that recursion automatically takes place for any arguments that are directories, and that symbolic links are not listed except when the file(s) they point to are not reachable directly.

In the checksum list printed on standard output, only the part of the filename following any `//' (or `\.\' on Windows) is printed. Any --cache will be used for querying files' MD5 checksums and/or writing the checksums of scanned files.

list-template, ls

Reads a `.template' file and outputs low-level information about the image and all parts contained in it, including offset, length and checksum.

You can also use this command with temporary image files (by specifying something like --template=imagename.tmp) - in that case, the output also distinguishes between parts that have been written to the image and parts that haven't.

The exact output format may change incompatibly between different jigdo releases. The following different types of lines can be output. `have-file' only occurs for `.tmp' files, indicating a file that has already been successfully written to the temporary file:

in-template  offset-in-image  length
need-file    offset-in-image  length  file-md5sum  filestart-rsyncsum
have-file    offset-in-image  length  file-md5sum  filestart-rsyncsum
image-info   image-length  image-md5sum  rsyncsum-size

DETAILS

Jigsaw Download was created with the format of ISO9660 CD images in mind - however, the following also applies to many other filesystem formats, as well as to `tar' archives and uncompressed `zip' archives. A CD image contains both information for organizing the filesystem (header with disc name etc., ISO9660 directory data, data of extensions such as Joliet or RockRidge, zero padding) and the files contained on the CD. An important property that jigdo relies on is that each file is stored in one contiguous section of the image; it is not split into two or more parts.

When jigdo-file is given a number of files that might be contained in an image, it detects whether any of the files are present using a `rolling checksum' inspired by the one used by rsync(1). The resulting data is written to the `.template' file: If a section of the image could not be matched (e.g. it was directory information), the data is compressed and written directly to the template. However, if a matching file was found, its data is omitted from the template. Instead, only a reference (an MD5 checksum of the file) is inserted in the template.

Note that the template data only contains binary data, it does not contain any filenames or URIs, since it cannot be easily edited in case any of these values need to be changed. All that information is stored in the `.jigdo' file, a text file to which you can add URLs for your server(s). The jigdo file provides a mapping for each MD5 checksum to one or more alternative download locations for the corresponding part.

Apart from the mapping of MD5 sums to URIs, the jigdo file also contains an URI pointing to a download location for the template file. This way, the jigdo download tool only needs to be given one URI (that of the `.jigdo' file) to be able to download and reassemble the complete image.

FORMAT OF .JIGDO FILES

The overall format of `.jigdo' files follows that of `.ini' files, as also used by the Gnome and KDE projects for some data. The file is organized into sections, each of which is preceded by a line reading `[Sectionname]'. Within each section, lines have the form `Label=Value'. Such lines are also called `entries' below. All `.jigdo' files use UTF-8 as their character encoding.

Comments are introduced with the `#' character and extend to the end of the line. Whitespace is ignored at line start and end as well as to the left and right of section names and the `=' in entries. Furthermore, the jigdo utilities split up the text of the entry value (i.e. the part after the `=') into whitespace-separated words, much like the Unix shell. Single '' and double "" quotes can be used to prevent that e.g. URIs containing whitespace are split apart. Similarly, characters with special meaning (the characters '"#\ and space/tab) must be quoted with \ to appear in the value. As with the shell, there is a difference between ' ' and " ": Within ' ', the characters "#\ and whitespace lose their special meaning and become ordinary characters, whereas within " ", only the characters '# and whitespace lose their special meaning - in other words, backslash escapes still work inside " ", but not ' '.

`.jigdo' files can optionally be compressed with gzip(1). jigdo-file always outputs uncompressed files, which you can compress yourself. jigdo-lite supports single uncompressed and compressed files.

(Behaviour which may change in the future and which should not be relied upon: jigdo additionally supports any number of concatenated plaintext and gzipped parts in the files - for example, you can compress a `.jigdo' file and then add a couple of lines of uncompressed data to the end.)

In all cases, the `.gz' extension should be removed from the filename - the tools will determine automatically from the file contents whether a file is compressed or not.

Below is a description of the individual section names used by jigdo.

Jigdo section

[Jigdo]
Version=1.1
Generator=jigdo-file/1.0.0

Information about the version of the jigdo file format used, and the program that generated it. There should be one such section per `.jigdo' file.

Image section

[Image]
Filename="filename for saving on user's disc"
Template="URI where to fetch template file"
Template-MD5Sum=OQ8riqT1BuyzsrT9964A7g
ShortInfo=single-line description of the image (200 characters max.)
Info=long description (5000 characters max.)

The value for the `Template' entry can be either an URL (absolute or relative to the URL of the jigdo file) or a string of the form `Label:pathname' (UNIMPLEMENTED), as described below.

The `Template-MD5Sum' entry is added by jigdo-file and specifies the MD5 checksum of the generated `.template' file. It is used by jigdo to detect cases where the downloaded template data is corrupted or belongs to a different image.

Unlike other entry values, the values of the `ShortInfo' and `Info' entries are not split up into words, instead all quoting is preserved.

The value of the `Info' entry is special in that jigdo(1) can optionally parse XML markup it contains. If the markup has errors such as unbalanced/unsupported tags, the string is displayed literally, without XML parsing. Supported tags are <b></b> (bold), <i></i> (italic), <tt></tt> (typewriter font), <u></u> (underline), <big></big> (larger font), <small></small> (smaller font) and <br/> (linebreak). Supported entities include &lt; (`<'), &gt; (`>') and &amp; (`&'). Note that the whole `Info' entry must be on one line in the jigdo file.

This section may occur multiple times, but all except the first one will be ignored. This is useful e.g. when creating a `.jigdo' file for a DVD image when you already have `.jigdo' files for CDs with the same content: You can simply `[Include]' (see below) the CDs' jigdo files at the end of the DVD jigdo file, after its `[Image]' section.

Parts section

[Parts]
xJNkjrq8NYMraeGavUpllw=LabelA:part0
GoTResP2EC6Lb_2wTsqOoQ=LabelA:part1
kyfebwu6clbYqqWUdFIyaw=LabelB:some/path/part2
-J9UAimo0Bqg9c0oOXI1mQ=http://some.where.com/part3

All lines in the section, which provides the mapping from MD5 checksums to URIs, have the same format: On the left side of the `=' the checksum (encoded with a Base64-like encoding) is given, and on the right a string corresponding to the part with this checksum; either a complete URI or a string of the form `Label:pathname', which is expanded into one or more URIs by looking up the definition(s) for the Label in the `[Servers]' section.

In case a particular MD5 checksum cannot be found in any `[Parts]' section by jigdo, the program will perform a lookup for `MD5Sum:<checksum>', e.g. for `MD5Sum:xJNkjrq8NYMraeGavUpllw' if you deleted the line for `part0' above.

A checksum appearing multiple times in this section indicates alternative download locations for the part.

There may be any number of `[Parts]' sections in the file; they are all considered when looking up MD5 checksums.

jigdo-file always puts the `[Parts]' section at the end of the file, and it even rearranges any file specified with --merge to have only one such section at the end. This is done to allow jigdo to display the information from the `[Image]' section while the rest of that file is still being downloaded.

Servers section

[Servers]
LabelA=http://myserver.org/
LabelA=ftp://mirror.myserver.org/
LabelB=LabelC:subdirectory/
LabelC=http://some.where.com/jigdo/

All lines in the section, which provides the mapping from server labels to server locations, have the same format: On the left side of the `=' the label name is given, and on the right the value to expand the label name to.

A label name appearing multiple times in this section indicates alternative download locations for the parts that use the label in the `[Parts]' section. This notation makes it very easy to add mirrors to the jigdo file.

As shown by the example above, the label values may themselves reference other labels. In this case, the entry `LabelB:some/path/part2' in the `[Parts]' section will expand to `http://some.where.com/jigdo/subdirectory/some/path/part2'. Loops in the label definitions result in undefined behaviour and must be avoided.

There may be any number of `[Servers]' sections in the file; they are all considered when looking up labels. Either of `[Parts]' or `[Servers]', but not both, can be omitted from the jigdo file.

Comment section

[Comment]
Any text, except that lines must not begin with `['.

All text following a `[Comment]' or `[comment]' line is ignored, up to the next line with a section label.

Include directive

[Include http://some.url/file.jigdo]

Lines of this form cause the content of the specified jigdo file to be downloaded and parsed just like the main jigdo file. The effect will be the same as copying the included file's contents into the file which contains the include directive. (Exception: Any relative URLs are always resolved using the URL of the `.jigdo' file that contains that relative URL.)

The URL argument can be an absolute or relative URL. Relative URLs are assumed to be relative to the URL of the jigdo file which contains the include directive. Includes can be nested, but it is an error to create a loop of include directives. It is not possible to use URLs of the form `Label:pathname'.

The URL cannot be quoted with "". Any `]' characters in the argument must be escaped as `%5D', and any spaces as `%20'.

Include directives are only supported by jigdo, they are ignored by jigdo-lite.

An include directive terminates any previous section, but it does not start a new one. In other words, a new section must always be started after the include line, jigdo does not allow normal entries to appear below the `[Include]'.

CACHE FILES

Any file specified with the --cache option is used to store information about the FILES presented to jigdo-file. When querying the cache, a file is considered unchanged (and the cached data is used) only if filename, file size and last modification time (mtime) match exactly. For the filename match, not the entire file name is used, but only the part following any `//', so that any changes to the part before the `//' will not invalidate the cache.

Old cache entries are removed from the cache if they have not been read from or written to for the amount of time specified with --cache-expiry. Entries are not immediately removed from the cache if the file they refer to no longer exists - this makes it possible to cache information about files on removable media.

Cache expiry only takes place after jigdo-file has done its main work - if any old entries are accessed before expiry takes place, they will be kept. For example, if the program is run using the default expiry time of 30 days, but accesses a cache file with entries generated 2 months ago, then entries in that cache will be considered, and only those cache entries that were not needed during the program run will be expired.

Due to a peculiarity of the underlying database library (libdb3), cache files never shrink, they only grow. If a large number of entries was expired from your cache file and you want it to shrink, you can either just delete it (of course then everything will have to be regenerated) or use the utilities accompanying libdb3 to dump and restore the database, with a command like `db3_dump old-cache.db | db3_load new-cache.db'. For Debian, these programs are supplied in the package `libdb3-util'.

If a different --md5-block-size is specified, the entire file needs to be re-read to update its cache entry. If a different --min-length is specified, only the first `md5-block-size' bytes of the file need to be re-read.

EXAMPLES

Preparing your CD image for distribution

You have created a CD image `image.iso' from some of the files stored in the directory `/home/ftp' on your harddisc, which is also available online as `ftp://mysite.org'. As you don't want to waste space by effectively hosting the same data twice (once as files on the FTP server, once inside the image), and you are fed up with users' downloads aborting after 200MB and their restarting the download dozens of times, you decide to use jigdo. How do you prepare the image for download?

In fact, only one command is necessary:

jigdo-file make-template --image=image.iso --jigdo=/home/ftp/image.jigdo --template=/home/ftp/image.template /home/ftp// --label Mysite=/home/ftp --uri Mysite=ftp://mysite.org/

People can now point jigdo at `ftp://mysite.org/image.jigdo' to download your image. The template file needs to be accessible as `ftp://mysite.org/image.template'.

Note that nothing prevents you from doing the same for an FTP server that isn't administrated by you - in that case, you only need to host the `.jigdo' and `.template' files on your own server/homepage.

Preparing an arbitrary large file for distribution

We assume that you have a large file that is not a filesystem, e.g. `movie.mpeg'. Because of space problems, you want to distribute the data on two servers.

In this case, the parts of the image need to be generated artificially with the split command. For example, to create chunks of 4MB each, use `split -b 4m movie.mpeg part'. Copy the resulting files `partXX' into two directories `1' and `2' that you create, according to how you want the files distributed between the servers. Next, create the jigdo and template files with `jigdo-file make-template --image=movie.mpeg 1// 2//'. You will need to edit the `.jigdo' file and provide the right URIs for the two servers that you are going to upload the `partXX' files to.

Customized versions of images

Because it is possible to assign a different URI for each part of an image if necessary, jigdo is very flexible. Only one example is the possibility of customized versions of images: Suppose that someone is distributing a CD image, and that you want to make a few small changes to it and redistribute your own version. You download the `official.iso' CD image with jigdo (passing it the URL of `official.jigdo'), write it to CD-R, make your changes (say, adding files from the `myfiles' directory on your harddisc) and produce your own version, `myversion.iso'. Next, you instruct jigdo-file to create the jigdo and template files for your modified image, using the command

jigdo-file make-template --image=myversion.iso /mnt/cdrom/ myfiles// --label My=myfiles/ --uri My=http://my.homepage.net/ --merge=official.jigdo

while `official.iso' is mounted under `/mnt/cdrom'. By using --merge, you have told jigdo-file to take the contents of `official.jigdo', add to it a new `[Image]' section for `myversion.iso' and write the resulting jigdo file to `myversion.jigdo' - so now `myversion.jigdo' offers two images for download, the original version and your modified version. (If you do not want it to offer the official version, edit it and remove the `[Image]' section that lists `official.iso'.)

Now you can upload the `.jigdo' file, the `.template' file and also the files in `myfiles' to `http://my.homepage.net/'. Thus, for people to download your modified image, you do not need to upload the complete image contents to your web space, but only the changes you made!

(In case you only made very few changes, you could also omit the `myfiles' parameter in the command above, then all your changes end up in the new template file.)

Combining many jigdo-managed images into one

It is also no problem to combine data from several sources that use jigdo. For example, if of five different and unrelated servers each one distributes a different CD image via jigdo, you can create a customized DVD image that contains the data from all these CDs. When people use jigdo to download your image, the individual files on the DVD are fetched from the same sources as the original CDs.

Consequently, even though you will be distributing a 3.2GB file via your web space, the actual amount of data that is stored on your server will only be in the order of several MBs.

BUGS

For certain contents of one of the input files, most notably a sequence of zero bytes longer than --min-length at the start of the file and an area of zeros preceding the file data in the image, jigdo-file make-template may fail to find the file in the image. Unfortunately, this restriction cannot be avoided because the program could become very slow otherwise. If you use the --debug option, all instances of jigdo-file discarding possible matches are indicated by lines containing the word `DROPPED'.

In fact, not only all-zeroes files trigger this behaviour, but also files which contain at their start a long sequence of short identical strings. For example, both a file containing only `a' characters and one containing `abcabcabcabc...' are problematic.

SEE ALSO

jigdo(1) (NOT YET IMPLEMENTED), jigdo-lite(1), jigdo-mirror(1), split(1) (or `info split'), find(1) (or `info find'), mkisofs(1), md5sum(1)

AUTHOR

Jigsaw Download was written by Richard Atterer <jigdo atterer.net>, to make downloading of CD ROM images for the Debian Linux distribution more convenient.

jigdo-0.7.3/doc/jigdo-file.sgml0000644000175000017500000021253510262752636016112 0ustar richardrichard
jigdo atterer.net
RichardAtterer 2001-2005Richard Atterer Jul 2, 2005
jigdo-file 1 jigdo-file Prepare files for Jigsaw Download (distribution of huge files, e.g. CD images). jigdo-file COMMAND MORE OPTIONS FILES Common COMMANDs: make-template, make-image, verify DESCRIPTION Jigsaw Download, or short jigdo, is a scheme developed primarily to make it easy to distribute huge filesystem images (e.g. CD (ISO9660) or DVD (UDF) images) over the internet, but it could also be used for other data which is awkward to handle due to its size, like audio/video files or large software packages. jigdo tries to ensure that the large file (always called image from now on) is downloaded in small parts which can be stored on different servers. People who want to download the image do so by telling the jigdo1 (NOT IMPLEMENTED YET) download tool to process one `.jigdo' file; using it, jigdo downloads the parts and reassembles the image. jigdo-file is used to prepare the files for download. What makes jigdo special is that the parts that are used to reconstruct the image can have any size and content - they only need to be contained in a contiguous region anywhere in the image. For example, if you wish to distribute an ISO9660 image which contains a snapshot of an FTP server, you can instruct jigdo-file to prepare the download data in such a way that when people use jigdo to download the image, jigdo actually fetches the individual files from the FTP server and assembles them into an exact copy of your image - during the download! (If the image is not a filesystem dump, you can use split1 to create the small parts that the image will be reassembled from.) You are completely free to choose where the individual parts of the image are stored: They may be in entirely different directories on different servers (e.g. because of storage/bandwidth constraints), but this is invisible to the people downloading your image. The information about available servers only needs to be added to the `.jigdo' file by you before distributing it. The `DETAILS' section below contains technical details on how jigdo works. The `EXAMPLES' section lists a number of common scenarios and may help you to get an idea of what jigdo is useful for. OPTIONS Many options are specific to a particular COMMAND; the ones below are general or used by several commands. Further options are listed below with the individual commands. All options are silently ignored if they are not applicable to the current command. For any BYTES parameters to options, you can append one of the letters `k', `M' or `G' to the amount you specify, to indicate kilobytes, megabytes or gigabytes. Output short summary of commands and options. Output complete summary of commands and options. Output program version. Specify location of the file containing the image. The image is the large file that you want to distribute. Specify location of the Jigsaw Download description file. The jigdo file is a human-readable file generated by jigdo-file, to which you add information about all the servers you are going to upload the files to. jigdo will download this file as the first step of retrieving the image. Specify location of the image `template' file. The template file is a binary file generated by jigdo-file, it contains information on how to reassemble the image and also (in compressed form) all the data from the image which was not found in any of the parts. Depending on the command, each of these three files is used sometimes for input, sometimes for output. If the file is to be used for output for a particular command and the output file already exists, jigdo-file exits with an error, unless is present. In most cases, you will only need to specify one out of , because any missing filenames will be deduced from the one you specify. This is done by first stripping any extension from the supplied name and then appending nothing (if deducing ), `.jigdo' or `.template'. Control how verbose the program is, and what format the output has: is the same as except that no `x% done' progress messages are printed. restricts the output to what is absolutely necessary, mostly error messages. is only different from for the make-template command: It enables output in a simple `<offset> <file>' format which is useful when searching for binary files in other binary files. Overwrite existent output files without complaining. This is the default. Refuse to overwrite existent output files. jigdo-file usually needs to read the entire contents of all the FILES you specify. If you use it repeatedly (e.g. because you make a new CD image available daily), caching the file information will increase the program's speed significantly. The cache file is automatically created if it is not yet present. Data is usually both read from and written to it. This is the default. Do not use a cache. Set maximum age of cache entries. Any entries older than this will be removed from the cache. The default is 30 days. You can append one of the letters `h', `d', `w', `m', `y' to denote hours, days, weeks, months or years, respectively. A value of `0' or `off' disables expiry, so that all entries will stay in the cache forever. See the section `CACHE FILES' below for more information. Set size of internal buffers. The default is 128k - if you have a fast disc, increasing this value may make jigdo-file faster, but in general, changing it is not necessary. Uninteresting internal parameter. Set size of blocks into which files are subdivided. The default is 128k. If you change it, any cache file will have to be regenerated. Internally, jigdo-file may choose to use a slightly larger or smaller value. Read file and directory names from the specified file. If file is `-', read names from standard input. Each line in the file is taken as a name, so the names may contain spaces, but not newline characters. An empty line causes jigdo-file to stop reading from the file. find1 is a powerful tool for generating file lists, but make sure to use `find -type f' if possible - otherwise, if you instruct find to output both a filename and a symlink to that filename, jigdo-file will read the file contents twice. Output checksums in hexadecimal instead of Base64-like format. This should not be used with the make-template command, because the resulting `.jigdo' file violates the `.jigdo' file format. Its intended use is to make jigdo-file more interoperable with other Unix shell utilities like md5sum1 . This is the default. Use jigdo's own Base64-like encoding of checksums. =help|=all|=UNIT,~UNIT... Switch on or off debugging output. Just `--debug' is equivalent to `--debug=all'. The argument is a comma-separated list of unit names for which debugging output is to be enabled, or disabled if the name is preceded by `~'. The special name `all' means all units. By default, debugging output is switched off except for the units `assert' and `general'. The exact list of available units for which debugging can be switched on depends on whether jigdo was compiled with debugging support - the list can be printed with `--debug=help'. FILES Names of files or directories to use as input. These are the parts that are contained in the image. In case one of the names is a directory, the program recursively scans the directory and adds all files contained in it. While doing this, it follows symbolic links, but avoids symlink loops. If one of the filenames starts with the character `-', you must precede the list of files with `--'. A value of `-' has no special meaning in this list, it stands for a file whose name is a single hyphen. COMMANDS The command name is the first non-option argument passed to jigdo-file. Most commands have short abbreviations as well as long names. The short command names should not be used in scripts - there may be incompatible changes to them in the future! <command>make-template</command>, <command>mt</command> Reads image and FILES, creates `.jigdo' and `.template'. This is the main functionality of jigdo-file. It is possible to specify both and . In this case, first the list of files is read from standard input until an empty line is encountered. Everything following it is assumed to be the image data. This can be useful if you use mkisofs1 or similar programs that can output the complete image on their standard output, because there is no need to store the image on disc temporarily. If a FILES argument contains the characters `//' (Unix) or `\.\' (Windows), this has special meaning. In the final jigdo file that users will download, each of the parts is referenced in the `[Parts]' section with a URI of the form `Label:some/filename'. (See `FORMAT OF .JIGDO FILES' below for a detailed description.) The `[Servers]' section gives a mapping of labels to servers on the internet, with lines like `Label=http://myserver.org/jigdofiles/'. Using this information, jigdo will create the final download URI for the part, `http://myserver.org/jigdofiles/some/filename'. Specifying `//' (or `\.\') in a file or directory name serves to `cut off' the names at the right directory level. For example, if the Unix path of one of your FILES is `/path/some/filename', you can tell jigdo-file to cut off after the `/path' by passing it the argument `/path//some/filename', or `/path//' if you want the whole directory scanned. The path names need not be absolute; `somedirectory//' is also possible. Specify a name to use as the label name for a path on disc. (Influences the output jigdo file.) If you used `//' in the FILES arguments as described above, jigdo-file will by default pick label names automatically (`A', `B' etc.). With this option, you can give labels more meaningful names. Note that the label name will only be used if one or more FILES begin with `/path//'. Try to use label names that start with uppercase characters, to disambiguate them clearly from protocol names like `http', `ftp'. By default, using as described above will cause lines of the form `Label=file:/path/' to be written to the `[Servers]' section of the output jigdo file. If you want to override the `file:' URI so that the line reads `Label=http://some.server.org/', you can do so by specifying along with . Giving just without the corresponding has no effect, and even if you specify both, an entry is only added to the `[Servers]' section if the label is referenced by at least one `[Parts]' entry. The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\ or '\\' to get a single backslash in the URI.) The mapping specified with an option is ignored if it is already present in the output jigdo file. Users of the Windows version may notice that the `\' directory separators are converted into `/' in the `file:' URIs that are generated by default. This is done to increase cross-platform compatibility of `file:' - the print-missing command of the Windows version will automatically re-convert the characters when it prints the URIs. In case you supply your own `file:' URIs under Windows using , you must also exchange `/' and `\'. to Set amount of compression in the output template file, from (no compression) to (maximum compression). The default is , which can make the template generation quite slow. By default, the compression algorithm used is the same as for gzip1 . and Choose between the gzip and bzip2 compression algorithms. The default is gzip. Bzip2 usually gives a better compression ratio, but compression is significantly slower than with gzip. Set minimum length of a part for jigdo-file to look for it in the image. The default is 1k. Parts smaller than this will never be found in the image, so their data will be included in the template file. The search algorithm used requires such a minimum length, otherwise template generation could become extremely slow. If you know for sure that all your FILES are larger than a certain amount, you can increase jigdo-file's speed slightly by specifying the amount with this option. There is a hard-wired absolute minimum of 256 bytes - anything lower will silently be set to 256. Include the contents of FILE in the output `.jigdo' file. The file can contain data which you want added to the output (for example, a `[Servers]' section with a list of your servers as entries), or it can be the jigdo file output by an earlier run of jigdo-file. It is possible to specify the same file for input with and for output with . However, you will also need to use to make the program overwrite the old version of the jigdo file with the new one. FILE can be `-' for standard input. When adding new information to the supplied file, jigdo-file will not insert new lines into the `[Parts]' section if an entry for the same MD5 checksum (but not necessarily with the same URI!) already exists, and it will not insert new lines into the `[Servers]' section if a completely identical entry already exists. When reading in the existing FILE, the behaviour is slightly different: The program preserves entries in the `[Parts]' section with identical checksum, but different URIs. For completely identical entries (same checksum and URI), only one entry is preserved and the duplicates are removed. The `[Servers]' section is left untouched. This is the default. Causes jigdo-file to add an `[Image]' section to the `.jigdo' file. As an exception, a new `[Image]' section is not added if you use and the file to merge contains an `[Image]' section with a line which reads `Template-MD5Sum=' (end of line after the `='). In this case, the generated template data's MD5 checksum value is just added after the `=' of the first line of this form in the file - no whole new `[Image]' section is appended. This behaviour is useful because it allows you to pass via an `[Image]' section with arbitrary content and then have the MD5 checksum automatically added by jigdo-file. The section `FORMAT OF .JIGDO FILES' below explains the `[Image]' section contents in greater detail. Do not include an `[Image]' section in the `.jigdo' file. You need to add one yourself if you use this option. However, doing that is not easy (you also need to add a `Template-MD5Sum' line with the correct checksum, or jigdo will complain), so use of this option is discouraged. This is the default. Causes jigdo-file to add a `[Servers]' section to the `.jigdo' file. This default section uses `file:' URIs, which allows for immediate reassembly of the image from the local filesystem, and is also useful if you want to edit the file manually and replace the `file:' URIs with other URIs. Do not add a `[Servers]' section at the end of the `.jigdo' file. Useful e.g. if you are going to append the section with a script. Whenever a file is found in the image, execute the supplied command string by passing it to a shell. jigdo-file sets up a number of environment variables with information about the file match. For example, if the file `/path//a/b/file' was found in the image and `Label:a/b/file' is going to be written to the `.jigdo' file: LABEL="Label" - Name of the label for the file. The example assumes that ` Label=/path' was specified by you. In the absence of such an option, LABEL will be set but empty. LABELPATH="/path/" - The path corresponding to the label, or in other words, the prefix of the matched file's path that will not appear in the output `.jigdo' file. Is set even without any `' option present. Ends with a slash. MATCHPATH="a/b/" - The rest of the path, without the leafname of the matched file. Is either empty or ends with a slash. LEAF="file" - The leafname of the matched file. MD5SUM="lNVdUSqbo2yqm33webrhnw" - The md5sum of the matched file, in Base64-like format. FILE="/path//a/b/file" - For convenience, the complete path of the file. The variable is always set to $LABELPATH$MATCHPATH$LEAF. Please be careful to correctly quote the string passed to this option, otherwise your supplied command will not work with filenames that contain spaces. As an example, to create a backup of hard links to the matched files, use the following option: --match-exec='mkdir -p "${LABEL:-.}/$MATCHPATH" && ln -f "$FILE" "${LABEL:-.}/$MATCHPATH$LEAF"' By default, no command is executed. Use --match-exec="" to remove a command string which was set with an earlier use of this option. This is the default. Imagine that your image contains a .tar file which in turn contains another file x, and that you provide both the .tar and the files inside it on the command line. When jigdo-file scans the image, it encounters the beginning of the .tar file, and then the file x. At this point, a decision must be made: Should the smaller file x be recorded as matched, or should it be ignored in favour of the larger (and thus better) match of the .tar file? Unfortunately, at this point it is not clear whether there will actually be a full match of the .tar, so by default, the program prefers the small match. In the case where a large partial match is present and a shorter match has been confirmed, ignore the small match. (See the option above.) <command>make-image</command>, <command>mi</command> Reads `.template' and FILES, creates image (or `imagename.tmp'). Provides a rudimentary way of reassembling images - jigdo is usually better suited for this task. However, in contrast to jigdo, no `.jigdo' file is required. If the image is to be written to a file (and not to standard output), it is possible to create the image in several steps, with several invocations of `jigdo-file make-image', as follows: You first invoke jigdo-file, specifying as many files as are available at this time. The program scans the files, and those that are contained in the image are copied to a temporary file, whose name is formed by appending `.tmp' to the image filename. For all further files which could be parts of the image, you repeat this process. As soon as all parts are present, the temporary file will be truncated slightly (to delete some administrative data that jigdo-file appends at the end) and renamed to the final image name. The possibility of reassembling the image in several steps is especially useful for gathering files from removable media, e.g. several older CDs. Scripts using make-image can detect whether image creation is complete by checking the exit status: 0 signals successful creation, whereas 1 means that more files need to be supplied. Other errors result in an exit status of 2 (`recoverable', e.g. file not found) or 3 (non-recoverable, e.g. write error). This is the default. Whenever any part is copied to the image, re-check its checksum against the checksum stored in the template. It is recommended that you leave this switched on, even if it slows down image creation a bit. Do not check files' checksums when copying them to the image. This can be safely used when no cache file is used (which means that files will be written to the image immediately after being scanned) or the whole image is checked later with the verify command. <command>print-missing</command>, <command>pm</command> Reads `.jigdo', `.template' and (if present) `imagename.tmp', outputs a list of URIs still needed to completely reassemble the image. Together with the make-image command, this provides most of the functionality of jigdo on the command line. For each part that is not yet present in the temporary image file, the file checksum is looked up in the `[Parts]' section of the jigdo file. Any label in the corresponding entry is then expanded according to the label definitions in the `[Servers]' section and printed on standard output. jigdo allows you to specify several alternative locations for each label in this section, but print-missing will only output the first one for each missing part. If the checksum cannot be found in the `[Parts]' section (this Should Not Happen unless you deleted that section), a lookup is instead made for `MD5Sum:<checksum>', just like with jigdo. (Thus, if you want to get rid of the `[Parts]' section, you can do so if you rename each part to its own checksum.) Override the entries in the `.jigdo' file for any label with a URI of your choice. With the example above, a `[Parts]' entry of `Label:some/filename' will cause the line `http://some.server.org/some/filename' to be printed. The supplied value is not quoted by the program; if it contains characters such as space or any of the characters #"'\ then you must quote it. (Under Unix, you may need to quote the value twice to also protect it from the shell, e.g. \\\\ or '\\' to get a single backslash in the URI.) <command>print-missing-all</command>, <command>pma</command> Just like print-missing, this command outputs a list of URIs still needed to completely reassemble the image. However, all alternative download locations are printed instead of just one. In the output, the URIs for a file are separated from other files' URIs with blank lines. The option has the same effect as for print-missing. <command>verify</command>, <command>ver</command> Reads image (presumably generated with make-image) and `.template', checks for correct checksum of image. The template data does not only contain checksums of the individual parts, but also of the image as a whole. make-image already performs a number of internal checks, but if you like, you can additionally check the image with this command. <command>scan</command>, <command>sc</command> Reads all the FILES and enters them into the cache, unless they are already cached. The option must be present for this command. This is the default. This only causes the first bytes of each file to be read. If the cache is used later by jigdo-file make-image, the rest of the file will be read once these first bytes are recognized in the input image. Immediately read the entire file contents and store them in the cache. <command>md5sum</command>, <command>md5</command> Reads all the FILES and prints out MD5 checksums of their contents. This command is quite similar to md5sum1 , except that the checksum is output in the Base64-like encoding which is also used elsewhere by jigdo-file. The FILES arguments are processed in the same way as with the other commands, which means that recursion automatically takes place for any arguments that are directories, and that symbolic links are not listed except when the file(s) they point to are not reachable directly. In the checksum list printed on standard output, only the part of the filename following any `//' (or `\.\' on Windows) is printed. Any will be used for querying files' MD5 checksums and/or writing the checksums of scanned files. <command>list-template</command>, <command>ls</command> Reads a `.template' file and outputs low-level information about the image and all parts contained in it, including offset, length and checksum. You can also use this command with temporary image files (by specifying something like ) - in that case, the output also distinguishes between parts that have been written to the image and parts that haven't. The exact output format may change incompatibly between different jigdo releases. The following different types of lines can be output. `have-file' only occurs for `.tmp' files, indicating a file that has already been successfully written to the temporary file: in-template offset-in-image length need-file offset-in-image length file-md5sum filestart-rsyncsum have-file offset-in-image length file-md5sum filestart-rsyncsum image-info image-length image-md5sum rsyncsum-size DETAILS Jigsaw Download was created with the format of ISO9660 CD images in mind - however, the following also applies to many other filesystem formats, as well as to `tar' archives and uncompressed `zip' archives. A CD image contains both information for organizing the filesystem (header with disc name etc., ISO9660 directory data, data of extensions such as Joliet or RockRidge, zero padding) and the files contained on the CD. An important property that jigdo relies on is that each file is stored in one contiguous section of the image; it is not split into two or more parts. When jigdo-file is given a number of files that might be contained in an image, it detects whether any of the files are present using a `rolling checksum' inspired by the one used by rsync1. The resulting data is written to the `.template' file: If a section of the image could not be matched (e.g. it was directory information), the data is compressed and written directly to the template. However, if a matching file was found, its data is omitted from the template. Instead, only a reference (an MD5 checksum of the file) is inserted in the template. Note that the template data only contains binary data, it does not contain any filenames or URIs, since it cannot be easily edited in case any of these values need to be changed. All that information is stored in the `.jigdo' file, a text file to which you can add URLs for your server(s). The jigdo file provides a mapping for each MD5 checksum to one or more alternative download locations for the corresponding part. Apart from the mapping of MD5 sums to URIs, the jigdo file also contains an URI pointing to a download location for the template file. This way, the jigdo download tool only needs to be given one URI (that of the `.jigdo' file) to be able to download and reassemble the complete image. FORMAT OF .JIGDO FILES The overall format of `.jigdo' files follows that of `.ini' files, as also used by the Gnome and KDE projects for some data. The file is organized into sections, each of which is preceded by a line reading `[Sectionname]'. Within each section, lines have the form `Label=Value'. Such lines are also called `entries' below. All `.jigdo' files use UTF-8 as their character encoding. Comments are introduced with the `#' character and extend to the end of the line. Whitespace is ignored at line start and end as well as to the left and right of section names and the `=' in entries. Furthermore, the jigdo utilities split up the text of the entry value (i.e. the part after the `=') into whitespace-separated words, much like the Unix shell. Single '' and double "" quotes can be used to prevent that e.g. URIs containing whitespace are split apart. Similarly, characters with special meaning (the characters '"#\ and space/tab) must be quoted with \ to appear in the value. As with the shell, there is a difference between ' ' and " ": Within ' ', the characters "#\ and whitespace lose their special meaning and become ordinary characters, whereas within " ", only the characters '# and whitespace lose their special meaning - in other words, backslash escapes still work inside " ", but not ' '. `.jigdo' files can optionally be compressed with gzip1 . jigdo-file always outputs uncompressed files, which you can compress yourself. jigdo-lite supports single uncompressed and compressed files. (Behaviour which may change in the future and which should not be relied upon: jigdo additionally supports any number of concatenated plaintext and gzipped parts in the files - for example, you can compress a `.jigdo' file and then add a couple of lines of uncompressed data to the end.) In all cases, the `.gz' extension should be removed from the filename - the tools will determine automatically from the file contents whether a file is compressed or not. Below is a description of the individual section names used by jigdo. Jigdo section [Jigdo] Version=1.1 Generator=jigdo-file/1.0.0 Information about the version of the jigdo file format used, and the program that generated it. There should be one such section per `.jigdo' file. Image section [Image] Filename="filename for saving on user's disc" Template="URI where to fetch template file" Template-MD5Sum=OQ8riqT1BuyzsrT9964A7g ShortInfo=single-line description of the image (200 characters max.) Info=long description (5000 characters max.) The value for the `Template' entry can be either an URL (absolute or relative to the URL of the jigdo file) or a string of the form `Label:pathname' (UNIMPLEMENTED), as described below. The `Template-MD5Sum' entry is added by jigdo-file and specifies the MD5 checksum of the generated `.template' file. It is used by jigdo to detect cases where the downloaded template data is corrupted or belongs to a different image. Unlike other entry values, the values of the `ShortInfo' and `Info' entries are not split up into words, instead all quoting is preserved. The value of the `Info' entry is special in that jigdo1 can optionally parse XML markup it contains. If the markup has errors such as unbalanced/unsupported tags, the string is displayed literally, without XML parsing. Supported tags are <b></b> (bold), <i></i> (italic), <tt></tt> (typewriter font), <u></u> (underline), <big></big> (larger font), <small></small> (smaller font) and <br/> (linebreak). Supported entities include &lt; (`<'), &gt; (`>') and &amp; (`&'). Note that the whole `Info' entry must be on one line in the jigdo file. This section may occur multiple times, but all except the first one will be ignored. This is useful e.g. when creating a `.jigdo' file for a DVD image when you already have `.jigdo' files for CDs with the same content: You can simply `[Include]' (see below) the CDs' jigdo files at the end of the DVD jigdo file, after its `[Image]' section. Parts section [Parts] xJNkjrq8NYMraeGavUpllw=LabelA:part0 GoTResP2EC6Lb_2wTsqOoQ=LabelA:part1 kyfebwu6clbYqqWUdFIyaw=LabelB:some/path/part2 -J9UAimo0Bqg9c0oOXI1mQ=http://some.where.com/part3 All lines in the section, which provides the mapping from MD5 checksums to URIs, have the same format: On the left side of the `=' the checksum (encoded with a Base64-like encoding) is given, and on the right a string corresponding to the part with this checksum; either a complete URI or a string of the form `Label:pathname', which is expanded into one or more URIs by looking up the definition(s) for the Label in the `[Servers]' section. In case a particular MD5 checksum cannot be found in any `[Parts]' section by jigdo, the program will perform a lookup for `MD5Sum:<checksum>', e.g. for `MD5Sum:xJNkjrq8NYMraeGavUpllw' if you deleted the line for `part0' above. A checksum appearing multiple times in this section indicates alternative download locations for the part. There may be any number of `[Parts]' sections in the file; they are all considered when looking up MD5 checksums. jigdo-file always puts the `[Parts]' section at the end of the file, and it even rearranges any file specified with to have only one such section at the end. This is done to allow jigdo to display the information from the `[Image]' section while the rest of that file is still being downloaded. Servers section [Servers] LabelA=http://myserver.org/ LabelA=ftp://mirror.myserver.org/ LabelB=LabelC:subdirectory/ LabelC=http://some.where.com/jigdo/ All lines in the section, which provides the mapping from server labels to server locations, have the same format: On the left side of the `=' the label name is given, and on the right the value to expand the label name to. A label name appearing multiple times in this section indicates alternative download locations for the parts that use the label in the `[Parts]' section. This notation makes it very easy to add mirrors to the jigdo file. As shown by the example above, the label values may themselves reference other labels. In this case, the entry `LabelB:some/path/part2' in the `[Parts]' section will expand to `http://some.where.com/jigdo/subdirectory/some/path/part2'. Loops in the label definitions result in undefined behaviour and must be avoided. There may be any number of `[Servers]' sections in the file; they are all considered when looking up labels. Either of `[Parts]' or `[Servers]', but not both, can be omitted from the jigdo file. Comment section [Comment] Any text, except that lines must not begin with `['. All text following a `[Comment]' or `[comment]' line is ignored, up to the next line with a section label. Include directive [Include http://some.url/file.jigdo] Lines of this form cause the content of the specified jigdo file to be downloaded and parsed just like the main jigdo file. The effect will be the same as copying the included file's contents into the file which contains the include directive. (Exception: Any relative URLs are always resolved using the URL of the `.jigdo' file that contains that relative URL.) The URL argument can be an absolute or relative URL. Relative URLs are assumed to be relative to the URL of the jigdo file which contains the include directive. Includes can be nested, but it is an error to create a loop of include directives. It is not possible to use URLs of the form `Label:pathname'. The URL cannot be quoted with "". Any `]' characters in the argument must be escaped as `%5D', and any spaces as `%20'. Include directives are only supported by jigdo, they are ignored by jigdo-lite. An include directive terminates any previous section, but it does not start a new one. In other words, a new section must always be started after the include line, jigdo does not allow normal entries to appear below the `[Include]'. CACHE FILES Any file specified with the option is used to store information about the FILES presented to jigdo-file. When querying the cache, a file is considered unchanged (and the cached data is used) only if filename, file size and last modification time (mtime) match exactly. For the filename match, not the entire file name is used, but only the part following any `//', so that any changes to the part before the `//' will not invalidate the cache. Old cache entries are removed from the cache if they have not been read from or written to for the amount of time specified with . Entries are not immediately removed from the cache if the file they refer to no longer exists - this makes it possible to cache information about files on removable media. Cache expiry only takes place after jigdo-file has done its main work - if any old entries are accessed before expiry takes place, they will be kept. For example, if the program is run using the default expiry time of 30 days, but accesses a cache file with entries generated 2 months ago, then entries in that cache will be considered, and only those cache entries that were not needed during the program run will be expired. Due to a peculiarity of the underlying database library (libdb3), cache files never shrink, they only grow. If a large number of entries was expired from your cache file and you want it to shrink, you can either just delete it (of course then everything will have to be regenerated) or use the utilities accompanying libdb3 to dump and restore the database, with a command like `db3_dump old-cache.db | db3_load new-cache.db'. For Debian, these programs are supplied in the package `libdb3-util'. If a different is specified, the entire file needs to be re-read to update its cache entry. If a different is specified, only the first `md5-block-size' bytes of the file need to be re-read. EXAMPLES Preparing your CD image for distribution You have created a CD image `image.iso' from some of the files stored in the directory `/home/ftp' on your harddisc, which is also available online as `ftp://mysite.org'. As you don't want to waste space by effectively hosting the same data twice (once as files on the FTP server, once inside the image), and you are fed up with users' downloads aborting after 200MB and their restarting the download dozens of times, you decide to use jigdo. How do you prepare the image for download? In fact, only one command is necessary:
jigdo-file make-template --image=image.iso --jigdo=/home/ftp/image.jigdo --template=/home/ftp/image.template /home/ftp// --label Mysite=/home/ftp --uri Mysite=ftp://mysite.org/
People can now point jigdo at `ftp://mysite.org/image.jigdo' to download your image. The template file needs to be accessible as `ftp://mysite.org/image.template'. Note that nothing prevents you from doing the same for an FTP server that isn't administrated by you - in that case, you only need to host the `.jigdo' and `.template' files on your own server/homepage.
Preparing an arbitrary large file for distribution We assume that you have a large file that is not a filesystem, e.g. `movie.mpeg'. Because of space problems, you want to distribute the data on two servers. In this case, the parts of the image need to be generated artificially with the split command. For example, to create chunks of 4MB each, use `split -b 4m movie.mpeg part'. Copy the resulting files `partXX' into two directories `1' and `2' that you create, according to how you want the files distributed between the servers. Next, create the jigdo and template files with `jigdo-file make-template --image=movie.mpeg 1// 2//'. You will need to edit the `.jigdo' file and provide the right URIs for the two servers that you are going to upload the `partXX' files to. Customized versions of images Because it is possible to assign a different URI for each part of an image if necessary, jigdo is very flexible. Only one example is the possibility of customized versions of images: Suppose that someone is distributing a CD image, and that you want to make a few small changes to it and redistribute your own version. You download the `official.iso' CD image with jigdo (passing it the URL of `official.jigdo'), write it to CD-R, make your changes (say, adding files from the `myfiles' directory on your harddisc) and produce your own version, `myversion.iso'. Next, you instruct jigdo-file to create the jigdo and template files for your modified image, using the command
jigdo-file make-template --image=myversion.iso /mnt/cdrom/ myfiles// --label My=myfiles/ --uri My=http://my.homepage.net/ --merge=official.jigdo
while `official.iso' is mounted under `/mnt/cdrom'. By using , you have told jigdo-file to take the contents of `official.jigdo', add to it a new `[Image]' section for `myversion.iso' and write the resulting jigdo file to `myversion.jigdo' - so now `myversion.jigdo' offers two images for download, the original version and your modified version. (If you do not want it to offer the official version, edit it and remove the `[Image]' section that lists `official.iso'.)
Now you can upload the `.jigdo' file, the `.template' file and also the files in `myfiles' to `http://my.homepage.net/'. Thus, for people to download your modified image, you do not need to upload the complete image contents to your web space, but only the changes you made! (In case you only made very few changes, you could also omit the `myfiles' parameter in the command above, then all your changes end up in the new template file.)
Combining many jigdo-managed images into one It is also no problem to combine data from several sources that use jigdo. For example, if of five different and unrelated servers each one distributes a different CD image via jigdo, you can create a customized DVD image that contains the data from all these CDs. When people use jigdo to download your image, the individual files on the DVD are fetched from the same sources as the original CDs. Consequently, even though you will be distributing a 3.2GB file via your web space, the actual amount of data that is stored on your server will only be in the order of several MBs.
BUGS For certain contents of one of the input files, most notably a sequence of zero bytes longer than at the start of the file and an area of zeros preceding the file data in the image, jigdo-file make-template may fail to find the file in the image. Unfortunately, this restriction cannot be avoided because the program could become very slow otherwise. If you use the option, all instances of jigdo-file discarding possible matches are indicated by lines containing the word `DROPPED'. In fact, not only all-zeroes files trigger this behaviour, but also files which contain at their start a long sequence of short identical strings. For example, both a file containing only `a' characters and one containing `abcabcabcabc...' are problematic. SEE ALSO jigdo1 (NOT YET IMPLEMENTED), jigdo-lite1 , jigdo-mirror1 , split1 (or `info split'), find1 (or `info find'), mkisofs1 , md5sum1 AUTHOR Jigsaw Download was written by Richard Atterer jigdo atterer.net, to make downloading of CD ROM images for the Debian Linux distribution more convenient.
jigdo-0.7.3/doc/jigdo-lite.10000644000175000017500000000420610433432656015315 0ustar richardrichard.\" This manpage has been automatically generated by docbook2man .\" from a DocBook document. This tool can be found at: .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . .TH "JIGDO-LITE" "1" "19 May 2006" "" "" .SH NAME jigdo-lite \- Download jigdo files using wget .SH SYNOPSIS \fBjigdo-lite\fR [ \fBURL\fR ] .SH "DESCRIPTION" .PP See \fBjigdo-file\fR(1) for an introduction to Jigsaw Download. .PP Given the URL of a `\fI\&.jigdo\fR\&' file, \fBjigdo-lite\fR downloads the large file (e.g. a CD image) that has been made available through that URL. \fBwget\fR(1) is used to download the necessary pieces of administrative data (contained in the `\fI\&.jigdo\fR\&' file and a corresponding `\fI\&.template\fR\&' file) as well as the many pieces that the large file is made from. The \fBjigdo-file\fR(1) utility is used to reconstruct the large file from the pieces. .PP `\fI\&.jigdo\fR\&' files that contain references to Debian mirrors are treated specially: When such a file is recognized, you are asked to select one mirror out of a list of all Debian mirrors. .PP If \fBURL\fR is not given on the command line, the script prompts for a location to download the `\fI\&.jigdo\fR\&' file from. The following command line options are recognized: .TP \fB-h --help\fR Output short summary of command syntax. .TP \fB-v --version\fR Output version number. .TP \fB--scan \fIFILES\fB\fR Do not ask for "Files to scan", use this path. .TP \fB--noask\fR Do not ask any questions, instead behave as if the user had pressed Return at all prompts. This can be useful when running \fBjigdo-lite\fR from cron jobs or in other non-interactive environments. .SH "SEE ALSO" .PP \fBjigdo-file\fR(1), \fBjigdo-mirror\fR(1), \fBwget\fR(1) (or `\fBinfo wget\fR\&') .PP CD images for Debian Linux can be downloaded with jigdo \&. .SH "AUTHOR" .PP Jigsaw Download was written by Richard Atterer , to make downloading of CD ROM images for the Debian Linux distribution more convenient. jigdo-0.7.3/doc/jigdo-lite.html0000644000175000017500000000603410433432661016116 0ustar richardrichard jigdo-lite

jigdo-lite

Name

jigdo-lite -- Download jigdo files using wget

Synopsis

jigdo-lite [URL]

DESCRIPTION

See jigdo-file(1) for an introduction to Jigsaw Download.

Given the URL of a `.jigdo' file, jigdo-lite downloads the large file (e.g. a CD image) that has been made available through that URL. wget(1) is used to download the necessary pieces of administrative data (contained in the `.jigdo' file and a corresponding `.template' file) as well as the many pieces that the large file is made from. The jigdo-file(1) utility is used to reconstruct the large file from the pieces.

`.jigdo' files that contain references to Debian mirrors are treated specially: When such a file is recognized, you are asked to select one mirror out of a list of all Debian mirrors.

If URL is not given on the command line, the script prompts for a location to download the `.jigdo' file from. The following command line options are recognized:

-h --help

Output short summary of command syntax.

-v --version

Output version number.

--scan FILES

Do not ask for "Files to scan", use this path.

--noask

Do not ask any questions, instead behave as if the user had pressed Return at all prompts. This can be useful when running jigdo-lite from cron jobs or in other non-interactive environments.

SEE ALSO

jigdo-file(1), jigdo-mirror(1), wget(1) (or `info wget')

CD images for Debian Linux can be downloaded with jigdo.

AUTHOR

Jigsaw Download was written by Richard Atterer <jigdo atterer.net>, to make downloading of CD ROM images for the Debian Linux distribution more convenient.

jigdo-0.7.3/doc/jigdo-lite.sgml0000644000175000017500000001117510067032076016115 0ustar richardrichard
jigdo atterer.net
RichardAtterer 2001-2002Richard Atterer May 04, 2002
jigdo-lite 1 jigdo-lite Download jigdo files using wget jigdo-lite URL DESCRIPTION See jigdo-file1 for an introduction to Jigsaw Download. Given the URL of a `.jigdo' file, jigdo-lite downloads the large file (e.g. a CD image) that has been made available through that URL. wget1 is used to download the necessary pieces of administrative data (contained in the `.jigdo' file and a corresponding `.template' file) as well as the many pieces that the large file is made from. The jigdo-file1 utility is used to reconstruct the large file from the pieces. `.jigdo' files that contain references to Debian mirrors are treated specially: When such a file is recognized, you are asked to select one mirror out of a list of all Debian mirrors. If is not given on the command line, the script prompts for a location to download the `.jigdo' file from. The following command line options are recognized: Output short summary of command syntax. Output version number. FILES Do not ask for "Files to scan", use this path. Do not ask any questions, instead behave as if the user had pressed Return at all prompts. This can be useful when running jigdo-lite from cron jobs or in other non-interactive environments. SEE ALSO jigdo-file1 , jigdo-mirror1 , wget1 (or `info wget') CD images for Debian Linux can be downloaded with jigdo. AUTHOR Jigsaw Download was written by Richard Atterer jigdo atterer.net, to make downloading of CD ROM images for the Debian Linux distribution more convenient.
jigdo-0.7.3/doc/jigdo-mirror.10000644000175000017500000000443510433432662015673 0ustar richardrichard.\" This manpage has been automatically generated by docbook2man .\" from a DocBook document. This tool can be found at: .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . .TH "JIGDO-MIRROR" "1" "19 May 2006" "" "" .SH NAME jigdo-mirror \- Maintain a mirror of images offered with jigdo .SH SYNOPSIS \fBjigdo-mirror\fR [ \fBconfig-file\fR ] .SH "DESCRIPTION" .PP See \fBjigdo-file\fR(1) for an introduction to Jigsaw Download. .PP \fBjigdo-mirror\fR is a script intended for people who want to offer direct HTTP or FTP downloads of files for which only the jigdo and template files are available. .PP As the first step of using \fBjigdo-mirror\fR, you need to set up normal HTTP/FTP/rsync mirroring both of the `\fI\&.jigdo\fR\&'/`\fI\&.template\fR\&' files and of the parts that are needed for the reconstruction of the images. For example, in the case that you want to mirror Debian CD images with \fBjigdo-mirror\fR, you need a mirror of the `\fI\&.jigdo\fR\&'/`\fI\&.template\fR\&' files and a Debian mirror on the local machine. .PP At regular intervals (preferably immediately after the normal mirroring has finished), schedule a run of \fBjigdo-mirror\fR\&. It will search through a given directory for any `\fI\&.jigdo\fR\&' files and attempt to create all images offered by each file. .PP The script requires you to set a number of variables - please refer to the start of \fI/usr/bin/jigdo-mirror\fR for a description of each of them. Since \fI/usr/bin/jigdo-mirror\fR will be overwritten whenever you upgrade \fBjigdo-mirror\fR, it is highly recommended \fBnot\fR to change the settings directly in this script. Instead, personal settings should be saved in a file called \fI\&.jigdo-mirror\fR in your home directory, or in a different file whose name is then passed to the script as its first (and only) command line argument. .SH "SEE ALSO" .PP \fBjigdo-file\fR(1), \fBjigdo-lite\fR(1) .PP CD images for Debian Linux can be downloaded with jigdo \&. .SH "AUTHOR" .PP Jigsaw Download was written by Richard Atterer , to make downloading of CD ROM images for the Debian Linux distribution more convenient. jigdo-0.7.3/doc/jigdo-mirror.html0000644000175000017500000000562510433432665016504 0ustar richardrichard jigdo-mirror

jigdo-mirror

Name

jigdo-mirror -- Maintain a mirror of images offered with jigdo

Synopsis

jigdo-mirror [config-file]

DESCRIPTION

See jigdo-file(1) for an introduction to Jigsaw Download.

jigdo-mirror is a script intended for people who want to offer direct HTTP or FTP downloads of files for which only the jigdo and template files are available.

As the first step of using jigdo-mirror, you need to set up normal HTTP/FTP/rsync mirroring both of the `.jigdo'/`.template' files and of the parts that are needed for the reconstruction of the images. For example, in the case that you want to mirror Debian CD images with jigdo-mirror, you need a mirror of the `.jigdo'/`.template' files and a Debian mirror on the local machine.

At regular intervals (preferably immediately after the normal mirroring has finished), schedule a run of jigdo-mirror. It will search through a given directory for any `.jigdo' files and attempt to create all images offered by each file.

The script requires you to set a number of variables - please refer to the start of /usr/bin/jigdo-mirror for a description of each of them. Since /usr/bin/jigdo-mirror will be overwritten whenever you upgrade jigdo-mirror, it is highly recommended not to change the settings directly in this script. Instead, personal settings should be saved in a file called .jigdo-mirror in your home directory, or in a different file whose name is then passed to the script as its first (and only) command line argument.

SEE ALSO

jigdo-file(1), jigdo-lite(1)

CD images for Debian Linux can be downloaded with jigdo.

AUTHOR

Jigsaw Download was written by Richard Atterer <jigdo atterer.net>, to make downloading of CD ROM images for the Debian Linux distribution more convenient.

jigdo-0.7.3/doc/jigdo-mirror.sgml0000644000175000017500000000777007701377543016514 0ustar richardrichard
jigdo atterer.net
RichardAtterer 2001-2002Richard Atterer May 04, 2002
jigdo-mirror 1 jigdo-mirror Maintain a mirror of images offered with jigdo jigdo-mirror config-file DESCRIPTION See jigdo-file1 for an introduction to Jigsaw Download. jigdo-mirror is a script intended for people who want to offer direct HTTP or FTP downloads of files for which only the jigdo and template files are available. As the first step of using jigdo-mirror, you need to set up normal HTTP/FTP/rsync mirroring both of the `.jigdo'/`.template' files and of the parts that are needed for the reconstruction of the images. For example, in the case that you want to mirror Debian CD images with jigdo-mirror, you need a mirror of the `.jigdo'/`.template' files and a Debian mirror on the local machine. At regular intervals (preferably immediately after the normal mirroring has finished), schedule a run of jigdo-mirror. It will search through a given directory for any `.jigdo' files and attempt to create all images offered by each file. The script requires you to set a number of variables - please refer to the start of /usr/bin/jigdo-mirror for a description of each of them. Since /usr/bin/jigdo-mirror will be overwritten whenever you upgrade jigdo-mirror, it is highly recommended not to change the settings directly in this script. Instead, personal settings should be saved in a file called .jigdo-mirror in your home directory, or in a different file whose name is then passed to the script as its first (and only) command line argument. SEE ALSO jigdo-file1 , jigdo-lite1 CD images for Debian Linux can be downloaded with jigdo. AUTHOR Jigsaw Download was written by Richard Atterer jigdo atterer.net, to make downloading of CD ROM images for the Debian Linux distribution more convenient.
jigdo-0.7.3/doc/jigdo.10000644000175000017500000000207110433432666014361 0ustar richardrichard.\" This manpage has been automatically generated by docbook2man .\" from a DocBook document. This tool can be found at: .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . .TH "JIGDO" "1" "19 May 2006" "" "" .SH NAME jigdo \- GTK+ download manager. .SH SYNOPSIS \fBjigdo\fR [ \fBOPTIONS\fR ] [ \fBURLS\fR ] .SH "DESCRIPTION" .PP BETA version of the jigdo download manager. .PP This version is not yet capable of processing `\fI\&.jigdo\fR\&' files - use \fBjigdo-lite\fR(1) for that. .PP \fBjigdo\fR is a GTK+ download manager with FTP and HTTP 1.1 pipelining support, pausing, continuing and resuming of downloads, and automatic guessing of your proxy configuration. .SH "SEE ALSO" .PP \fBjigdo-lite\fR(1), \fBjigdo-file\fR(1), \fBjigdo-mirror\fR(1) .SH "AUTHOR" .PP Jigsaw Download was written by Richard Atterer , to make downloading of CD ROM images for the Debian Linux distribution more convenient. jigdo-0.7.3/doc/jigdo.html0000644000175000017500000000306110433432671015161 0ustar richardrichard jigdo

jigdo

Name

jigdo -- GTK+ download manager.

Synopsis

jigdo [OPTIONS] [URLS]

DESCRIPTION

BETA version of the jigdo download manager.

This version is not yet capable of processing `.jigdo' files - use jigdo-lite(1) for that.

jigdo is a GTK+ download manager with FTP and HTTP 1.1 pipelining support, pausing, continuing and resuming of downloads, and automatic guessing of your proxy configuration.

SEE ALSO

jigdo-lite(1), jigdo-file(1), jigdo-mirror(1)

AUTHOR

Jigsaw Download was written by Richard Atterer <jigdo atterer.net>, to make downloading of CD ROM images for the Debian Linux distribution more convenient.

jigdo-0.7.3/doc/jigdo.sgml0000644000175000017500000000513207701377553015173 0ustar richardrichard
jigdo atterer.net
RichardAtterer 2001-2003Richard Atterer May 05, 2003
jigdo 1 jigdo GTK+ download manager. jigdo OPTIONS URLS DESCRIPTION BETA version of the jigdo download manager. This version is not yet capable of processing `.jigdo' files - use jigdo-lite1 for that. jigdo is a GTK+ download manager with FTP and HTTP 1.1 pipelining support, pausing, continuing and resuming of downloads, and automatic guessing of your proxy configuration. SEE ALSO jigdo-lite1 , jigdo-file1 , jigdo-mirror1 AUTHOR Jigsaw Download was written by Richard Atterer jigdo atterer.net, to make downloading of CD ROM images for the Debian Linux distribution more convenient.
jigdo-0.7.3/gfx/.cvsignore0000644000175000017500000000000407701377654015225 0ustar richardrichardcvs jigdo-0.7.3/gfx/close.png0000644000175000017500000000470707701377637015057 0ustar richardrichard‰PNG  IHDR szzô ŽIDATxÚ•Wmh\U~Þsî±i¾¦IÚD0©`¨bu,æ‡h´~d­‰·tUð‡.(,6+î*”þTPâŸ5ER¨ö`¥hlK°¤-2…ãGÉWg’3Ù™$óuï¹÷žýáÜ»“êþØ ‡{æÎ9çyÞ÷¼ïûœC£££‰D`šæ¦f Ãçœs0Æ@D "h­¡µ†R RJH)!„€¾ïojžç…ï?jFdÆï˜¦¹‰c Á£”ÚD <µ$•Rá:ׯgj‰Ô’©õÂõ¨µþÿúÆõjÁGGG)•JQLDèîîÖÔõBˆßY~ìØ1J¥RPJ¡½½]ïÛ·O×’`ŒÁØ\ßFGGéÛo¿åW®\ñ µµµÞ4Mµ¼¼,8çrhhH®€•Røä“O(‘Hðùùyšššê9ç*“É¥”ìëëÓA<…jÝ€Ÿ={ÖH&“î|Î9£D"Áçææ¼7Þx½½½ˆD"èêêBKK ¢Ñ(¢Ñ(Nœ8çŸ7ÜpCW4-Äb±ŠÖKKK•wÞy}}}p]®ë"—Ë!NÃó„ÈçóH§ÓayžššÂÇ "ú€ed‰(gš¦³uëVÑÚÚªùàà 1ÆÇQ(t6›Õ¾ïK¥”ÔZk";þü³]]]èèè€R …B!¬åøÊÊJè‰Ë—/ãÈ‘#xžˆ2ÒŒ±Œiš…††÷Ö[o•÷Þ{¯æO=õE(£T*él6«|ßµ$&''ŸíììD{{{H‚ˆP.—‘N§7ŒŒ€ˆþYµ<D"ù†††Êí·ß.z{{µ†b“Ö8p@.\¨¬¯¯<Ï#)%Û¶áû~D R"†ÂT.—ƒ8sädcÙªå•Ý»w‹ûï¿_AËûûû7©Üyç(•J:Nk×uáyÞÜË/¿Œ{î¹'¬pAªÙ¶ ávlß¾---øá‡úcG‰(cF®¾¾Þ‰ÇãâÁÔÁáEö¿N1RJh­Q.—×^zé%ôôô„ÿ‹Ed2™p|6›E±X òŽ;îÀþýû¡”:S›vJ©p«‚Æ÷íÛGµzŸþ9}÷ÝwÆÊÊŠû / §§'ÔuÛ¶±¾¾)å¦S‘mÛ¡…hmmE,Õ+WþFDÿ’RŠ|>¯,ËÒ7ÞxcH–÷õõQ-Ë“'OÒ¥K—Œ_ýÕ}î¹ç°gÏžÈqlll„ìgff°¶¶†ææf!Âí)%ZZZÐÔÔ„ÙÙÙ¿øH! …‚²,KwttÀ÷}ð‡z($pêÔ)º|ù²‘N§Ýýû÷#‡®s¹\.$377‡“'Obff&T<)%Ç I!‹ÅÐØØˆùùùP ‹Å¢²m[oß¾|ïÞ½¤µÆøø8ýüóÏ|eeÅ{úé§±{÷î¬\.oŸŸŸÇW_}":DDãsssÚ¶m7‘ÎÍÍÍhhh@2™|Mký‘RJ8Ž£,ËÒ,¤t:M¥RɘßK¥Ö××Ãßsss óÀ:ýõÌ™3¸zõ*|߇”¹\Žã„Ñ.¥ HÅ„uŽã¹\ŽBÕe†atazz–eacc#\daa§OŸ+€Ñrµýù›o¾A*• Ç 8ŽƒÙÙYLNN†j¨µŽ*õ›³ ¦+¥Â½cŒÝwêÔ)LOO‡ÞXXXÀøøø_«¶e"8þ<Ãy333˜œœ€MjÜ-XÛJ)0ƹUåzâܹsH¥RH&“˜˜˜T- CDiÆØ*ç<Ã9Ï0ÆV‰(MD/^Ä/¿ü‚ååe\ºt ^'" @±úv‰Hi­aT¯GˆÅb:ŸÏ ß÷­uN)0099ù%€@Ï­ª¢¥cÃ0 [¶l©@¹\¾ï‡ŠD"ñeµ?T ÕsîD"QWW§Àú={öh¥”¼zõªë8NÁ÷ý =ŸÐXí«21 #¿uëÖÊ®]»ÌÎÎjÛ¶u ×Ï«‚gc…h4ê¶µµÉ›o¾ù¿8çˆÇãZ)%’ÉdEkBhžÖºPõ‚ED9Ã0 uuu•îînÑÓÓ£µÖð}_,,,üá¼ê–æ8ç…h4ZÙ±c‡Ø¹s§ö}ü¶Ûn£Úû\GG*•ж,K)¥ˆ"*2Æ ¦i–¶lÙâÞrË-âî»ïÖ ¶··ÃqœMó„ó Ã(E£Q72…wwwS¼wìØ×uu¥RQZkŸ1V1 £lšf¥®®Îëêê’wÝu—®Íq!ÚÚÚP.—u¹\çqÎËœóJ4õÚÚÚd-¸ïûø1`8Vþ$oZIEND®B`‚jigdo-0.7.3/gfx/jigdo-gnomish-buttons.xcf.bz20000644000175000017500000006166207701377636020676 0ustar richardrichardBZh91AY&SYÿßP5/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàh_@xfXx^w×ÖôC ¯­Óuï”O9¸ûí­‡Þ»Òû}&í·WwsÖO·Ôûo§Ë¶õ]›A¶·ÝÚ+ouPÝ»îÞ^ºÛvÞËÛ«ÛÛ­½ž·n–åK³vÕ³¶vÞO[[[½ÜÙ˶IÛ¾ûî¥ò÷Þ}v÷7Ù^ù½í´­ïgSÞîì­®îâÉemݶ뭖ÖÛ;´»é×nû{ÌÚÎúööókI®·oc»Þï5Ýžuö÷Ûo“»m¶ÝÝ·Ù÷½}¶÷>¼ž—¶¶ONíNZk×R½»ÙO{:d¦•vtïlSº)K¼\Ý—eöö³Ýö{±ÓÓ‘¶S®ŽJ;c¾ÆÙïºç»¹÷w=Þá÷w>ìrY÷Ü{ï¸éëÚÔóŸO¾nn3ž;Ä÷ï¾á$‘#a0F†0#h4i¦“h˜Ôžšž†¦Ä2diˆ›MTÿMM£Ä™©¦È4Ó*~€ÓLšSSÚšyLÄz4Ð& ’D& M4Âa ‰“CL¦I²i•=‘2›Ô›SÓI4ا“MG¥=&ÂdО„ÚLÐ4Úš2Ñ¡£@ ÓM CCÔhhhB" € Lc#H“L¤ü)¤ôšƒÁ!êzž¦”õ£M©¦†š44‘£@4 hhÐõРz€ "D“M4£ ‰„Ú#"ž’= F“õ2žÐÔh›F‰úSõM=ÅIê6 ôŒ2 ©ˆõ ¤z€=F z¨Œ€  I 4L™Ñ†Lh@LE<ÒSÙTüšzšz©ûU7éM¦§ª¥#z›i2!âd™!æQ‘š§ê†QêzƒM S @z€`2ˆÑꇢD‘4Ò=S&† Ó2dÈÚš=OIOÉO MŠž„ö¨Í1”ÁÕ=&É”¨õ=OPi õz§©µDôÒzž¦Aêyê ž£OÚƒAê=@Í@ɵ9>~zÛèÂŒõ 7'¹š|¶Þá6ž÷Ó&Ä­Öw¼¾ÇG;ÎØú?ÅâòŸ”@s–›N™cÎg9ÍïZ€5Õ.Ÿ—à»ó;OWc²Òÿ‹oz7Áêuÿçøæ~.‡äõ< w¿uòjo½o³æþm^Gìøznã°øw8gKcéö]o¡òðs:é?¨Üõý¶¹Ý$m·$>ÂwªI XŸïìþÏþãf©¿Ok‰üùÿ³‰i§ö!vœLg§{îë=¨÷_ãñl¿ë{ãpy­Õpùÿw¶ãëq>γ[¸Ãß|;Ï×àzÿ—û^{ç/ײ¦ì¿Ÿ£¢ûš¬w/´åòí®âò»šyÐB<ï?“ßð)}žóëæzxª®?NçsW¬ñª*3XxüÛÃÿ?sîÄû»Ž5n‡“ÿî/›çx¯«õ¼NŸ¡¹¿ÝeøÛΗ™Ÿð:^gk±‹{Ç@wðSv¬"ù&@É FEBP‰ ÷XCóúåCÚR€†ªH'´A=ÁñUýÚ  ÕÎ9*Ò¡÷J‡Ï~:î’@€$±U€¼` ¬é‚@"€ÚBaÉ@gCŸBlm¿œÛo/w„‰œ $Uù„*Ô‹>U8§’€‡ 54ä›döf<ÅÆ÷·/ú¡õ>fGÕôj¶žÞöFË5‡Ùg£é¿ùý¼¯ƒµé{]=oíþ/S £ÚÒê¿¶Ó_äo}Ê,{OÁ½óý*3οÿoÇiIíå|ƒ}éú›à»mƒm16›@¡‡D¨„8Ó=(û;ÄK©!$€DËœ6ЫË娗“Ý”¨RD‘@‘Ïöq/6-@ù3ò,®€üÄ¢€:¯ðÚ½|Í*žæ ü‰D¶Yq=–>Æ:•GÇ©„òªÂÔÇ¿b¡H ÿ6¢ÊUȨmQò¤T|Sà?¯½«ÿWÀ‘PÏð~BIü9ÂÞ(Â#×U¸¨øÔÿíöu{§qôéýŒ¿¤:]§ãÙ䣨ÚT|Å;“Qò)ÿ‚_ÙËûL j'”£˜£ôׯ}wUQýO/ëåü™Û—úYE/èeþ_Ú·Éø«G¹§¶_Å—qõÕ¿§÷~Ä¿ÒKü¼GÕÔ}}?äËýÁ$þì‹OÆ´|ú’B[”X§à\ÏÔPñËä([¨fq~úF¾ðrÀ1°Ç/vkÜÓú*õN"_ñ¥ø%ûRÿ{î„‘Ü2' Z­†ÚqvPŠ…šZ; T¯·Éœ›.~îªÑD;ÆÈ¬å’Yµå0A/»H*qG n¯?eÔAP:§fä•&ÉrOzös« ž¥MTzø¤´åsM†§¡_,|ÆB"„®«/Mé*†ºS‘¯Rg¹;x’\Zʨíœú¨!mGdaÍ3 §=ʶv„3ÈEeãâÐØ}?Û Cã ¾c¶ôÅš'½=ZØ®˜€¡…™!A'a¦+Œº„GöØìN ^é­Â§‰ºñÇòq×DsÊ„ˆ™‰•ôGÃYO!#ºyd6tT¶úaLÌ q‘VéõQ[“É·I¥URA¤4ŠÅxhßgQ|&œHÞ--EdGèå¨["mIê@O'ª° $抛N#ÖˆœJmð+lLèÔäJ2 dAz| ñª»‘q§•ãw+öéàÈ#·_äÉ9UÇF8Ð]ëý`P(V¬U„ öŽ%óøÀ°åk<¿ê¸‘%jy>Rn† E§ŠØ?´ˆ˜¸°1Šã‚Ô‚¢’,Œÿð">èÕY{´…f„dQ ôiW‘„\U @tGÖ»Á݃e È,Gn6üD‘ŸÝ ©© .PÊn#hTˆÕ¶_väúZ£2áÏxUÞ¡:[~{wÆ·RKñCyŸ|Õv½Z^nvõ7G†í¢ B!»­)PÖª*Èz"E$SwoÞ÷¾J”0Õ‰r9$¢ˆ(QPlä™ÄJ‹(°iŒ,væ#›û~µD¯khÊhåP®P—û,Ö¦¤Q[±(ŠQF±N[0AÀe›5›×UH&E4F$S´Áꊿ7Ü‚3üÖÂ: ‚ŒÉÜš@i¹Œ&<1àhI±¤dwG+ÂRk#ATQë0ØP¥6Sw,¦£¢1ºc9&$µ'R°¼³41H$‰‚¤³Ù6´à\ —P‡ ĈÔy‚‘÷ÎV0#ýzйim»—,´æIìï_¤òELVx3äÊsNSˆ@ËÍŒ¶Ä,>Â,"ç5%ÇÒ¥iµ×ïä}ŠaV(Ã+›nr*¢3éëȈآM¯Qbªˆj”*ªOy[ob©ÔSQúW›QŸüû'³>¡áYG¿ëFT¬X~G ûôíáI£½,G—|ÂD"sµÅSls¼b¡Âð=|™DÀݺn¨»w.ˆËÖsm>k2 ;娩‰Ç/VœyÃo}PsÿlëÙ|úpš <˜‘ì{ݼëKP×ø¾Côz Y;ïƒÃ_‚·ëýEç£Ðú>úÏ3 Ècº÷ô)öyÌß°2=¿`ý¾qû’:Õú?}ö d¯y+Óü{Ÿø|kì¹Ì^·à.÷£~Vô½¿\ÏIyµ«§Â|_Ëöÿ0ý'Â/kåôÿWýGúþO¼½8¼´7¤±ˆe÷ÏÕê¯ÈoÐ©ç‹Øû 5i<Ó.w¬â!Âvÿ{”%—Ë>áV‚HñÑåPê#ê" ?ãü¤MÀPxÕë9xȯiC\Ez.ž_ƒïëÝŽyh©SïQ ø_ƒ ;5+ÓžçNñB@Aù’’‘ϒº‡ˆ¥^ ‘ ‚VE|ìÄ’44JbS‘B˜ˆ{{êLÁ¨uJ!·°c³ƒ.ƒûS ÷)fZ;zŽ»ˆ‰ªTÝu!S0TËËÆ$[/KÙ+'Ñ FAlOàâ2šIß‚¦€.€—<ÏG;œá˜\Óœ Ÿ¯œäxHðÜœ·'01—z%Ç8éqøçâð»yÝÐ{Î^ΫP`ÿ¤©vÞ—þ¯óþæ™iF²e¨Z–†‹"ÚÖc>iª=T—g©»‡±»xÕ¯VL©$(ýlÉ y%)D67-ÀËûw¯3îy­ú ÙO²ÌÌöþUºKʨŒÚªG­ @#Š|9ˆŠ$¦ $Ø4&‘Ž §Äᮤ¨ ˆZhz®6Lį-Ê× ?±¤Àí“èÁ48@–…:½x/7B¿u–0´gÃæ¯3F®K¤®Ž–™eO,úžWÔ¸Aôó:´jÃQ‹ÕKÍææs‡/;›¿Ëwº¼ï‹ƒ,µšÇju åÒÔ+ýHË:‰Ñ$ˆ… "‘ƒ¨‘¥Pd¨‚(þH>˜$C}&§hÜv¬oWƒË¢šI£ƒI·²RkÀгÃSFxœ0Eõ³ÂÁXÎù Yq]¥ri[,®/‹Æè¯nLœ”dI6B† W<ýˈˆˆ†'T€çþ÷ëS_ƒaÑc"!÷÷V n„A–Áaœš,xÅŽ0Øõzù“&A’N‰,$‡-.¢¸Ünñ|üyXª…NVò¼„ï'›Íæè–_ ÁŽD\µÚÉÄ}F;ýþýv<ÜnõGÕ­®´­­AXQÑ¿²xY¶W6Õ£2á;“3%UdVö‰+ »BN q 3¢Š+‰ÒAdPb-î*M)hÞ • —ah-Á àR†âbdRë6({(‚z?'0ØÂ*1#$>7ó Kœ?¸˜âê„8::sš|"fÒZ€ÍEóo”‚ÌûH÷ m³eˆúa³›sÔŸ 'ë`ëÁ7XªoÓIÛ(~ec;È'ÎáSåùÝ`‘Kç`òæ™1{ÐØbÁŒs†hS@Ž9ÕŒc ³@P `œ.)¤Ò;†ó& Ž"šo/}ê²$+º‘ 8 €ˆBCú!X-'œ^Ùßì6ïÔ«³”=PT…¥UR³SB«*êñˆCêaņC"°‹…oóÏ·@‘X@a™„É—r­$UûÃÆA“jUsÕžPçÚÚ9È™@ºŽÅ R¡™£8s ÑÌ/ɱ$¹„à&"Dãb¨•DQ3‡+èãJ…)Ø<ç¶vhF)™G˜fFЄzèð…3% 2d™ºbb-²5t»t»Ã>ܤÎ}î|w”—gôáuGqÓ|BôS¦˜¯D¦‹¢û‚ÕU0¿ÈII˜Lwûé LزÑ!dþôžS"c ‰âùï˜jø2}Ê»’¡É“;ËŒ^¨¹BRhVŽ`´ä‡ ‚M¤ ‚4 cµtŸkæö©ÏɦEƒ /Ÿ-5÷ê4ÐÕ°ÁÛhk‘¼…!¹àÀˆB5a?§éÜbúˆwYÙ¡šfVéu­MVäL€XIE Ñ) €{#©Ø¬õCÍ˧F˜xg:ƒ¬¢uëY.T’J•$’¥HHši$’I¦’I¦’I&šI$’I¦›|æLÆ2’ è? %­7¬ãê…Å+ÀÒþ=b´Yݲ’’!$d/op‚eĽ% –‚RB±AäV©õ@™¬L5Œô„äØÉ {Ó¥­ žmþP°TV .Š#F¨”Ð ÃÈËÿ¸âÐ )™†Lyt‚ÈZ¡bõB íRLB|Nù+B0G~€;ë{ ¡t`„™ê3Ž#mYÍyyU32 L†dv:ÌßÝé7¡š¤ôhúÿ}@+U?ôàÚm±ž†_¶‚¡l¶Ò´Ã褤ÞP{ÄñüfÌž2DØ+ L˜@Ó›')ôy9~êC³úZu\Oìó”âáìÆg±'ãüª‰:WìJ@Q ˆ?«üu÷˜Òˆ²3 «›æiY˜!ŒûãdÀýö;ºóh^ 1•'2¥¹ùë_-â}kÛU'Þ÷@D¦RÏ“ZÃ33 ¤Æ!" B,’$ ‰ "HŒoÔM\¡™TÓTªç÷ýé ú cØ ÜM©–0“šha¸ÏP4ß+zv8x b›2­Vn÷7”ùwÇÌ1€ÚMÜU.*›ÑñAE9 {HŒŠ›„=|EhUžÎ‘ðõÁ±foÂ8Á)A[Î"F,ž¡(Þ„  0}9æxFHô«Uš)(4+û¬t½~Ëà|Yªr]ÄŽžÓ9a˜3å>àÔê±x°ºéU —Ô@Xå˜dvß³ûµùW¾†ÌŒŸWßžK¥%ò¬b ÛaÞm¼]LìÑÕ½û àÕ’fú¿ÍNg°ðçC¼¿qÝi°uN{UÔïak›ÛØ+›Éš!¨t£ uë×®âÞ—"ŽG_ñ¨G±àì‹5XŠÈhðR¸ÔïÖ¬îî_fœˆÓ§Ÿ ÎT×”áy¤uxçz÷×®çÑ©èhn[ìµâ+ùV"Qô¾ŠыĠ¾|@;¥lÑPb‚Ôp§{•ÎL©,ªÜ+…ûtÓI[ïà,y²Ö*âÿOKY>«ż‹µÃ¥?Ói9S«>M¶2ÆòÞÏvlC„è–˜mÑZcÝàQ­ö›Ÿ.ê •#ë>HOOŒÃ R °/Öù_l`}ƒ~‘bÓÛNñK»À_Õv[ð³öZký’ ’? uÅ1Yí~Vn­lWQ¦4æ46jµ v%°ÊŽã¿7;UiÖd1ýÊ'RqÖÙMªw)Ý>Âè„©ºšaJ˜93™7Uÿ Kª7>¨qÛˆ‡›ÁÛòfÂP§?ÅJœÛ_œˆ"T•„æÀØ¶Ž‡ÛØZ£‰ŠÕ303ffHâ‹ÆOV~ŽÊp{žKÝåϲ–²ášËšÏFͧÁ EŸG¡oõ»€\Ä ¶“lwÆ@Û´ú¯…—Ê-¶ˆ\Ä9R¼]ä'‘>„ŸEbíSÓ&ûp?kËæ?yÝ¹Ž ƒÂN&U.1yw]OÏ/(}ÚÚ!ŒieYnl5€¨åuÞûþ.;ÞÙ[/0©”ÌMozŽà¹ÝCIPMAQ@÷Qfž9Å—Ò)½†øqŸArq6H¤˜¡Æ.8“|GŽ?C_a—·ñõÄî®§€».W:åÍ]uw™ï6yd_eñuC2`f3 ™¸ÂËÂ]zÃAH‘Ðàʶ$ì;k°».Û»mù—’»¼”©+¼l^v×¾Ýkîî ±´ÆÛãc!4›½†˜^o¯\2c„W q†ºë‘ô47–…¼P®r˜rI”7ßÝm¾”moõ*VSv7=‡Câàô»ë7)Z&Ÿn˜áˆ I€¦âÉŒª›+¸›g¡SÚz»ÅÑïÏX¤‘ãÓßÓSÁN™òpüžÂúW¯'&É‚ð^7e‚)EŽŠÏ&G 6¨˜lêésÙÁÁM]fh zZa±Ž¦‚#ɱ<è‚•^%„Ë´rp¨ ôg¥­¾Ùœ†:š“ûÞPÏ9Ýï¼áyØÐzl7;«g·^¦²¾öuËé=^- ²8ŠýdmË"§Üým -òÿâ‰ÑɨE:Ågª»æ5Ãsçñö=IB»’ìø0¯J$Gñ «rb®T Ã6¦‡&Ï=#/"Õ™²,¹\‰Ë™V²²Ûµªè î›wÚŽ€¢nò­¥\Å®Jõ[PY¡´_È¢DäÄÎ ¦`‘¹ŸWÚ'ñÁÎZõZÂU3Ôí»»‘ªF÷¸Aüƒjìt¶Dñl†¾ “5—ÞÃD,öìNñ>z;kl^VØ®òÔRP^€ÕõEµ©SÄèWAë7ÂtUØú]CX¡­RêrpM’’ß!Ö=•VL1n™ ³b[öÝqÃ#k^W£*x²/¸}°v¢C؉¬nD©+M'Ðw|³¾/SkÛ¥!ªé„i© ¢Š¤Ö3O‰µX!=]r£µÒ¤åwX1x´uJYûJ•¨Ö²Õ ·> Îéê>ìD]7;©öÈŸEªwϺϩK /jCxâ<Âám?QÐôÁã–ê$ 9‰XrÿÖqg&€æ2WÑ~$ßðù”‹$º¾w”eìüßi‹­†rÓh/\£O©µð§PÎÛ±$Œ´®‹ö±áø‘z…[gY“Ó èqµ@'ëƒ,S¡|gwCƒFIÉ©RÒfrÉΊºc-…ù´PNo7tƒª«AR`ÄZ׊FhFjƒl Tµg¯kìÎ~øÚã‹/±2EKiµFÂDËußoÞáö ×aÚl%—ª;I–ivVnãuC.ʾï¬JL…Ô—æçÃGŽtC²ü-SÝò¬+…¯Kß ˜.[X<„_X±:àQ4Zù {QñÂä#º2ÃgÈ ÐÂ0Ðñ%D¼;æ{Ú¬ªžì„ÛÆÌÛ¶». ©``rÄx)Aq,÷Ÿž­^ý}w³ˆ ÁSÕG*%åñã‡ÌA]ZXHŠ›žcý,Ÿ¿µÐ“>lD¡ÄsìžL™•{¡Æ!®`ùÛi9}ù²F|³hÈ2ŒnŠ/[ ­‘l &eéû]˜¶E'oKXc1 ^ŒMæÏ§ ¹5@éÇaøó®š_¿2ÈÛÍ@u Vƒü×N9jÃ÷Uñ A˜ ‰¡7”CŒÓ¹gÅýa~Žêú³ûŽ$„{‡õDNÿy6ÈÆw©„ñMËRN¬"CûVZwB’w«ÝH§BíQ™\ä°ýi~þ?gמI¬"í/±æc”€óÀK›Ï™xø.JÅà_©C® ü9¦™¡„ úóP¢È3_ÛuB“Üj™ÈÛ“éMWðþ- >‚«µ][6¨«µÕ[QW{AYçTÁ=XüÓP~ã\i&–&Woöý²+Z½ `½uÙiï;‹”Ûjé"+40qƒ=¶ºÞ¦ ¹¢¸¨É…UO©ñ´x|´î±¨1ú(;fÚ_d_ºs´ËÖ΃5l¹ÜÁXŸ:ˆˆç(…qü–Z„ñ YM¤¦äÎ¥vž¹ÕZPWDæ‹‹ÄÁÃVt®¦¿ð:‘ö™¶rîYæ’G~ß8¿¿_» C§Å¥ —Ûz q&j]¤ •4¡HÓ˜1–Û-U²W9+¯ËÈ©¡$sdGXd7ðö1äÔ»æo9»®bœ¿j€–Nç m§ˆ2y¨˜ÏžUA¶t}å”:dXè§ñLkŠpy[ƒ”ÇÛ1Ø-­(Çév÷å”8xõÂì[œMÿ2žòÀidßø{kYˆQ'^™I”)ÛŽjáeýM|8{ÉÔ]Àd˰çã•bÜÐÁ…/mž(ƒ7X¦a†¥ÏFÆÏ-³ÌÇÃì……½b‹¨f#f#‚>×¢7Ä–±4Á¶¨dɤË–²V•8f´Qb¤jD ÌdŽ€ÍëkZ›ÞEpúÕH,¨[»¶Ê.¡dBº <Ðpªo¾ nèҙÊ$o±¾§mox.ø;­h¶Î´Œ3bÙ-Xë(Å\˜e <²ô•yb”¯àþðYW¶±õä+Ä>~ '"­ƒÚm¥ù¼ÿê-”ü6B9¯6€zº<ÍKâ‘Ìb›!ài;›/P)¢r‹ ±L“& F=ùN˜¹'Àò]”øƒYâb2%ÿ~b4ÌÖ|SóÎmúP¨ú‡¾-ŠCEÑ/hóO(øGLðŠ„}ií¤‹…Ô4{Â3—úì¡y\˜Ìí³@Ú6‹Û(i±;D{Ù§œúsn<¶q^s VMý€ße©[x´{ÏàºÒ¦WhVÈŸÁèª!gŽø>þФøû"™7¼Z ïçQËJØóUÎf62²lÖÞó¨`7ûUCÅ鑨ŸËÄ6IžžÉBgq &8!QHÿi¥DH©H*lWÃæŽCEó]H8î¢ïª-ïG„ƒ0H¶?ÔüåÿÝ„p53p!`¼Iø7ÒMt'!È4\;IÑÒáÅg­›1ly¢¦C‰m40ƒ·¸üWi{矑ó_w®é?@zN´w¹Š lÙ³70:Ø¿ ¯ùf0tK¶Wb^wúé…ˆWîÙ¦ãÿ5ýBÁxë1¨)¼S‹a-¢Ç/jf1eÁ¨Û¼ >† þ¶Úl{ADj‚Ÿ4Nâ‡#Ýn,eQ¶2Ià>è¦K´‰^nÍ •«¦sÓAhUr®MaC÷ˆõC‚ 9£ë6ý þi«ßô¼¡bkÍùHï9ËmŒ¾ÁmÐ[ì…:ìýþA转b„tS;/—Gv÷ë=¶ÆØÅu2`À> ø~,uÕm ¦1¸<èGù\§ò*„@#××ÄÇ|Æ£Hð1¶ù‚øÅÚáé‰O ¡»¹½ˆMÚe4Ô"n”ÄϺÒ˜¶Ø$}»‡p0°Ø>tYNºïôldepî¡ s=Wxõ¤ ã{ÍÕ5úï§u4Zœ® |Ó®lrÅcó‡ÜÐKƒ}q® Mžuð×a}44ØD ¸´”¥ÅD •¦bßùðàÈ 68™‚ßBVü©ÓÍ(uù-Ó†Ø>äg»\í.…¸›¶EÛg¯Sî—Ùe¬7¼[å¥;ßa£9O‡ç}ãè ì]Å/úoR@ŒuÂ1ߤ)‘â}õºD³¯‘&†&YZ&ËI¦_Z ‚ñöKÚûgÕÙž:–o£w°ô~@ ±£íKþQ7eÐÞ3íñ­'ÁžÊîV'§ŒáO m(",(ÀÆ“lÃL gÓ¨fã†ý$(“»N—3i¦6Ðr@¶Âiåx†c±Ïùs¯ ¹´L¸Ë/óÀ#É®…ÉX–¹ž'¹k²7&Š:®3JžûNÞ¦›2aLøPé ú°¡§V¯ªEê HâÕ€cc±®xÄËÌl‘=Œ“5¶_?7<¨Ô¡³9„Žz ÚQ.¼µt扻mçßä‡Îæõý¡ktÛ6ÇèÀCHÉC«¼ÌÝeA³ ÄCL“D9Âàó‚jî´Æ3R‡ pzyn‡ˆÕÔJFŒ„†c8̉eF^¼¸{¬Ä¾–CP0¼0a‡ÿG‰IMRõë7ÿÅãøÂï‹„¶zdPù€0¸2w\Ú]v_hÛ § †ci¦† bI!»„kÝ7o= !#ÍU „!$Œº¨…ÎÝKËG©2{™ÇZ‰å0úäæ³ðS2–˜8ªSÊcƒp¡)‡õ›Z þj˜U"ñèê—± ~Í&8™ ½ÆhvÑÂì[Èa2QRd3Ú¨iÎÓNªA@\],)H5#bÏK ‘ªn²LlÔfGÖFÉ€×ËÿÍŒÐ=úŸ<ƒ"Ld /cÄÊÞÖÔ<ÓµÇÇñw~QÁ^6H[Bö÷1vež#OGŸžlª„Ntª¨Íês¶¸yÚrOrCÉŒÃ,&Æc 7Ðú4u¡Ð{ 5ö-Ô<¯$‘ö7Ž(Ö׿î8Ë£ò¯€Ž©Xª¿^º­h%´(+Šš#'™šØ™¢ÑN=GO à޼ŠóçÏ“°ËomÊË]«)çðzkþ=t(õ¨CƒQ›ÏŸT‘ŒB1óÜîk œŸ¯böŽÛ^¾ÚuX”a!A H@Ï”@h&mš€”@I4ºnjkŸÜS¯¸%Ä Ôª®ÏRÕ]aÛëmõ“«²"L·<ðó—Ý‚iQT@P“&ç™Íä<ÔwC¯`T¾e)”äxÜFœØiž}Óc kù\ë•(Ç|¦àÓ ÷lJíÇÙÃ#õú,3‡K;P=OøvŸZÐÐGÆ2ãO<ˆëZóóÅí¤ëi« ¸Ë*ã²7ü#']“Ï ×652a;Žžç\gs $¾HÉ"f6ÐP[0)¦Œíáþ4C¤æ ÜH‘U(x¦(JƒGã„ÇÞ¤°dhr‰žÅr*‘º™¿µ3¤é}ÀÒ¨{ìÓGÓ¥‘¯\hh]„«ñpú¢XNÕ’ºÞA!£Q&Œx6ai¸*üè“öÒ§ÚO>â`oà]×ĉ7Q‚gàfàÊâ'2|Š$¶ô³ò3µ‡– o\8׌ò7 ›z¶SRšƒ’G|9pá¸7M´yÄ2¼»!Š!m%H˜¾E4ÆþeÎ •™‰üóIi)˜i&¡D-·Mà}m\œžiùH·¦X ¡³ákõïq²ƒNƒ[òv©¤cêûúÌaïøŠmï“ù^á¯Ü´êòEô¬Úe1ö‚–­µŠÍ8³r ü¡…7 Ò€ M50áÚaKC$Òœ¢ÅþP,ß%JÞ²{Å:BAz yÊ(²:öì{»ë;škÜþß~©/Á2džäx¸¢N^WŸ±à¡¶Æ1Œh(û\S¨ðÐíÔÏa†è¦1{F»/4ð´ñPÙ‚’²2gÉ*)-bÒÙvú¿—`õyàqëCè_ýTù?¥éû ‡—zõÑî£3¤ð„8L]† ŒbÈÑd–ãÃïr^H(ÑAŽ…(hc×6û6(šîIbÄ"ûk±ž„}pÍm–LóÍ•6ÌÝ0žÝAÛšßÐâ//͵_'“„-¢ÄÄHbi kCX‚dBÛ;L {hq‘M»ôA·´Sϼ…¶|­O«‹œ#¾ëDØ`lF5¸ø)í2! é%í¨Q¨ær,xž4g«stÚ7YjZ3æ·T÷×6 µ¼-î&ç… _ã w‹)/’qäéï*zU ºO {=KmÍÔÌáãc‡05Ö>Ãjΰé2mŒ#ˆÄæ#7+íþØ6}Mÿd c}$C§OœðH¿NN‹ K”úÒÝÌT‹L ðKËë\ŸªçL}*© ùqæå6øZÚhtøt¾ÙöÖ@b8å^X4 2,ì È6¾ø2Ã8x/š‡ ‹Xè¹@á0$É…ä.­fY³Mꃰñ³ÚQfÌM½r¹G{bÄÜ7²NˆµÁ5,¤PÌ€…Dø.´”Ž«T1âa‹h#8 g¶{Œ8xÚ`öËl:,Ò‚+çukœ^Äv¢Ñ@ÕÙƒ´²€Üµ˜/ß–öþ”îµ79h®*+ÍÙdðxAó½èŽ“BßÑÞ¹2zšþT·ÉÎᦨŒßc¤§¥Ç»¨‡ fÛþ”éµÙ€GÚžj6êÐrÕòlÈ¥ÓÇžœ>{†Ø³ ½BEOóü !tÚ¦S¹Çpïw:Ç×èüÏl]ÑJû[BÚ\þÃ{–nAäyœvs4 0hii%ñ„0PËøa!ÌLtS9 œ4«íª­È½¬4BM1£T×D ñH‚]ó´Bëß~%Þ¦OW48æhô¼dF5­§1¹´xÛ)øšè칂2å±}»à¸|ÓÙó2;ÒŠA\&J2¡šùªÂ}È@‰Ú5(ÅšÍ4Ÿ@×V'¬ø5æè£0ÝJ[³'Í7Z&¢áœ¹gwÎÊ6É–âÉܲ{Ìß«øþwƒ|¦Ë•ÔCF]àŸ¦ï~¿}ßoz+Žš›r`çOu÷îúä¼a^dC$F i9x‡˜Á9²§›“,F¤{$T>ïfòTN”i5 x.ož\›o4ɧ£|§²Å<¡²ÄNé…ž óêùg²Ñw‘Ýòð¨©w!«O»ÂÜB;tqð\±œPá ±¦ °Î5—]õäTÕîMŽ|älŽ‘ Þ^eGÑCøöÕØ(z9±Tk§iî’> ,ú`(]OIU?9D¼s½0A÷«Uwz+ èP?PëFE”hrM#-¨§C.ô DB€"àÔ–4žA~öh ›€¢ŒÍQ”¦8•Ý'‹fØ¡¦Æ9FŽÄi¨ÇxÊC?W¦Ö²õIVVe§Û2²_¦Þ ÅìchÃtì.™åô5ÆÓo& o‹/ŸiTf“L¡mJÄ‚ã´ÔÓÓÁ1˜×Üç‘„ŠÔÇu5c!‰IYI¬ÅáeÏ{z…R6ƒÓ2Ó‚a• å3\Œ™9)|ä¹?08¦Ô0‚Ä©#(»1lÿáü»ªL¬e[/y«…ƒ¦èÏâÏn U T ši º…,ä!ÌJ lUÎôÍÒÑÆì1‡ U zLUé—ÍN©ž8Q`ƒî"+Ÿ [ŒîÍÑlâ;ª¼bÈ3´Tбß䑎8ªÁ\Œ  DBL+ˆÁL˜ t8NŽ0¤9¥æcózWL†j*ÄŸÆ÷¬ÃKaÌ0¢pA„h¾VH¢z†?¿î,¾JñtoìvEÛv|éiéCåýE‹Ü"õîÚ!˜.é;ÒÇ·0¥„Ä“„¡š¹I#Ì÷´ Aƒ¡Žw¶#²‡¶ãJ%5A’(ÕÜÓ †TÑñ†aÅõgZ¼M](+Ö‰c-°3:$;$Õqruë/X2–2ê†VGzÓ 2‰qíaß2ËŸdM³ÿ–XÈs§K`o¤Îœ3u`ô·šƒ¼yóÛ„ÁËÀ¤3÷™šËª@’ÁáÝfÇõÐrhAðA¨XË-®®Õ¡vï“¡]'R¥/ì¯Ìê|Ä«ØÄÞ.So€4ÄÛ—G…“ðÆraŒpXt°¶†åu"Á£”ÜÈ@iÒCEˆ© &[Ám—IòÒèp^gÂ3íLÙ(k£‰N‡¢zå(‡˜Ceo)¼ ª(XÕ‡›†=™¬PŸ_°ÓÛ$n59ј¯QrV 6*K‹V3e­vñ=!Ù1RV )  rë3 ÆiލBÌêžkѳ§LcÔ³[®L®yŒL‘ûãÛ&M &ZhÙ©H´yʼo“Ú÷çwèú݇áMÖ>÷YÄfýC¿&^’zÍÀ¯U•¥£µ– b²€ú³OF˜ùªg”AŒƒÞO#F.ÐŒ|Æfœ™å1^w{…¹mYwM7šŸ*ÑMG’ª¹ˆÛœ|ˆu¬\ Àš}Kד&ŨVÅvEi@8pżbvçpžŸþç–i$évÃÝ<½“kìÉW…&‘á­—€Æ»5¬&…¸·ÅT+u ËÐòâÚj8kš}­PÕ-Ô¿@¦n"|ò§±Ž ­¨&Å”¾ÑÚøum£ª^§BY–Råã5• b©£1‹"K:#ÀúcWÿ¡†HÄj«€®SÁxfö4étð«Zc9š!-Õîá ª Oì¥@M,^“..W¥+)œÁO¼dEÖŒANºUìŠSÎzÒÃV*VQf9#‘:¾…žù"*àÒ§‘à#¨òAœÁ¶c£ˆÀ¬{ˆéìê”4¶‚=•2&ÚÁœÞWÓôèÑ^ü•@Ž`¨¢¨"5ç·¡„¿?Ó`Ƈ͹õuè(u£^E¯Ç·cÚòSÐ1‡83;nV½YÀ²VPWW«Tj’V³³ÍH¬ëQf §˳ñ}¼¹/Q>ò¥Wø£8äò¾>‡£,PöØfaFvU’“œ7Ó:s—Q­lsõó™S½!ÝÒ:Ò˜&L«GL(’ ö;âÓH‘CC¾²Ó’dªŽÒ7ÎÆÆûï»Y ¿³_nxyp¬Y"fzd)Ë#\t•b°['biN²ªË¸¦§œYˆi1YéÜa& ,H[2¼+2ñ­™…ØJC6ϬUn%Å7N»SÞãÔ<²›s(A¤zZKyØ;ß½íö»çŸ{º:?6õ%…ÊU.‘…2˜Ml.Xm”ã¥YéÎt©ÐS¤ûûÀË “;×ZhÂË$†•͈ƒ g ‰flçÒD1»ÐѬõµûeÖÐ4ú‹ßå'õ<˜XÈ0Ò07“9„ß<’¸$àHus›ÄWßyFdÁâæyv7 !Ó8¥kC;¶ºpØÓ¦ŒÈ ú%ø82$&«¦¶JJ5H |Ì”´VÕnƶxm*ËÒqèûc‡æZ¯—•›¤›»±u£3:Hpçƒ&iËÏcÉKÈò O†€Îâ4„Ùp]¶næã‰yÄKË´©ÍF!¦+˜ƒïºÌ™’ÑC°O<Ç‚¢«xê,îT|Sjåa]*•N³ùµÃ—8FS"Œ{"˜3ØçÊPl‡ö C R¶•vá"ÍKL“îŒ×ï›'Íyóß±Wp<á)òÑÎ߆£t…ëP×j4â®àhÚ¢9µ=?[A¶±×”ß—Î-Ôƒë6±BLôLè&æ–þfù¾3mpÚ¡ºÉž^•XÆ%üac"å3}1ì숉¼~*[`Á<ôWço…£›¬LxééíS«²nHp&{˜ÞOpk}mê§+{òmÐC_1©Yí¨=Ñ®‚VÁŠCªÉ!5SAFP¬¶£µ£å¢Î–ÈÎ+ÏN«Zý^ê°6<ÃÍ¡ùå á.Z¯iî•lCêz¿“îò}®¹Æ~#&W@ ¡Ê]2Î{…““”0VbÎy0 dgUõ­ÏŽ>ë“Ô“êFï “ XS(½Šz{a˜ÊeË›¬ ªõ•ý]YžÎ(«õ)}ñÖ†ø´4§ÏØš˜™ÄèèÊ,ŸŽ:º;ñÅhÌÂŽòd³¼tIÕæ¨Ñæd*@BÀlò¨ÁÅâRœf1ÛŽðóÌÒ>,ˆqh¡åbÑ||)9dÖ©5'$˜º\½Eu‡Ü/]Œ…P.âĉÈrßIzJy<±qž3‡ÕŽy`ö|WC@xóµÌûsŸ¡…²©5Á¼õ°¢,ðˆ¥¾g).K~4‚ ŸfyFŒ£c ¨sôœ¦‹±¯qͦòÀøp¸ÐӪߘq e˾Òsí\M±É6™YÙ«D7ÆV»$š04Bç!ˆ™€Ù0:¦a›ñ3¹Q—†š9#yw|¾Wy7æûR3<ܼÍTËxê±.wî™Fòq”öC'¤âçÊ­ù30 ‡`™vŠø˜jÚ únNrÆ ‡ó±§ÓD°‡ #SæH£Y8A3)—©—ƒ÷‡2϶sspÒwkafû‡içÌ™‰Ñ'g7zÒU pC¤ÀEi§.µPOmŒäñ rê<¦Œ„Ñ™(eÑ¡³Ì¸âsú]’c«Ùš¶¥–Š×­ùýèh1®#/Vå™dw0“gøtš˜Ù;’6:Y'óÈÌ”;¹ Êcp%Æ3(ð›$ü¼:g±fy*­Ðï¼yÎ"¡±IÊáÅ©Ëm£~[h…+†Þ^©ëö׆²/g³©¹ºÌ’‹é2ÞyÞ"Ê&3&éŠÍ£³J¥>Ŧ'¡X £í`$Þ{0£%l‘j¨£Ž³–œm:».6%Œ±#Ê3‰“m³FLް/è%¹/îðé&.W×wycÿ^îüîÒÆLf àJO93(ùÅBCÒÜ\saÄL?Œ¢ŽŸ,˜a¼àóŽfAñŽx97Ï·Æ{‹¢óôF(0g­GkzÇt4>¡ÑA.‡•(.ʼ”¡2$¥ ç•‹ö"¾‘)YÊv3q@fEl·û ÃF«8=)Úœªy½=•²R“K˜ðèª\·I±R˜OJiEà 4«á(”¼é€¬¢ `‹ø…вð[°¤|h.x˜ipÈ,= “8ÊÈ„W#.†EZãq]ëIs÷͹h¶ýŸ»1¹¾Ä#†£¦ˆ;œžV.$ªìa‹ŠŸ ø@ ›ŽñƒÈp”€œ6Á’‹[Ö`à1‡ŽÎÍEbÇvÆœ‚¨JÉ\s"ÆdDj²³<÷ÝÒW:Ž7mI 3\€¢Z×fṫ¶’n"¾±Á5s”vL äïÅS«ÂJƒƒªg´±µkW5"dÈì{E # ñŒªCKÅ|‚ D®ŒB¤üLDÑÀ’¹Õ#A2èL«%÷W¼¹Âõ pHáE% lŠÎ(ê*y£EPc>=vî«*<×\¥¼§4 ò! ´<¦»e9† Øàè mN9¾ÍoÁ†PáK ä¤ /ÆƼ‰*˜FÓ:»Sˆê£+úܸt_³Ù—+³ö+ÿÙA¥’ö!èÆ+æûG…Ò78¨*Ó r.5áðŽöµÄ$à(}p5Èb4ø—oòÖ«ß›QZYäãÐÁЍ q÷fœ,Úª•wb³w·9˜§vÇF ñl~®ƒÛÐoés—Y¥»Px¹k™æy¯ôNVßE¿&ì'w‘ñúû¨¼TÚ+1—`Ãn§¿ƒŒMþ#£_V×Òè¯>‚4úRPÖvn¼™I)]·¨ƒj´…+«Œãr0c>_Ûíó=®w8s䟲X±¦aÇZQYÝõ Y^)8³ŒdË2ó]¸ eðßlÑð|w£Ô —ËÙô¥`ŽF.JÍ ]e,˜ãiM®–B¦4gÁ9¤µC“ìëÚD¬Y„ÊÄë^ˆ~¬h»%ÝÔ¤d‚ÇT:JzD¨ ÊAròÁà`ýdIÞPïmäïä:#a›Q©ßÐ.•c±SŒpÛ:ÆþuŠ,…2 í# FŽD8Œq˵~í<)ÚþF¯ÔR&1ç Ï Tç‡/:®¢I }r¤>Ëмô=áAÍD$ë×ß×êÓ %®oñ7WD#t,Œ±Ä±™ƒÑÏÖ%^b *9§¬H(t²š­R¥ Ka.x-a3œàø£/#\d2«Á1ôàìB6 çLåiŽÏ «§øðéÿ÷êÛÜ+Eü¿†{£Ô0’®·ë“E%¡»Pz`ñÆÐ«Zñ,xbTÖ#hòÅ€r£ M¼àÈìMR§¥ :P“*Y)Òåéf”\Ê€÷S¤–ÏitªÄQäI±H=wÙ¨½åíå,Çd‰™àí¤ E*&JÔ=ÃhYËàÀ)ÝDU‚Vvgp>Ñn›y2I´d*BKo9%ÖF§Bßœpï[À—ô¹¨¦„gƒ`¿ä ³¢Ç’HÀN’¦ßRPÁu™xåó³¼> áp}®¦˜~ ·Â#² àŒlYåV8ý4pí‰Pƒ-ÛxQ)m‰ÇÈã)ÈóRó’¯G7kDûœ;8%qAîµÌ,—Æ,tOK ¤Ê‰`Vb È%-&› 0ÓZ¯@úQí&Ž Ï0@°²êzù-uìƒ4&Ò–¸lë%YÖyÌ)îdœð£2Æ•EAueLxÏÔæào eÕÜ\TõÅNv#¤Ï<svà”ëDAÌ®ªÂWRxFçérx¨íÓòœÿuæG”Hë­çz'F‚.¹õ4i„ + "ÉE&+ÕA¢–[Œ±ÓAß¶ ÛdÑêc¿JÓÇÄðɘ±>9brÃÔ”¤e‰Ô–ZJ3 $ÁàÐ{ÍÜ๙ŒÀæ8îü˜:Þ&%›‘1ãÅ ¯+&ε.ð\ÎÚ“à[ëù©,>ªà‘ªöê+Jìp?yðz`¥‰Â4$"+å¥@é&&Ѥ"s0VxD‰®¬†¥#Æ@æ:ḚÁâ(õÖœ¦zS  L8YLŽs—}ð,ŠŽNÇkñMØmúßÏ­Ôðw½8šR££>ÿHðrcŒeVz‰=@gvÂx›.†uÃd^ÂÛkkcœÁ[|y «hɰÀ{@ÚVÔW¬cžn<%fH¦pŒiv-ðè>žæC§ˆKƒºý¥‘dÇŒ½þøF´`æ+IøàìÞY“L³±«í‚¶™ UÂ…V´€W´óSÜÔ­Ý@ÄáSm€ìŰ–Ðo99àÂÒ”S¦Zt0˜Q-„í(¦©ÜÜáÙ+ûó4Ó:Vƒ¤õ½Tƒ ÓS¦`yõJ9ÈZtõÕ ý®|&c:ÖîÝèpÞÁZ±,·nÛ;ÀkyÃ&.Æ´ h3­ l ß "}çQØ h ZK+ÖÉ?NÅñŸ[?so?ö?™ñ’X›XV,_~äõÉ×1ƹ‰•§OÄgbÔWº¸¨ÜVäqXq1ÌÓ–ôí` óëCnN,U“ÁYÑUHžý+'/˜ºc{ÛÎó>Ó¼¾Û.–ÎðÙÁ õðk4¢ã¼l­™Í("øâ©Ñèvg’-£6ŒN7€hX3zc0µ/¨ òzšµ)Ål*½(°`ER‹åN`qÀIp.còx}k‘ß¡äge" %lFŒÎVɇ¬|éçÂP?X0QígÖvߨÌíåÑPžÜµJ ZÒ•\ŸG©‰Í|QµmÏŠxlÒŒ¬*‘šyœfÒ^âÙ‘xGñOÑ=EÆûÊR¼L¨g|^Ä–L°pLmEŒÅIO&šcïAì6†R„$1¿jBg`2”±ã Ó@L]Eo/tZi ›·åxÌÃpVë}içÏÐ÷f© UŒyãô¿ÎË$JV(Šœ]NGZgKHNLæàb<CFÑj7ˆ¸ww3¼ÏCM p[¡ïƒà!Ò<&ÞRãqƒÁt ç€3 ÀÞPf½ Á¥ d!!þnm†vÌþšüekFv¦Ð’d«ë3VW'9èŸ<' ا¯^€5˜½ÃªŽ1—asÁ"JšÞ¨ÅÙ@Kh€Ï°uêØNà¢F5DÚN)èxš(:•ÓLs/Ôì>™# n¿??ÐáOmÓ©¾p3ÖsSdžŽ# ZM¢s îN (A}ç¢Q‡¬ºsÏHaØ3Îhr7olsǬY™ÊQbRàñ˺ï3Y{â³ðÓì¦Æ±ýc‘ÕíaÚ›lÌ,Î!ëÔíBÚALzoZ™:‹üÓd ‰©MµÈ·¥WÕÃ,0)×¢ï"&­Ï"+ìÚFPU3­ŠÌ(TRÓбµ^"ö1wÚgô7û\ô¹ÝRé“•q`®°¬Á…•›8H2Ô]›Išm°V—*u~U ât˜ŒœÚ¼`Î^e X'Õ6/E` VY’( ]g@"4ƒ†áR[P¨oÛÙ©¬TŒ#+ÁE¤ZÚÌEV6’"™±£,F@O›ÖÝHîÜZ ½šÊ“´×¬hÁU¾›‰BîÙ—^XaNàůPœU) ®ù;cTK áw7”â'Gråàe0ʨEÂÓ´áQIÏPŽ»‡\D<ȹh5ѽ·¨ï¹¿éLCo";ÉÈOWw½““¼ï™e°äˬ¸s[CQüܖ‰76Ú O~‚+ßmxŽ7áŸ_·çïîšï÷ôäÝgÞeÕ÷íE³iŽ“í›Šµ£à›€üàâsA/0÷•Qk[<ÙlY‰èˆP6VÎ'œô±Yxg‘G4¬c•æÔjdž EJmÀJ É«à ¶ ʶF>ÌËÃóý ¶Î.mW6~Ð9¤Q6…¥ ùÜ÷$ƒ`=$.¸oB®4GIGÆñªfïÅ&OI`éBö°âË0 ‡€ñ°)4¡QL¥2Þ&ÄœÒÍs3=feÉê/¤È'õ†›Pe±ftYT4¸þHZ;vGʱD7Ý¡á¶GŒè}>²hÕyõA[©ž§¥{·¶p5n =ÝY­À@ í[²K?6¥Q ËÛÜYŠÞw✈©ŒÆ½= 1®°$©$u&ïM‰“(é”r´®´Þ€ü!0–aylÛRfç‹]IpI$‘æ {áÉé útôå™È+·M¥+ âñ1&ŒÚyxM3‡`vÁ@â®\!S±‡ f@ðÉ™'dTFϸϥ†S™@„f¤@Ïy·™$G%txÁBÆ\˜“œâz_†`˜)< -5°šŒH~7×ñE“]|,À} µúØÛ/þh´{«L…§¾-µMhœ8 _)ôöŠŽ q>™’P3±À”¸K=G)ÏŠ"SìQNªpN«¥ƒ‹‘Ó´•OˆˆEžPíqŒ²×4³ntˉψ²îœŽéy–U21ÆK~H«³ÎÈ œGS”°ŸˆÓEŸ1× ©\WéèÙ~]wÀmî]Ü˧¬ÉÎÁæ¦dÅZÑëº{Zˆ›£ÁBTRa±j˜ R;Ú{ÇÇXÎQ’ô#i¹À·&­)éµiÕ?`ЯuŽ¿í,£:ñ98 ©«øôgá(¡Xg×p”¬ûNu—`k²9d~1…«¦d¬QYlN/ÉArG¹(Í aÛ»¥VfG˜Ðíå×YÒ´å‘ã`s-0lã‹-èãÌy¥´2aÚëÊP ~^°¤{4’Û¹jL¹îq(ó.ìuy^ȶ¯g6Ù.Ѿ7vwßð>ïq{Ùz?¾x³zgað „€èb341¤¢3HÓ¿!ùåhãÖwñ#" ؘìç>tÕq¸ýYµÂÌY,W¡äòNÕD\CÈ…$¨°±>Ô\ÞJ>t1ŽäV`ÃŒ²˜ú,‚ysVÇ•ÆÓæÛ<]êíÏH~qÛÇmÅåÆ]Bùk}r»j*¡“9šÔ'škPÅJ(FZ™b’,ÐÒÁ:T›%š-2ºš²ÏY½Ð(¥ˆë2LøD(£ã  L°êDÄHYíRP…ÔQ˜[aƒÖP2‹3ÏÇËW1h,ˆ‰£?É›j¼c@=j`ÈW¤êõ=‚ ÅEÄ‹þKùÿ™˜SË‹BŒ–(v6»IÒR’ܹhN¡S3úŠºçÂix~íhºÌÓ]»DÚ=R€ò<žnw1蜳Æ]æ)_ö•8úùŠ0ÈÒŒ””S¡T¬–ÆìæÄˆôUæœã§rƒ²çÎåá’tmª¨À4²‡ÔT™@yÄZP©-RgˆOsŸ T„avY$#9ÞƒöNŒðéâÕ†Tc$¥IÂÞà{Øê\8÷ã|«.ôì *Ç'-Mæñ¤Ñ¨â}ßlfÚ º¡J¥TTA”äÑ>£l¾W ìKÛÑ÷>AÇÉ –êÑm¥žœG]Ž÷ž¼éÁ›{ØÑÜ7Jí;ÈŒEÕŸ¾fìºsoóò²·V‹ÐVŠƒÐn†G3]_Vªšf¥=±è.~¹¯÷ äݰí`À³l¤ícõ®ZInÈ–ùoB‰bEUÓ³• Š¤à†›”"Û^¤ú“j7¼¤ÑTXÅÄæø“2R£HˆËÆ FÔ+ÔËq9…«wYÚ–€ž¯ø±’ËŸ¥yrðÁ"PõK¿¬—ãsÙ¥ðež}³n;_W¸³\«Vûû'vJìÖ½l6OjãǦÀÓ;!»Ûè®p&–×lñx’ØêÙ÷2Ý–=éUJ7Ó™viîÉð“·H=ÞìÈ•ƒ,)OnYQßöéÏLŠq À‚”e`h3òÒà¤PUŒiÝX3 ·p猤R¦¡Rr´ÚùÕSœ¬Âfg«~!^P]¢¶‰Ï~„zHãt‰K$Ô—h‚ˆHŸÏˆ„á×kÍè©&öòêÏE…{‰è­âh fp,[›9HLà«và†NºU׈i\@ãØÉ½¤·ÈmY;—™›AN¾î¹V $ÝÌð¨2ŠD0 ÑVÕTIrw nÂ!IM+_¤ Ñ-¯BSùñÅÉ|UåHC°<¶ºÇñi~˜e7b»±³µ@¸gÒ’€¡ãÆ'ŒLÎyžÜ;g[Ùj~3$Óëcš]ö³yF Žà¦a˜`ÈLýXò‹°p*–Áœ€;æ –28x eŸv(¡‚#€OAz8Â%*œK‡¬Ç› ³s‰ÁøSj±6)$qÇ,ÉA·–îØVaH`§V%H‚Ý•E”x­Ukja`“°‡˜ãNBC+ÒœÅvp–HqŠ6ý_ãç†QU*á<ïäqr‡aqìRt.—ÚQ¼ü29fP*ŠÖ¼‡­2&R! °Ó«¾»s¬ãmÛ€òÆn+»ôŽ(n¸Ž ,*ãlµ¿·ÄIâ>^1OoR&ÈÜÈȨ–ƒÊ@·‚•Õb}ïH\ö»ƒÉxÁC’„9䔜ÖÍ0Ç^0Ùç½W¡¨‹6 Íî—ÔÿOCÙþ´»ÅÊ;?½êÓWnß5„Æc&W)VmYS§PŒNM4Ь‹O”q —Ò\£.HÈtuAªÄ@¥`}rš²<²hÕØ i.+DPä'‘ØP  â3$/ Pf£MêÄ!8RJðœæ.Ыf”šÂŒÅ¬ˆ*"‚máO°¹|‹¥êƒé$âШwêð¢ÖÎ/GØ‹[¹Ø·}n-ÖÿÄà|óyéól2Âær7e´H¢ÉM'£DÕºÛ)"” ½à9%H‘窡Söà^Æ™DÊm«^»zÈ–"˜A¬í)‰€²‡p'׬‡¶R“Bç‚uã¼ZGVZ†j°£<‰KÐû¶Ûm4þ:$Ûm·E{Ak-w7b!é"thíÍ· –¸‹¹T#Ũ•ö;/ðcð4iþëœ[Q¶Ð´O¡²—»Ã@|î _Îlç·8ÆNøüBLòöãÝC%ˆW¬"Q[• m¶Ä+}ôBº9F;Ò°hëWVâÂÞÊk#R€@ ¬J@r?u[¼üV‰îeÀ§£ñ07Ý|õ—ÒÐd€¥Æí‰RªÙd9m §1®Z_Ì<ù¡ê¦yÒu¼DäCÁQÁñY¸…£'§É¢‚gpXÓ´í̘ålðìø7‰S0K>,çRBÊãÌ„¼É=!AáP™Ÿ’Š×öýAE˜ÔÚ{iÇ¥”à-ºàB?R}2ñG-dT“,d6g™b°L"DL'40„ÙÂ#ÕQVv9\˾k—(`2½ëŠØ'hqÃŒw¼¶ÔøÞÁJ*t¨EJ~{*ÂËÎVSJ.c1·Å¢ø€ ö«fs'Бáö\3lè®·Ûª.ý膦ËÃfßËÀ„"mDc¤V2 axì  ‘’puíaÇC\Ž+˜pEB")1çÏ–§¿×”:ª#Šç$×–k?—½zÄd¯mÝèñÚH”–VHÌPÄ p‚¤ø²(Jö¨<„µjsÄöf ¹I¡4E(MåJꀺ[C™;Hí8Y ÝÞîyxY¤Ï™±a!¯'7ƒÅ’Ô2Œ/X Ûa =Lý°h‰-EÖc¨ÐØ<#]/¤n—?v|ãÔ.Ýâ,ŸLþq?Ž~qð?¢H‘ek"¤~aÈù&jþÑíeƒDê ™RÀt;. ÔrhƒÕ`ë0o-’Š(…``Ô"K-²ˆ%©šûé@Ø'·ö4–•˜Gª©Šqof)—èT„w’P˜£„AûªÂfH¤H'ÁÌ&Ä 6 P ÐÒPHA`ú6?9„¸'® }ä=Ç`YYÁ¾Ñ©Jmû“jâîùgVL›&²w·SwÙWêI°„]Žž› nYÔ?©†H@}  ÖåÕq`5‰ÔÚ°W \ª*7 Óí©ïhF}z*é×0·S ’ NU…A™ŠqÓºòƒ²UwLS³ƒ3fhK(ª ¤‡wˆUÐ*Èð~‚¸‘n©š§´eóÛúc]B6TÍõ ¾O»¯zxH‰>?<®Å:;TPŽ") þÔIJ×"² ¤9‰tåfq†DFð¨#Þ¢€]ÀEõvvlì:þçÊ™ëÝãaÞ<í­®²\÷·ØÖvž×âÃÛû4Ùüþ‹ËáÇFÉ/Z:!#³Òúc¥ö°þ­ŽN÷ ߃Æ=ÑwþBü<‡l<œEaít<Òè®|;ìg–þæ––•R.~2ÜÕ›Y…9Ç#å‡Ô1‚ ”F xŸYà ±hz*F$ 6ucO9qKñòÏU$|êP—äjQSÊ3§õ@Y*³Ä%@ ú6ß»¥%8óÔ )¨¶yèãÂD©÷TƒË ;5‰„BVäöO<”ÅRù¬jj3¹Z…ÅV•j‚ h´Â1–\“w‰àñ„JÃ2'q‘‘Œ’@Œ&ªPŠfÃ$l¦’"Ó甆J¤a"rÕ%B˜C°QÄ*ÚT$¢Ø€àa‡Û«DMh5<†ÀFÀ;H€ûwÈ©A€ t ïqPLq%eù¶OÐÆª™¶ö,~·wÍ©‚FUôLψ©IÖ1W­æ½B§k‹j¸/Õ‚wjsókåë ÎüJ4šiïÏõ?™P" P½öú{þ÷ù1…f Œcžlˆ_²H-’k·ä2 §¬ôö{ „€µÙjnÜ•§ÝúžÑàvv¾M¸P¶ukñF‰ÖÊçR¿’)Ð0¢‹ZÃI4•À*ým÷ëËhBî½ÿÄüK¤Ê»– t¢IÑ3°x‚ò|­ØK¼ @ˆˆë‹EÜÿoE!Ζ›4 ù$#ŠíZ5Š%–”µe­-œr‰ P¨™Õ*ªÊ˜õUÑPr¦†CÊÊŠ(¢©E“"2š**ȬF¤'*Äå8!Çd0ßY_ÃH •Jb"³ŠÄ¥Xœ‰œèh¶ U¦p’º‡PÎuŸDö¡÷&}]DÌǤ$bdK.î]’/ºæ#‰®&¨Xò2dÄbDdQñxÕ¼N¤¦ãÛ—ÅjõÌØ{4^£€ˆÈr ]¢ 3ˆœÄDÒ9(¤‹Q ´ÿñw$S… ñõjigdo-0.7.3/gfx/jigdo-icon.png0000644000175000017500000004211007701377624015756 0ustar richardrichard‰PNG  IHDR˜˜ ! IDATxÚí½[e×q%¶2÷¹ÕèntãAO’Å(ŽD“=k(ÏCÒÇ„B?3¶Çá‡ýëpøËa"üãGøá°Þ6À€Jý9 ;v¢ú^âIâþ Ù®‘*Hɯ%ƒ %œÅnMgQ”± yñO>õ«‹†5w4û nNb»Ð»€Ìb%j©€à+e‚¨‚ÉÞ•(@ê'€ªa  Š‚âï¾ÿÏ^×95ãªçÊÿIJÕ €¨‚Pˆbg¤¨Ÿä7EZÚ…\ 3 QAj‹Ú$µ›È/3 †¢€”ý½3(!K©¸èÖ ƒSlG&‚™•t¿g7Ô ì†ÉÍÀTíõ'H–8iv© (À¬±F*ŒM(¹@s–…ãô‡AÍ nÉІð& ‰ÀHõòŠJó~º›ùE3 29mÞJw§{QA€ˆP$J˜Ÿ*êß—Rü÷@‹ßªvÂüµUüÄøA°ÃJLvÅO6Ü‹U³7O ˆŠCQŠ€9É\*³’Ü€“"T½)aƪ06RÜ{ˆ{|mÆàFÆ~Þì€j„¨çÂÞ¹&1”0ñÃÊíb ùÍÄÝ/«BŠØM«ÀXWqD@JH)(¥,z*"{¿õÆë mndƒÛ ˆÁ0W+¥@I"€Åïfµ ãQã€!@T`wÝ¢u¥ ‚H±ê†(\PX‘À"@ä¡'îfuOTï&U3 ¿C¡jÆ&‚üÎSñpà¯#ÊíÂ7ëU…€g²ú*ö¼‡–© v¶Ãq;,ðq‰.ÛSb(yÖË ‰!O™ žüØí”I1ÄݬI‚‹…Ê1›‘ElÒ™÷Ò.‰ZÊ,+-ѧÚä$”Õ®I"4°š/S»á  —Eä’ííÏ Œ™ÁÌ(¥ ¥TkµZUãê=Ù4DÂ0‹¨p3 ðZÄBd`Ø…'% ‹¹åz1šÇA=í€ZÌu/Ö—¸[¶”YÈîJ ÛTïr)žn9H5oeǦY ÙÃfX¡ÿ|u¦!aÆUÃ!Å×Ó@ÅD ²OÈp—0,’@@H,îdBDƒp­i"2!eBö°Nª]r1ã½ÔÏ¥.2˜&?ÎdQD”ŒöÉöœJ±°¶ø ¡Š‚²ÝÀ£ ˜xËÀRJϵZ­sÆ0 õg¶hЏs‰©â•â™IË2,dª_huâO»»4Àº‡7‘înÔz¢Ø“S'œeÁ†ˆJ Ë®›¡¢5ìi)nøþ‘ºH6W¯n`¥Ø1ø÷ãrÕˆøÕ/¬œV/a@ÛB “SBP2ÎDÆ­u€=SÁÀS¿ÃXƒ²ƒêíà†V„µË°—´yÙð` 32÷¦-3U0% ÁxDmà³ÿ3Á@‘ª¨†)¥ ïX¬A&Ð-D¢qCä–:8–ìôBqï Ú»c­oKâÅ“Å%« Wœ‹°d(”ì‚¥žjé}1,§c©ÞT²˜‹ÏjáÜ=Ú/=ð³xÿm_w‰ã´>þñ+ŸÃW×áØT¾:H ^@µ‚ü00Á0 õ\†W+¥ÔW‡Õ‡H¨g2]Öó`Z ÊŠmÔîüŠÃàl9,ƒ1|IêÝ´ÊÂu°Ò 7â”ÔKÐ艉÷2ƒÓlåŽqTO»™ oRú ¯ã “ŠV޳ÈÔÀz߃ýÀdµÌ6ód5‹$qPÙˆ@ ˜[êî˜)J4Ú ¦?Øà0!ºUi#ÏØH½¦ ²é5 uÂPTÁšAj³õïjÃX½*³2Ç’-¥Ü7ŸgÓJÈ‚¨–õŒSë"³*rΕ†èËFöK)íj„˜(™G!Z•xuWjY™VÒ•Š{1/‰¨¬J‡µ«ƒ;ÆðÉj˜@­ lw–{k"†À ìÆ>[xT'-‹,QÙ±Éè¡ÒÃúœ‰^*qÜlFT‡Jcø9®©j­=[¹Y¶2ÃatóecÚºÓ‰*Ù.²TV=÷@^Ža6o¤ÚÊÔ·ºì²Ñüv§X§QRä…u3Z#"ƒ—«¥)ÑêÆkx­Ù¥{µÎÍKWí jN .•ANëƒSĘ»Ñº D}ªÑEYÜ’qÍ‹âS æ(;ÊÕx@n4ÅR=ýз˜D]޶ӠöŒ}ED•õ&¢I6£„V>ÑÎKB#ajaœ¨¶µ÷igÙY˜íé±7… ÉÎeñú¤R­ ƒ¨–—°ÃÈvy­½!²–iU} Q––JKk›©5EA+–øä0˜®d}HQ &4ÂÕ{vjb]Äêz¢‚¤4ÁêîÜZwZ²¥Eº¬tlÎ=Xp:ñ¹7²Ó2ydhv㪵IªôR¥px’þœµùt¨ ²îÔbme…Êmx¬Ž:¥S^î´(ÒB,u¡³‚ýFEÔ;Æ/DX—‡–¨‹JåÏ4pYñ2–ƒ8-ÍÈæÙbx®ø†abh§=TòÕä5I«Â Âd\ÿÚ}¢×|vc0Š*‚ZAðûV±†6»~Œh€›$ˆ[=Kè©CÚÆxAc01TË´EÇß «uYHp‰õoDƒ£T’¹üz©ýÍy@oXñÏÅÏÌ˧鑮$`°Î[%!ÞgxŒR,͵´Î[»¼¬ÐÅbµy0;ÊR³Õ·PüS´O·^t,8GS@ÅS¨ÀÞê™j]´,V4öú¤u¦ú1é´ñHw®¢…ÊÚšT¼§íj¶Öä°~-ªÞ*²ÝwNȯÝ=ĵeÅÚW¤v8Ô£ÃmQÞ©P¡kÞ‹võg3ìF´dd=¡xZÃdÐ`­éZûu<bÔ¢~½XH®å1´Î‚R‚LÕŽ|uìEµÃ¡]aqL„h½ŽNŠn¨¢ï»ªžÉ0¼ß¾ò1ÞÐÔ¾oErªÅ÷Ê×x7«vu=UÝQ#Þ6²ÞÐú0y ,<˜@}À§˜Q! kÚh´+áÙÕ«¿ßÀˆ­Úž‹ÀâBeܑ؜€Ð@m3\?i–ê¸1g’­À;Pk[³Ö¯ÝuºŸõ&Æÿµ‹ëêCå܃f Ã=êÍr-ú`Tegô5‡È˜˜IÄx€ŠõË×°HÅÛr©UF˜tä×1—IèU8žŠŸÒ"{Ž-&•”zcÖ‰wÓjÕƒÝö틬÷bKm(K”Åé"Z-ÊP"'\»³ç¯ïbÙô¯9D’¿Ø*­UŒ$²ðTiƒèM÷Qª¾˜=%ciÉÚYž¨ˆØw™…š7“®Ó"8:êýw÷÷¢µZ½q \\_4®%LÖÈ?­öäÕg ÐWjÇ/ [b§µ‹MO„¦øÄ9¯ $—©M¯0B͘úÎËÚ0Ap¬6 ÕíöõÉšsNæ'}°#^Kš'C`¾ LµâûÚD§];ÐŽ¬gÉÐæl~?Ôpڲȗò+ØëŒ£¬½"]ò~B, >ûtÊ ÂæÅY¨ ã"ÇbÑ.–ì÷ž‹úROAZP¬Þæë”E°E)@*¢•¨ÕA³2–.c…¥nŠ]uÈSËèSƒ,µ“­gO|<ñ¤d"†þ¢Cb»€ (,P–šºJÅ_ÓlL.²’wÈЬÕVRÚYŸ¤VŸì‡"t‚À‚m–®%»‘„ÄØÉÝì3¢ÃŒîßuïUß 9¦åiGkß⽜‡_拲¼-8BÕŠÒ€WU?‚9ù„s+€Û$/0û .µ»¡MvSGÈ6OæC!D>,CØï:$Dk©hEí .p„»o—áœVü5¹±zŒ¬ ´Ïr²LEvw2Ž,«Vh ùÀ+ƤÈ"–î:©Y¤qQ²ž,Âdœ4ÜŠvÙ$R뉪>™fBbE‹%(D¨ÖGë€FåÚq=zëƒ;ñ×õ2Ò§)DjßtîQ£·E§QâD l”gtå5?ªJS‚–4+2Ǥ6ö_²€Õ²L)aýè<5ü5Ï9ƒ!Y7Ikã™ê"¨q%[û€#G Ó-»0Õ¾ŠÓf€¿ú•­0Óx/#­ëü„è5×wئŒÈ©´aLÇB hR^aUçêPñye¬h@á²ÕÜ7¹c¢øˆÙó}XM–:‡ÊCq#ÒP0ü¬up¾a…ÉI #…‹›Õ›é”QE4 (©³úRç«ñÛõy°ŒMÙ —3XqòèÅ$ `1”§ •¦†ýCu­Æp9>š^D<™ˆs”N Ľhëß2ûèp˜zÛuÜqD„ûÏÜu$ïus†Iqpߥjd¥%žhˆÜÈ›r€uÞ`•VHÖ‡¥À ìK.–Qò`„œ÷"Œ2Ý0íu²˜GT¨Ud“Ñ¥±Æµ$ÑQJlÓG«õ%­TKZ¡gvaF²æÁn#ûÚOC™¬Û$¤žÿBã'0d^±¹Öv±ŒÚp”“€‰VÄažêfh‘î/Œ/O¸ÉÒVý\ûì®Ý˜vX–‚±ŒXç °Æ*% ÄHÑL­F‚Êî«ÌPXÉ2»F).KUŒ¬+¥›;ó ™˜kOWôâ+¬}·†9M<{'EíR¸ïªõºwtȾ÷âƒ;y¯]FuS…JÐÑ~˜¦Š½,·Kïã׎``ÉØÈˆM1ê«Ä•0”üàÚ?o`;„L¼>)m4”|̬õuÅä’ˆ€…+‡V Á"kx ¥ª¡'"ZuB]±´ï-§{ù ƒ;͆öøkOLÊožó·síXV½ý|Iàº<˜¨b”Œœ36ºÁfXa¥Ù½Wj³ëÁ’QêÀkÓƒðq41EC ‹@Èe.]ä®Åz yRaÝ´Ô••³hmÿu8·—©_òb7ƒÁ=þÚ.¼'?„Örœô3]×1ÿ¸?DÂæ ³*ÖeÄŠ×X‘ š«`ÈpA^g”uFÉyÌ~€R{ê©›£d­KDUôª “ðvE^!ªšW¤®ÈçÓæu 6÷ ƒ6…kó|œ¿ûH†u³iR<Ÿ/™>+LR^¤4 ‘‰$Ã~;6È÷>idd¬ËhĶsŒ ΀lŠi¨‡'ÛŒqÍ0ô'î³PÉWëšTb”Àb,èX¯·›4&hóú£ßTE«FY|PñI#¼ÿŽGö†Æ}ý`§ûúú™¦æ(­ç«b±^YP)¡ñ`µÔ#‚±øôŠIvŪx4¹LÙ” çÉŠ’smm6ª ëü"]ìL©®0t˜­ƒ½ÞUirÝô!bÜZ¨æÒDµ•™ð>ü}íhÞgh§Í³=~ù‰FMÄv”ÚXØ$ *Hã–¹Ÿ3ÀlaŽŠ%Õ²4ä)R!pVȦ@6#$g”q´‹.b$®IôÆÜ+H|œM "d×Ò'!«¸1uFŠhÃ.Òé°Ê„ ü¸ùÞwçÃ{;ToVÿçWŸ©rLJ ÙÒ ©R µ.×N¨"Ej ]Ë®*«ÂàÐÑ´·4 døKA 'R“÷µ“Ý) &À5ƒ‰$4É`Èk?†½lZ[³K…Á…T€wÜz/n;¸uoxœO-I9J‚õòÖNåxK*î’617 űeM!.à–½±p4ΉEAyH†–R°qÙJ/ÕH6Øþîó÷ãÇ.¼ àž³oÃùá,ž¿z Wä \.Wñüæž_Ä«å l?uRÝÝj´úcÕqφ·Ú]r¨=eï¿óá-C9̸nɦ¯­ŸÆ ù’C©§Dö(÷šQº s}L $¼Ã(àQ ™@ÙTš©$ V™Ñä®Õ?xû{ð;ÁùáìÖ‹ßsöέ‹þýõKøÖÕïá›o|¯–+.ò‹ÚeK3 ir™Êa¯óçåýo{d1$Îççv£Ãå—Æ§ð¥ñ`büE~/—×ñn¾wÒÜFçñcÃ}xh¸w¤ Gz½k=¾Ï¿þDÝbb;|O’/ÄR™…G½Y¤ˆñY¹4 ÊQAEÀY‘äÀ:2#“ã>Ggðó熳“lã°¡ŠûÎÞ…ûÎÞ…Ÿ¹ãÃxný"¾rù)|ãòw€öehb¿Ò¦—‚Õ§Î]â¾Çjx<,cÜçÅNÒ¸>7þ ~7—ä²Ý¤Ì®<]žÅwôû€*>źLä»ñØÁ#xì–Gª±-%}ˆ:Êñ>~õO+‘Úê»M¥°Ö¶{Š¢3´“ñ`Ž£¤˜€.emY€Q à³$ ÎóyüÂCŸÆ¹tËNÃÚçâóçîÆçîÆ¿ûcøòkßÀ—^ûºoç/†JSÀ;(´ ‹ àâ™ øø})<UÑð8'ô%y ¿rõÿ«xc2\¡%ô×R,ùéŸ.ßÇÓëçðÏ^ûîÃpþ1|è–‡·Œ‹®¡韾úyü _rœå½_¾o y. 6Ÿ@ضåà•l#˜d[}RÈ™0 !ekŹ˜nÅßû±¿‰[ø`£w ëE9ð6¾€OŸù(>zûû𥗟ė^~²vXÛ¢Ï2™A"u‡"ão?øIÜ~æÂNãêå–¦¹O24¾(¯â¿½òâUº ;Þð¶€T™© ¶Cò´Í.ˆ žŸÅÿñêp÷Õ/ágÎ~[ýøÞPÞ{¶xþùü2~ãÕÏCŠ 8K`xi])¥Ó¸]èÒÙ™®‹hU_e×wí„äçÓâ&D{ñàVüòÃ?‡wœ¿g‚veŒs-Ö]øë8†öß¿þã}\Êb(Ì`$_9H®iX̶µ”¶d‚´n&0D вf¼Ž+ø—ù |­<Ÿ>‚w¦·o)1öÇÿß½øñøëOÔlQr®Ý/F¦}º8Ë õxaqÁÀº^+ßIâ5>( Vçð‰ûÛûç†Õëoí¢z7¬Ý`mÚC·=€ÿìÂßÃ+ë×ðÌ«…"‚‹«óxàÜÝ“ßY Éýߟ×Ü£ýæë_ÀŸožÁÀ¾t^lP†EA%[oœ’o öåõU@Ùåá¿Ñl˜G Ä($H‰ñ¬\¯¿‹»ómø›üîœÏÊ+øG—~O^ý. jû·‹´;•?lÍmE£Nu˜Ž»R|#­Öm±¦^hüŠàÓïþiÜvæBõ^»°VH õÚ§óPµËÀ°bEÉ0 (¥àmøý–‹{uÚç¡q®$Ýk€$þz>¿Œúúç‘)»ŽYB‚@Äj«IRÐce$e —Å7ðÕMݾ3*ÎKÃÀ\0PÓÊ•MÏáþçõïW÷ãN~P.áÏ×ÏXÈ-¶Xƶ¢ŒæµøÆÇV)'Ó—ï¥"Wnv5ÁQ~ð“wÿÄÎØé%Ý^u—EˆŒRÊÎÏÿzC‹ãêµ¾zå¹ÑÍqâõÙo¼öy<_^öåöŒ!4ÍŠI±—bRìáÅÄ?“·* )”ˆ(¹š‘Õ 7ãˆ'Óö2ù" 4à5½Œ'ò·pe¼ŠQ³/5h#¾Ø]rë>ÖQêNÍ: ߇H>wb,²³-®ú¾W?}ïqÇ-'Økîær”sÝ`Ï÷ öª„aH½1Ås9çºÝ«7Æþ17ò”R=–¹‘íâë®åñ{o|ÕÙp“bã5\*d‹U”-«,¶2š]T¹¸¿#6Ã_Ø)*  #sF"Æ +Çj„„3pU“•ÙbIl1"µä\Ãa,2S)nXm0æò»vTËÀªG 3n¬âc÷h‘ðÛe\ë/î> ëâvXløÊ9×ïÏ=Ø<$ö,Žã¤€ýç¯<äKUºJŠ…I‰…¥ø0 Û.ò¬¦û²P©põ¢ÁÖ’¯3Lm(Y™Ý‹‘Md‰7$J8À`F›­§Ø§9«ÐŒöu[àªËô„œ|Y ŒÉœ´ét©š½gƒøØ½Âz½^ÌXúÔWïÅââ.üÞƒõ†Öc°øïÕ¯’[Ú6÷\ñy"kdŸ¿òD`­BÉ^+•l™";QØK`3j&g£ú\,!Ñ`Çh[QÆœ‘(ÛèàêpFaÆP묭g.[xZ,c,c·Ï|nH}¡{¦|\¿X¨8+ù¦^¨%ÂÇïûð"%Ñã˜ÞSõ!²7°}lÉ“ÅfÕ9öê½×ð÷¸jiÑÂ.½áñ_]þS§¤Îkƨö%[6. $aš± % _6ÏÄ~_+8%oØ$”1#cÄ Æ Æ„ !i IØÈq7*ÉZŠö™ÛÊŠ·fRš»j‘× ö}ƒw˜ûJcfÄ„ßÿáe˜óÌñ×>éû碂P{ïg,Z‘q²FV—aŦª2˜V{ÛÙÛñ×®½?KÙÚ<{ÜÕž<7´ùÌÞ‹Íy³Þƒ-1øóB÷q Ûÿöêw«V-k±Ñ?µ"6TA.\,¥X¦'.Ó¢QÓ¼eˆ‹+”ÕT’ºe )Yh°aFª‚¾[SI+õ5}î©¢ï«R½÷Zˆîæ“| Ñ‚]Wìr“µüø?¹ÍIÍ]!i¸_bÑûúæœHo΃툻ZtŽU{Ô×,¼Í€Jãò¬3˜|‰}£ê{2=”’ \8= ¤`1eÉ£tTT€¤ËHSêÌ(+a…„ä’ RJ ×2!W±ýQ«ï³I'h` Îsuß|ûÙÛ„¿æ@~¤’–6J̉ØÞ£Å×K†º¯çë¸ þ¯ü’øUÔaa©»ÃCæÓ¦ž äKãªâÆñÝOŠ„¢Ù¹´„”Ø6­øÚi&†&»8ëá·ƒjöº øä³ JÇ )¶zïu{¸›\œÎȽý¡ÅÐØã¯¥Úã®ÂòauWm0Œ,Bcoˆ» l×qŸDyèÅòª/@1‰w¥d£}ÿ”{ Ò¶G‰B¡×<#ì³®qõív ì=ãʃ÷ŽY%" epJ#Õ]çI+JVz ¾o(ìG ƒ'9Ù Õ©_dڱǻa¢£Õ®×ki î ÝùÃ]éóa­ÐÇ OmþÒp«ª¯K•†½;‚4²Ëô8µ-²ˆ$†MR»‹f÷Z¬ÐdÙ¥’·öŒ^Ò`•b(̲ÕDŽÐüï7ÅÂYøc:’‘XID tvöÉû>‚õz½sükÉ‹-aŸãäÜЖº;õÍ'eXíæ¶‹LâÈ%D|¥Î‰¢®K\gå˜:Þ‡¶MXDÚZžXnïà_SãÙXÜ&FÑUn^¹h ÊT˜¯uMtlè¿1!²YÕìß1g87¤Ñ³/Ó¼Ã9éAŽ?¼ò¤kv˜^ÔÉàVAj¬¢M®½SN€oõ­ÐÄûÅ#CDT |ÑEÌ6n8a( C¼ÚŽ%?Sªû§:Õ“׌Áâq×-wì:XêýÚ7µs’ÃvÞ(£šâTrÏÐCkB-4¿èŒ-é¡‘v#|˜REÕÙ†sf6æÏeƒ4r5Vñ9…Ä !,r^îM²§Æþ™ê¦Ú¿óÝ‹miiÔ¾LñFϾÙ3/äWZ˳…¬(,\5}ÃG¤-sì×6ª›bűÇR"Gêû;É´Û’ØJ¾Dìãh&°Õ6K¶y†ÙÌé›imC…\±âÍ÷`cÏØý’a-qL§q4ÿëïVd8Ç› ÁÖ0èÿÖ X ªžx¾Šã°°r:Œ Ý"ÖØ.Ga ÙJP&øƒáÀú¿`Ì=+³ßV—uÛê~!R‰Ú¾ªÙ®í]sŽ»ø®–gyÓÚ‰à9xîC¦ÑTÁ½zñš<»›ï"GŸÄÄ&9ÄVÔQ7 ÛOÊT­†$^rêbÙD ™ úUd[Ù#ßX ïDkÛTÖÀÄn…š}Ó©×Ûê¨%rmã€Ù8± ½”&N¢ÇAuõ!´ßfìÚјkÖ†Qôa•(DÕ[‘(Vi5ÙT;°)…o :Y?W,c Þì¤'TÓöNí#ˆ¹ÝLª5A ’ø2{/2O´ýÕ÷H+†W "(Œ¥YÄ âTd‡Óê6¦*ƒ¬}–«ö…lÔkR,:óbKìú ›-D²6FJt¤•w7››FAÛ5Ô 4¡bzh²}D¥îZP{²mÛo¦6øÑ8+ìoŒMÒ'€óR) ^‚r/†„FÊuãJëÛ›&Ÿ0“ϰ¹=?S”¨ãÃW¼Y„ÜâÜ7EÈŽcÇ\Dèÿ)ýâÌé~óžáï¦zÔù4Q×J“)%#®L²ÕUk±=ê"U«$ëúwèµàÇxG˜­-.g*E0ŽŒ›ŒWÚ¦®š‘uàMð\‰Ö òìß”¿ÿÚŸ-ƒÜ…çôw þ¨?>yû‡ R¼SÔ6¡„¾mh|„g"ÔYD¿|üq¯„šÁŒæ½b`Yü÷\Ý»3¦PÝ–\êôv)cÙ˜L“KaY®_q/X ˆðóøä]í5®kÕ5=Êãçïù~û{Od‘¿xØa,ʱ®›òé6Ê©Ë:MZ¥‘¬J­ñ™óøÀà]·Þ‹ßñh»!Ç×ðÄ•oá‹oü96È.+oSLñm‹{3íKƒ'ŸYló6iõ]ÛªP&¼T^ï¿ô9üòÅ¿±hPóÉ=Æðæ%yÿË¿ƒïày`ðåÜÑÛÄѹàÍt/¼q ¿úõ_Ã?úÀEüøÅ‡v×®NØã<~á¾Oá·¾ó{wIiJ؃+±Ø¢}ôjÙ&3Ðæ5ê“Ý’UTR_ñŽ[ïÁßy×§ð® ÷-¾×;.âÓÁG.¼Ÿ}ýðuqqeFÝpkך $öWn &k«gÙ—t%'˜ð;—ÿtåÉBqsv­íe½ŒßÌ_Ä}õÇ·ñœD¤ÕàÄŒ*º5¹Àç -¼üêŸþo[B)»ä–0ãuÑgnÇòàß5 aô±°‚桲y-Ôøg©ÏIÝ^ÒB(eˆ”hQ\Îá?ÿ‰_Âñþ_ž×.­;Vñîü îOw™qu8Ls+akîâÆñ`íˆÁCÛªÎü’ÿäòïATñу÷LFÆö}æ)ž¥Kø*¾>~ë¼1wíëh¤˜¸ ÕB¢àlÓ<”m«®dÅKò2~óÛ¿‹_xç_ß:ñýÊIâ2"Â/¾óÓøú‹ßÆÿêËVÚ‰Éé¬.`ÇÍÝäwõr1‡X´†Öªží!òâÁy|üþÇð3÷ÿäâ±¶6çïßþüÊ+¿îx-¦Ô¬XnZQ^Qt5Í? Oï¥"ë%rA*µxM‰¡Eð²^Æÿzù³¸$ŸÂgÎ~t¢Ï5ÿèoéÄ|kx_^=¿Ä x}|£­ZöPQÔ&¡U Vî©XLþˆ\ %ö‚ÛV^Âo?ó8þÖ½ŸØ2®8žþÄ÷ó”Ç=‰ÿðÑ_Šâ‹Ï~Å© ­}ùðŽ Û1àܘO~#Kmúêw)ï8>p×{ðÉû?rdÃZê*¾+݆Oû~oýDÛªýÜ•‡ƒ±Öµ‹7ƃ«/.‚â5…v…(~cóü‰|]=Š¿~z"­Ôò÷ñ%y I/à©ô,¾{æ%·ò±/oýÕ‚Z·3àl'×AR¯÷e€3µ0é*6/­_Æ×^øÞsÛ»å‹õ]¹×Š—°ÝÛÏÞ‰ÿòCÿnKçñÙo?Þµãkð@A1© Ôp–³õ?ñ¶Gðñû>¼— lׂ‰¹<ÂÏ^ø(þåhËêèf´tÇø-TôOv²€²a|˜ªŽª‚’ÕĈ½Ž-­}ZŸÇ·×ßÇoä/à§ny/(%|òÜ0¬Ú„÷Œ_ã<ÍAÁmg/âö3pa<ç/–VS±B®k¥ŠÖj,&¨«¶uDÈ>\ÌðŒÐ´ç¾þÒ·ñàÙ{«¤AhƆ°] ïÆ<âq€ÿÜ{üÇïýEüµ;ß/~÷Oðÿ=ýûþCšÔôY]M¢’AVçñÎ ÷âýw:Ÿ ?lÙÅ|Ò^Uñö3wàëKÛXkž9v ÿÄBdíL'Ãì¨Ó„Ðn hñßeï4/ â&¶!ˆÍ›å ltÄUyé€@8 ¡ £‰ ctC/¹˜ˆ”¡Ó¢×J½|§¢âœyXŒÐµT8Ù:—KÖÇ\ëF\Êû08ý÷æ¡UUñÁ ã_Œ_jÆ3wd‰Yú¥A¦“ó`:·dïpÍ1øÙ:.© –yó-Û2øV‘'(kí !‘ZpÝauÀX ƒ“õ’sL;û†7ê˜n—5 ïÙ#<íçèà$öÐNUG¿¿H½Bu\œ9)|.¬ÔªZ)‘¥ßé+tþ—ÂóÜÛÎÃßÜØæø¬7VUÉB#hÁ¸ô†u¢ óö ›@.«'òܨ-½AÆàuí…S[¾ãÄ0y˜m¤} ótI°âÔzÑ•ó­-!ÞKº”ª‘SÔ'A(¾ž%$H÷¶o×Ñ\¦óZ¬7 X­V[Þ+ŒkéXve=¸_ÂYKôÄÒ²‹Iøcló^uª¿e—'"ÝYXU]jy3<ï²(®=]a𢓷j:T]Þ©ì…¢@±)k3°dÚ  ¡NÄD{JrÌÕ®I¾¨ÉRVØàž,jm‘î*eõÂÁûx¨k •= #ëC]o\ý–’%›{±]óE«ó ´B…¾º7¤]=/6´ÜQZ‡v(&ZV±€CÿªZf»N{Ö|$2QÙ¨¬‘V^_†:ŠÀÎò3‘xÿ›"&Žk}s\‹ðð¥ŸIâ{–æKûúÛ®7T.Yd“ó𸄿Ë çÙä>-Ú-LÇÑÿåN`Âà߸FÍs‚BûNžR­Y]2 ­Éà˜š‰–ÛÚëýNêJÔVÓdÖu« )@»O‰#t¹¥K1-“1"÷\ ’\%” ´wåÒÇq¼×aF¶Txï«oš{Ÿ~cð>¹¬}Æ¥=P;'°m\Ôè§ wfU‹ã1ùÝÈ” k Ô oD\=©Ú„STæÙB÷9ÖÌ‘ ‹­ž+h° 6[k Å„uUr…™6-EmŸ·Ú" ÉâÕ…åž´}mE'ñX2²ž#«z¬ mNsŸÓ»º%Ž¢œ]„–/Ëî7t#º)¢ÛÒ´C¥SOv¹sÕ¸¸öý†uë.˜.Z ùnB`D å¡õžÏ´³ÂcÅï[ÝÝ šÉ~š1óK-Ý»$Oê±”$mÑÝ>žl_q{ß¿w…µ–é.ͼáðôvê: #Üu!oBÄjk3‰VKM3|H½;ÅNŸJ {ø«Bij„)‘ôËÂÍgÚ qA©jrÙn:R÷êV™Óñwvñc» lWׯ¾šåþâ!S²-ãdÛs-J`Ò:ä¡êƒÝÍgû§©%J‹uÛÃ&¡näã(ì×ò:Ç1ªÿ÷¥?œvOlŸ¨W‹ljAWL;*t²Ñ¬é-¨:š¦Sç›YÖ×­ýucÎàDeQA¨÷¡ö€Es¢€KÓÖ2L`]^¸/$½ùf¹ëm :ÞAD&‰mæ~²£è´LÓVµ¨C;쬻tQ\£u§×)u•j‰Љ¡6ê•¶°¤…JSá[ñàÅ”ú‹ÖÅÚ«Í›1Ñx:­|ö‡fDoª±\o(\* ÉŽX'*@GMú± ¨‘8õ^¢1û¡J­NØqâö¡Û[ uvµ%¾ŠØFð# 0¡]¯û)W&ÿ®3·ÿð/äàãÙÍ‹3£š^€ü°«É˜6؆K•Y—Àf®œì»þ¾”ææ8qMÂì*3Sì‹QAΉϊ/v%AʆÑT*æá  Ø(“=™Ô®.ˆ›áñÄëOa‹Žz31Ø®øK3âZÇ´¡Ý,_¬Já¡©¿u£†ÇHi‘wQ–ZS$[‘ˆØúÅ`¯,æÉIõøJxÛê¶Æµ(ŸvÙϺÍvWYèÍXçW‰Ìz F¸†ø¬ñd³ 3®`Õ«:_UMÓ¶¥¹µ]4m¢kA°B,RûÄY.l=ùª .œÇ8›Îê¹Nó’ˆ]¯¼þTÚº&£ð„ñWǃqÕloÛ(Ü#ùpE\}òM·½‹|&ç$+|.:=ø¾ CÚ;ê L ¾6Ø:Z‹FKtA)jºQÖRš@œã3׬o–ùýñ¥Î°ºkWÛÒ»•'¼ ¤‰Ÿ¸ø%òpÔs^¨Ì9Í«A4ÃRÔÂ'M2“¶ª¹í!ŸS-™ÐŽ`­­Ÿ@‘ Q—¯½Klò–‡„›Qúsòù*::ýþqˆçʃÑV‡š§I!6B`SDôöèନ£]-$2°DºzoY»øá©•« ]²¹3~•:q^YæyK:X …sú½ØW¯|óp?éǧIÛÓqΛQ·j_?IÀt¡ReŸúîŽ)¯ÿnk"»s¶ªº5êÒÚú}€@ɉ\FeÿJ”íy%EI‚»V·º®ù°Ö–Së½xðeçº!L¾óNâX'z¾T©éªët;|QÛ"f_s;XUôc$Ôý­>ÞGn o{–^„ .IIm –±›¤·¥·gÓ-G2¨ÃGœ:€ù©nÉ:¼…éܺGßr­–Èê‰\÷—Ä‚L1#Ó®Æè+ì mOuÝíK Mâ}ê¡tF´°Y¥…È MœwKƒÙК]XéC ¸gu硞kW&yšÏþêA|ˆw³¦[Y¤œL­¶Ž­1±÷K¡#)Zf¨Ò¸¦º½^¨Iœ: £ÐVd¢ªæ·”)“‡GS]D>‰¸Î>©ã¹Ð•‰–8°£ìU:÷Ä•ov«lÐ k%q«ýZˆÔÎ×D!ªcŒ.d¶PÙ/7Ô6[Ù5&.Ì`€Î(©“ÊŒfÂÖhh›ÖÖ!&—ðâ:1á¾ám‡ÕÍæ½àÙ«/VQá:4Ï%MÑ­ÇÏ ¦…w[¢ÔIkÄŽœ,}:S½é6\¾ZÅC)û"¦ÉÐV 0Ù0f['ºZêää®SaãlŠ6O˜ XP/<ʯ]Yç©Ê _ª©…0qžêƶ0©ý¢ð“$Z©–µÅK1ê :¢¥v¢J·Ø)ŽOh7„ÞýÎב«ãªH,cÁS-CÕ ±Ôü[LÁDA=i[L´œv‡b¯ùìài3²ºÅMfFÕÉe-‚þ¸v'0uU=˜À É1 òÛ¥›MŒ­aÝA…Jr-j“8OGùõ¶\"¸¬N² ÇrY,§¨ôXªA“+ÄhG‡ìóN»BäÒÐí©Z$1J'D,S ¸¤É4T¦ÿ$=XRøÚ§m[&º]MÄV4Ê9>íí{¥ës i༅IíXþ^ #TÓ:u§%•÷wÅÇèA³±7‚‚„ñ?¼ñÏlÂw`ÐÀ`b¤’@#[ãâ˜ÀWÙ$§Ø†DˆÚèýŠüÃ~ŸÀ(š1JÆ&g¬eƒM!š‘U”y¸–¯‚Ï‹¥YLÜ­<î³ê¾gJ°ÉY Æ’qUÖX— 6yãNF¦#ó>.í"Mé•®Õ n#À¨Sã‹ë¿/'i`”PJv¤Æ‰è$F‡º ŒYW«´=¬µé°›ð&ôä‡;"Aë/«ÞIg 'šs$DM²ÀVúö:Œú;b4?ˆ|yg*`uÕjX;$6+À ÊHlKÕ'FáÓåÖ—–ÀªÈÒÉWé–»­ÍJR·ÐÎ+ΨYáƒÿH¡ˆÍI˜šù•lª†BôüÑöàµF¿†oÖ…Ê>³Tl·Sä`PFÉæ9¸J.uA“­HéîŠÜôÐspÎÜÊ Re[pÞwmôwpßóÚ_ úŒô’B®tèêÔl'#%oï.†* º± ´”|Zmz=J]'lxÀ™a@"[Cœ’-òdâšÈpQp‘º/’JÔG£Óħâ%´nÃÛÚêPf®ñºÀ´!eÔÍ$ŠT©°I[ùzæ ¿µµ¦›@rç¹ÄCæÄ{ÉÔØÊ h8´+ö~wÜ£/lò…⋜j˜DkÓ¡Ø»…¦&ÍM×Ò¾þ¸0?Ùî–n Qíù‡“°è*MXt—ÒÐàC(B $6Û.|ÑW=…É 0qaLΤLƒ1JrŽ4Ìâ¢0E¸À ¼Ÿ<:Ä.[ÔÎÀÐWó¨47®ÁƒÅú¹$„â=ï”aKÄ×É©»vâ H¢¥Ý1•‚à:Ü1õI³~0Ú1.ÖÍZjßW[Hªw²S ¡:÷£X­2EÝÒþ®°¶pI‡¯1dJHÌÈi…DŒƒ´Â@ 9Y¨Læî|¹øŠ5© @{u­¯v²–€¸¶ ŒÚ’SÿZTP¤@ýoU>ß®6Å^:-…‘õ_—“㪭ï'êÁÔBHxÊ(nt:YuB~Wé*U×¾NÕd¯4ìE]™©O;pÚëp4…Eí+˜؆Sj-?H–Q Y¼/uAÈŒM u,1Õ­ec1˜9­0ð€‘7Xñ€`NHîMÃÓH]kìºDõ扯uòµNZ”*HˆýDnTÚyt3°Œ""Å8J)Mð¾Kж~À™ðh•šièœoé¥5ð8ŒC4&¿P‘pÝù&Q‰šÕÙ º&FWߘt µby Ú¼×Bmr2Þ%YqÁŒˆÕjÅäKì‰q­X%_qç¡OT LPöD„Õ½ˆ‡:rÔäýkÙËe™2x@æ„’Œœ°"óbÌ4U€tC‹ŠC äE§@»’šzŒë/]O€tΧ•ëŠéÅ2J©Þ³Ã`Á¤£¬÷IDATº ÑÐ{1tž,ë¶wëëÝõD:nÐ ,tOI€<:6Z˜4¬ñ$Úu@V8¤j,sˆù‰¥&4Eï‘û;]ò®3ƒSÀà°‹;yëq:9³y”Û广†: ‘ûÖ Ä Õ¨Þ2ºÓáÁvy²Þ°vQ‹ì°5(oÏ[Fx-»’³Èy±ózRß2ÂÓåÁö…Î}¯±·›b‰;ªñ¼•éÝ|‰ÀÒc¸–<‰AÌ·7—·ûÿt&ûÉ|ÕCIEND®B`‚jigdo-0.7.3/gfx/jigdo-logo.png0000644000175000017500000011367607701377654016011 0ustar richardrichard‰PNG  IHDRhr~ßKœ IDATxÚì½{%çyÞ÷û.}ïsÙ™Ù@&%Š 0S¢ÌR™.;fY²)©’*'N•íȶœ’d'U±c%±¢?¬²)¹˪ØVâJ9)+¢Ú’b&¶"‹)ɤDˆ½Îegæ\ûþuþøº{gÀÅÎY`AŠ‹y«¦vvw¦Ïé>ÝÏ÷~Ïû¼Ï+꺮ybžÎB ¤ÆQJ*æéŒIzÈ<Ÿ2Ë&¤eÊ$=´?ŸÍ˜fÒ"!79ká:‰½È™hGºxÚgžO1Æpc~IzÈîb›´H؈·‡ëlD› ‚ñ¡uïi’ò™Ëÿž³«Ô@Z$N€«<þÐÆ·à*ž7 0y÷{Žt™çS"7¦4%Y™2Ë&Çþÿv¯w§q¯[|¯â#÷ÛIÕuýaàƒòõz‘«‡—Û£¬ xeAarûUTÆgG9äGQ94Ý>žöñ´Ï^Æ‹Âþάuß p”ÓîÀË´HùÒÞç¹1»Ê²X•)U]q¶¾?¤ï)LŽ©+”P(¡(š÷¹1u]ST‹|βHX ¦®éûC\åuÇ|¥¨LµÒ¿ÆiœÆiè×íÀR³ÈçH) tH’/Y ùœÒ”äUÆaº–ö-(©™çSæÙŒ´HXλ}"7&r"B7FKM^e„nLeJ&‹ Û³ë,‹B|í¹1®òˆ½ó2pÞý_Üû=n.÷:`xSô0›ñV–%ZjJSèY6é€ +S–ù¼ø¾7 t"ò*c¬1 G'^›Ýù6R(L]ÑóÝ"à;þé]y§q¯@.ˆÜ˜i6¡ª*YRcÈʔҔ̲ ×gW;pže3Ky¤S’r‰£¤PÌó)ZjKu8i‘`ꊽåI± t"R‘ÒóxÚgàéyƒì*Sqmr™ßßû<‹|N^eÔu‚­Þy=Œ§}”P!p¤KiJzÞ ò¼ÌIŠ„E±à0±ç¨¤ÄQãp!Ä+^“en`šR7Y7ÀŒ[TI»ÛˆýÞ}qƒcîÙ±¤”§Oìiœôk¼Ê:ÔRÛ¬³XP™²Ë>‹ªÀÃaº¯CŠª`Y,(MIäÆ6›TÐó,ò9EU{=É€¤H(LÎöüˆ–šØq¤{Œ>Ø_îñû{ŸgžOٙߠª+B'bàyhü®òÐRû½@‡!ºÌ9¯2–Å‚e±à ¹‰OyTÆàH×R*Áðk®EZ¤øŽÏÕÉeL]aŒ¡4%®vY6 LËuk©»Ek‘ÍQRŸfÔ§q§}ïb’:‹|Žïh©™eò*#-f HJ¡ºÌt?Ù%/s&é¡¥4ܘžÛÇÕ.çz ˆ¢*p¤Ë$9è2Ø´HHË”À k(©‘B¹1¾Øì4òÂþ—™g3Œ1¨æu'`+>‡¯}6zãh½´ç¤\6@›–ËŽ–‘Báj—ž7°…P¡n{=Ò"áÚä2ûË=””TMF©¥¦¨ "7ƃ«]Šª )åØ,¾*(„BJ‰’ê¾»ùîTŸ¾Ónä4Nã _e´E2­ʪ° ÓlÛ«ºbo±‹©k*Sp˜îcêŠÃ䀬ÊÐR¹±ó*aŒpµÛ V ¦®ˆ~zà j#Àw|*Sq}z…íÙufÙ„½åËb§}Î÷`³w¶ËŽév  „è^£Ùe¹ä 9ètä÷p•Ç™hó¶™ó"Ÿ³·ØæÅƒç»…@ ER.úãŽÎi^ E^æh©Hh®ç7#åñJôF Ìu]ߤ_ÎíßÛãR§ñF‰{z§ÏÓY÷àI!:IZ^eäeNZ¦˜ºBKÍaºo½Èp•Çká:¥)#\åàëq°f$-<íw¼µ‚e± /s¦ÙäXñ.rczÞGºDµOäÅÝ5Ø™ßà¥É Ýïc:¾=vz8Êa軞¤XP˜ü`n2­œû¾X8›Ùûçhví8έ­Þ) ŸÆ)@¿úX \åQÕEÃ;·i¹ä ÝÇCÕ4€TuÕeÛ}oÀ™h“Èñš¢ï–³ÒýárŸE¶ +3Ò2e¬1ð‡øÚï$vJ)’"!+ÓN]Ñ÷•)k$ÉMªº&ÍS|'°Ù’º¶™ßar@^æÌ² IVd˜º") òÒ%öŽsÂi‘òâÁóÇtÔI‘ {>ÚgèíÂÔéZžøC&é¡]`šÝÀ7kÍzOŠýý}”R¸®‹çyø¾¥˜<ÏëÍ£žfѧq ÐwB c‹[eö͕1– £(­¤¬Ue˜FV×÷‡üwå¿G …Ód«4k²*£¨ ~ë%ÞÿŸ|I‘0p­¬ÎUod3[¿OV¦ìÎoðìo<Ç?ýéŸÅ4¼r››ºF`µÔÿÙ_ø³¼ãï ª*ûÞLNiJ”P–û‚¼ÊlÑ0]àj›5 !l¡°ÉÂç錽å‡é>I‘4Å@ Ô ¤2†¼‘zÚ§*­zeÑR3IY B7xMÀœ–¹×À^”ŽvîšÞx¥BàËcggÇqð}Ÿ(ЍªªËœ[êãTÙq§ý*éVC\˜¼ã ëºfY.)LÞé¢ó2'rcÒ2e–Í#B'BIÉ/ìWîø:‹ìIžúÀ»˜¦6z›ôœ}g@䯖ã’y6#¯2®_½Î¿ùøÿsÇã½ï¾·½ímìg7ÑZÓóûÔmÁ<›QÕ7æ×k £•Ù !ÈÊ”ÃtŸY:¥4%ŽrXäs««n®c¹k¤¨ †ÁˆE>ç0Ý'ДC'ê2»׃åMFái‘rcv•¨ÑO’¤”¯È«ìÃåÁ1öH¯±;ß¶ê“ÀúnbwwÇqˆ¢ˆ²´MBmA±-.ÞmV~§q Ð@VeöjÔÆ[Ä«2*Sv|mÒtNÒÃNÆæ*G»„:>ñuªªâpv€§(%¦4ˆZીȋ9\°3¿i蓳Í4åpv€«]ªÒ°H®SŠ‚¤\R‹šÂäÔuMèD8ÊÁW£pLß¹1¡ñâþólÏ®ËåÐó]W Àꟗf’’¹•õ)¡(¥¥y¤”wm²Ôv'î-v­¤Ñ”ÌÒ)®v‰Ý>¸ž]!özÝ{»—ìÍÒ)BˆîšÎó)ej¿%°¾[ÆÎξï“çÍ‚ õ1ª£¥4îwpN‹%Ô=_Oã ÐótFà,ò9¦®¬ïF¹ÄâXPV³|ÚñÒ¾0˧äeާK=Ô–w=q!È2vnn³Õ?‡r5”’HõеC–gL’–E‚l ‹'Åb9çðð×uIÍ’ZÕä%0¢B+‡q°Ö5Ô´¡„"¯2®^&É—Ýñ’"¡çõ(M‰'°C“£šì8¯ ÆAŸÒ”–ÎiÌŒ1T¦:±!%-R®O¯PšSWVfܘ_ÃÔCLV%Ó^»³ÉÊdàYæ d³˜Þ)³n³çžßg¹G^6RG!ˆ½žÝ%h§óúã¯yÿ«Rûûû­h­ Ã<Ï)Ëò˜ ò~è–š2u…©+ª¢:í"=9>y ЫdÏÕ­‡')—<­®-È•¦ì¬i6é¤c±×ÐNp¬Íúoâ,!›Ìê=ÙgàŒ(²7ö8XÜä¥Ã©±]y«<ÈËù’ýý} rjmp<‡J–(O±½¼ÆÅÑ%zþ-0í.šrØ[lw غó $n¨Ä×!ªéœe\å°, ý1RÊcÕ¬L_1‹n)Œ¢iþ)MÉ$;°†TBq˜p&Þèºæu|?ìº8eãrôÚ´6°·‹ž7°] Í"àjk¹»ý†^Ùç098ÖÛÓë ý1ZÝÝ­5›ÙNO×u‰ãø8ÕD߯q4AÉIçiÜ9„WîÓS{úžteª.{Â6‹TuÅ<›‘”K*cº,¬®k|íw»¶h×¶T·ãI”ÄÁÞ!*× Õ²§¨ Át1e'½FU–Ô²2îÈ{îïqùÚKHGPê\ÃÙõsd&¥çö ¨+0ŽÃuúÞ€¾{‹ºG¬HKS2×q¥Ë,Ÿ…°ÿ×fÏB(<åYËR“ëþm¯i ˜Gyå¶æh¶Üv'UÁùþ…®ÅÝî^f»ßÍ厥TjÍArGºŸ>K­Çõí¶Ôíyæ …eŸ>ó|ÊX¯Ûspû_C8Ú¹k@MÓ¥Y–uàÜ6­Ü UòÍíBhêše± ®ëûÆ4ë4î*.Ü€Þ_îÙ"X:Y•‘ Ûq§CL=#)›ìSXݳ§}¦Ù„Ð qÛ6g©ñTpò œ,& FzL™T$‹34ìÞ`¯Ø%V¬ÐÉ"aÿæ¥*PžÀëyLý)ë£5¢0&- †q´Öqì;óD^Ä<ŸS6ÊŒe± rcU #Ì¢˜uM9-˜G/3rª1ÇtØÍþr¯³/-MI’/™g3Óý.[nwm—¡§<¶zçê$¯2|â(§Yí÷7»ì,¶»ám ¶é&/s\ír&Þì¸êd¾$öz&%‹|n9vé¾bV=K§,²E—U‡Îj¼zUUÇ@¹ªª•é‘û!B7:vÏU–ù)TrЯᗥÆ4£tm‘L“9§eÚT[$œ6ý¡„õëuMQd'¾^YÌ&3Î’¼W@!X&Kf2¯gä¤-­­iu2§=›Í¸¾{•R—„½€XÆœn jEm@(Ù§nΣƺÑÕ ð–¦d«w¾Ë6=eõÛ•1Çé•PV—Ý4ë¸Úmxë…Õ|çe÷oJjv&_¥¨ ®N_:ð¡uþ×½F‹Ýf\­©TÛp¹1/¼€Á°·ØíÞËõÙÕ®HÛº^›^a=ÜÀÕ.7¦×:󦺮»÷èÓlb‡*4þ%¯”Uƒµ‘Hʪ\  _‰Îx#µz·÷‘iú Z7ÄÓ8車e¾è2çÒ”¤ ¯\TRJʲ¤4%ól†£’Â)©‰›¬-özÄn¿ÑC¯Ð\ ÈAV ǸÔÜ8¼Fâ,¨œ‚™™¡*i‹s+dг›„3'PHGàd×'WY­uÔFkCÚóÌó©m‚i¬G[¬›Æ’IzØæíh6\ívmåU]±ÈçxÚÇÅå09`–N¹yP=íãJÓ,¾ö-eÑHù€nQTBuЇ/ìü._¾ùûxÊc®óû»ÿ­œÎú´Íœ#7æáñ£\Ÿ]ífÎD›ì/÷‡ë8Êaw¾ƒ’’÷\ú#¦û ý1ÓlòŠYõ2Ÿw<ê*õ€£Ùrûý̓C Ebny®´&a¾³uŠT§}wÑúZ%BUÚêsà,÷;¿çvdUR$]¡-p"|ukYÃkN@R$<4~Ä6ÚT¹É9Û;‹þ8’IkÙ|~œ¶d¯-8·uÞÔuNúu]ðž´å—EÇ/·“®[ÓyÓ¸Ä)¡ØOnRV…Í@«%5J(|í3ðÇxÚ§6Ö/ºí«DEI‰ÐPÇm\î8ŽöFxžÇÒÌ©u[ ókŸ¾?@Ö¶øx3ÙCHp]å(F½5v‹JUPÖ9nà#EÆø^À´˜à…±•  I¹àâàR·ˆ´Š–/œe3 úùœE>gŒcþœž›~t»–^¡¬ ¾¼ÿÅN3tíïö_ëØ[¨¥‚^š¼À4’–Ë®33-ÓŽNr•×ù£¤Åißrmr™¼Êðú¡®kÅ‚7¯?n]÷š! ‡ÉY™Z°n¦ÙlÏ®£Ú…¥™¹=»Îî|‡3ñg{çíbRÝ=qwgû¸6¹ÜílÚ?GáÚéÅ9¥8V‹E6ï2_-5žö1Eb3ê2E+“Û"Yk T˜¼ó©ÈNénÀà趸¢$­’zö£áJÐs‡lβ±¶‰rY•¢]IIaU!nD¯1Ò÷2/EkxUÀ´š’$ ÛÙ6Æ«‡–;7KƒFUHG€””H)ñ´Ï<ŸÒóû¤UÒé_Î;$7éyƒîü[Z¡Yf Ù‚a`‡ÉÞ*ÎRÃî|‡½ùN×…9ôÇÄtî09è:Ó:EKÍW¾ÄïïþL]3äeÎÎb›ØùŸí´Ù[½óì/÷¸0¸DiJö“›ôý!‹bÁþâf§âX öæ;lõÎv´Ç4›ØŒr¸zø’¥h”Gi C¿Ï$™ ¥d‘Ù–øÐ‰øòîï³;ÛæÑõ·8¾©L‰ü”=ßËùˆGã^š6ùŽŒ*›¸,²yçšxÀ IåUt+s•g}šË”´HlE£ph+ÿu]Q8xˆ#`ܺÂi©Á@]Õ8ŽCNJ/Ž;9×ø„*"vzlö7 Fd"Á­5F´£(ëWXÙ^äEV¶§jRÊ ÂÍ5ñ GVgÔªfo¾C•—Ä:fÂ> @ù’Ê-q‡À (k›õ÷¼Y™v2¨¶h·,8ÊÁÓ~·0q7¡UE-‘vA*SÚƒÚg?ݧ¬ ¶çטeS*S²ÞÛd¬Ñ÷û ý1ËrIäDÝÛó«<¿ÿE–û8Êaž2Ix`ô&vç;\9ø*o| Ë}[€T×&—Ùž]ïÁe±`«w–E>ïtâ0ŽÖˆ›Ë=|éâWæ_åm[O0YvTŽ']FÁ»ó\=Lar=ÜŠ’šç®}ŠGƳŸ#pC\¥©1÷(¿ÒkÜ °îûC&ÉA—ÜÌõì Ç}ߤòš(ÝÐ-'š•û–â09•1ɾ£ÛÞ½˜ZÏ妳®ï ìôº¦2†ZÔ Áq]‚0äL½ëx¬÷×¥ÄÁ¥ïÙìÅ÷}ö²%Êh*™ZKJU3 Çôƒžãá£ì@×Ðé»1ƒhD†Dqˆ3±àõÅUÎöÏ¡E*— Ãý-B/²>Õ~¿óöÈ«ŒžßG@dzï/÷úcæùOyäeÎzt†žÛÇ`PR“7Ç4=´ —ª`o±Í4™à»!¯=Öuù ý1¡:U#×;Êók©™fnÌ®²,ÝŽa‘Ïyñày|íscv•¯¼€©+Å_T”ìÌoðÐø “ߢO¿î¬ÊØmsiøÑ÷&7mg¦òX6é¹1¦®8׿€l†æ:ʣƪA<í#‘,ŠYÇaoÄÔâ8¿V…È+)S^ëX®ÊØn{‹íîß’rÉ4›0(F§\ôý^@ÛîÁˆ²*¬÷s™w™e;ÚÉÔ¦±Ô4uÝyëOÑNiÕ IÁs}\Ça}pßõéGÊ¢BVi4=gÀ(“×J \ã „Ás]D;RIÊn²·«|‰Y>í®c+ÿ«Œá³WŸc«w–q¸N’/ñµÏVï<}oÀ‹ÏÛ 0¾KäÅÝçôÖÍoëÚÑçùœi6A Å[7¿Í¾Ïº²|x°Æfï<¥)é{vÛÖbU»ל—)=o`¢*c-\ÇÕ>‡É=ÏŽîZ%ƒÞoãz¿nYs °·êÛé°_îGÝþÌ€ún@º5Ij'ü´ rשڸ,žt÷ùª8nm»ñ-;M¿3î9Ló.Ó,ºvhÑMV‰ƒ>¢É¼ZuÖïWÕ5B FшЋ¨šÎ2a$ÂØ,:ò"‹)Ú×èJQ׉YÒs]¶{=\å5Uð‚ªªˆUŒÒ’J  )K–ù’Û=)ö‰ü˜Ð©…mÎúcr“óÙp”ƒ©+®L.£¥f-8ƒ¿)ºµ’º›½_»zÏ|æ3UÁ3¿ðL㽑u’CD+O[óþ|÷Ÿdóì&ïzò\›]¥2…¥:šÊv}½åy+Ë|iùèfv¢ïÄnŸµpƒ½Å6o?BèD\Ÿ]µ?S,Ønæˆæx+>ÇùþÜ\ìâ;¡¹1£`i6aŒ8Üžð;¿ñÒƒÏò¹gu¤vÐ*WÚø£ÿñw±¹µÁÓO?Í2Ÿ“ù¿;íÈî…ƒÛIàü‘Øçúç~îçî˜Mÿé?ý§¹xñ"O=õÔ1MöQ nm ^ ¨OéÊï–lM¹ª¦yhÒ4uµYtR$§’»û=^›ŠÃÕn×Ñ&FíÃÙrÒ-½q´Šï4s÷ ¦›žÒÍœ+kþÂþ_’–i7ÖªK'4OÞñÄÛùóþÏ“/3JQV žë#ð ÜÀ ša®Ÿ}îsü/ÿðwÛë¶¡&+SŠ2§¬*žü“߯£o}3;Ëø®Ï0Ñ@ç&Å4¿wTµ±Ì—:´tBÐsz„^ÔMçþÍßø-žùèÇyæžáúµëwu}?þ‹ï¾ÿŽ÷ÿGÝœ›ÜN¬1ew½ó*ãâû68ÿ΋LpÈ<Ÿ:[½óÇæö½å¦µÏ2_r¶w!çâ ì-w؈Î"„À•n3#Qñüï¼À/}ì—ùÔÿýìÞØ]ùþßýooõŸø£|î¹Ïø;¥)ÙoSû[lÝpnô#ù?÷s?w ”OŠù/ÿe÷ý÷|Ï÷ðƒ?øƒ<õÔSǎݺç—õI”GûŒµƒmë;Jjʪè$«ûË=Í.í Ÿ|£œè]tk[ÙÝ€ ·Òª¼*B4ŠY6±~³íw›ìYJ;¯ª+T]#…â™>sÇ×VB5ãªlGbQ”EAß’•K²*nøïŠë×nðñþ«;ïá§.á¾ÝÃÕn' ´[L;¥¥¨³nqIŠQceupqx©“j©ù­g?ËÿøßþÏ=ûÜ=ùp~õ—íÄŸyðÉ‹DªgmO]ð¤ÏÍÅ.žö‰½Ÿßùm6â-+AtzÍDñço~ Gy¼÷Á÷1ŽÎ°¿ÜãáƒcøµOþ{~öïü3>ÿÙ/¼æsø•ÿÊ]Õ5Ú±`«r |GÁ¹æøÃüµ¿ö׸råµÕ“~þ瞟ÿùŸçÛ¿ýÛùñÿq¾ýÛ¿ýØ€Û£S_N¢>Z:ãV]vJÛœâ;›ú,7—;&o ñ%™¶ Uè7z}_ëº~ú5te*;†ªI[õB;šªnÀúhÈFl»ý^7I„º¦0åjšYIºdº˜p3ÙE9Êfn‰ºÓö*©a•"ëW‘Rb¤ÁWižº!Ëb”’e±`Œ(«-I™tec ¾póÆ>?ú7þþÕ Âë>>eV!jI]BYÙÙ‰ZXÚálï<×gWB ¥fžNydí1ò2%rz]èô IDATGcϳM:ÿÍü ~õ—þ¿¯ûy¤É‚ZÓ}.rw\ôËÁùòåË|ß÷}ŸúÔ§îé{üÌg>Ãw}×wñ?ðüí¿ý·­7ø‘©/u]w`ýrïêv1©LÕQBí.³®këÇÒØÎ¶v²•1ŠGAYvøò©éýÊ?_xM=M uA;¾*/s«69Ii‹Ti™9e³-+± th]ßš¸õíh¥z'E^d¼´ó"I•P–¥Íä…@ÔÖD_6žÊí–ðÄmuZZbQ±5<Ç|9Çu]6‚³tìÖWyÓ†;ð‡lÄ[¦dûÚßóÇþÌ]S÷, AºÈJðÒòEFñ˜¾;dgv ±ÕpÏ Z)´Ô\Ÿ]å žb=Ø$öû?´bûú.ßÿ=ö®¨Œ{íÜJ°ÃõrýUƒó{Þóž×œ5ß)~ú§šË—/ó3?ó3V}ÓL oº£ínÒíàä¶ÈÞv¤¶GÛÞíj—a0bžÏ)²–þ°ò2g–O™¦‡Ýïöç[ï Õ´^—¦ldz'ô"]róæMvöv¸±wƒk{W(ó’$M‘µb™-lAªá¬OŠårÉöî6Ùìªìÿú‰§ßöžo8óÙŒ›{{°˜-Ø9Ø&R=z΀ÝÙvcç*úc\åuŠ ß íDs`¾7ç/~Ï_ú†3@XYã4™p˜ÜuÁPÁ‡?üa.]ºôº‚sÏ<ó ïz×»xþùçY,$IB–eEqG몘Ò1ܪáhåtJv‡ZI4Œ1ÌšEùÆi!ħîÓS»øš2èÖ_¸Íž-]uYfeì4ÕhuójŽ#ÝNòvÔ YKmyk¡H«t%ƒýe²à¥k/1+§hO¡}ÛÅzQ÷P8RSVÕ±¢Ë+Åó×¾Ìùá&r¬89×¹JÏï³HçA@I Ø…%) S²îŸaàyñ¥ùŸþæßõ'°ya“­õ-”TÝzc÷ׯ¼:þê—àLE¿7@{Šƒå>®v‰¢È3ó%o?û$×gW‘BðØ™·òÕïðÈÚcÄnÌÍí¾ÿOü§w ÎñF€ëjòÌHà»L÷–dÉ«ó0ΊŒb™qið&†ÁˆÝù6z.9sæÌJà|ùòe¾ÿû¿ÿ®^s<óÐCËÂ_xáVúýëׯó—ÿò_æŸÿóNQø¾ïûÇ@ùå™tQæÌ­nÁòÖðŠòÈðOûlÄ[ì-¶­Ë]Ó´â.I3ˆÙá 9·ð~–×½û5t»Ò+¡pµÛyoäGdcU30¶ÍÒrIÏ`ꊤ\Ò÷8²mP±¼ó²X¬<äuww—…™ƒkFÄ~L’$¸ÏæÌY“Yƒ.ç9Óá˜à M¹‰®„‘¤yj‡˜–9}ßÇmôÇí$ï?óǾ—×o¬tÝ¢±Çù·ã[Þú­ôt‡Æv†ô7&×ø½kŸåülƒí…5:L1EÅÕË×N>øÂ°sõ*×ÄWŸ=ËTpyúNèà‡>¡â*Ïê­€?ìlB+Sò#á¿^œÝž&Üôo YÌʺbìôº†£áÙõ弤Ê!_–dËŒžÿê‰Ç®«Šº–ÇðvpÀ*àüž÷¼geP~ßûÞÇ[Þò‚ 8–ñ¶œr–e|úÓŸæŸøÄ‰ÇûÜç>Ç_ý«•Ÿø‰Ÿ (ŠÛúY· ]™ê-ÓsKÇ)¡ÐÊA˜ã3:]ív—y7RΑîq¸ì}©àx¹IÒ]te¬€>-ʺco–6h§gdUB m‘ÍÚ]ª®:>"­+LŽ’šeÓv¼JÆ›d Wn\! Â^@à‡yI]ÕìÏ÷ƒK@¡Wò 6sP¥_±¸ÏZƒù>#gDa½¿AØH÷fÙ G»øÚçÇþæ¯ÎNèpé[Ï0ÞõÇLÙŸc:6ÙTÁ ;_&™Ì„ ýAw¼ó<ü¦‡¹öÕëüìÏü¯wþ\æ‚äzAR¦ˆâ ņÆQ’‡·cÎ!W§/Ñlô·¬ÚÆ”\\¢þçÿG|ö¹ÏžxÚWl=:¦hÆl¥s›%GhUUÔ@àù˜ÜP Å¥GÎó-oy g7.ð¿ÿÓÁg>ù™;U× 4VšQÙR?ò#?²­ñÄOðÇÿøÇu]²,ãðð°[(¾Öš÷¾÷½|Çw|?õS?Åþþþû‰O|‚}ìc¼ÿýïÿšãu€`û yÓÑÙ&3m‘½ý™v:<À¢x¡e~«ö£ßYôýšA?}[ZyÕßž¥Ón¢sûÕ>L-PgUBÚ´{·ôGÛg§V›N^'…¢0yäåjE½¢ÀLsT"X,,—Ën~] Cʪ 6vû¸ àË“(‹UQÙÖñ¦9F Å2ŸÛù†Ë›Dò›¿ñ[üÓô³'Û<~ò<ëë¹d7tåá—²,ÉòŒ¯ì‘å|J^d˜Úài×êÅ‘ˆæëDzž2;È(Áâ°¤/Ô%„:ä‰sï&rì@‡d³ûñÙ½¾Ë¿ø'>œ̓o?GàÇDnŒ’´Ê¨áV=B(<å¢}¤¶_Ui@ÔÇ ±^ñu”êÚÖûÞ S—œúЇVÒ7¿ýío绿û»1Æ0™L˜Ïç$IBžçTÕ­©í­ #Ïs\×å¯ÿõ¿ÎÚÚÉNr?ú£?ÊW¾ò¦Ó)³ÙŒåryŒ“ˆfÌ;nA9/ó¼ëºF·Óî›…ª¥[ú#«û¼½ñ¸èOÞǃb?øšúå©§}’@‡ÖŸ¢Ñ>—d.¯2åt­Én>vV¡Í´pºŸ_Å¡.a9)¨f"5dyJY–V¿ÜlS¥P+û,HU*)¾(‹Š¼(˜¥S$-mQÓj»-­óc+òÎo~òb§‡®+'}ú yŽ¿’Î÷Ê•+üäOþäÉçð¶·ñ| æ4µ÷ €ã8ø¾O†ÄqL¿ß'ŽcÂ0DkM¿ßçoý­¿Åúúɪ’ðþ‡‡‡Ç^ç…ÒÈQ1d®0¹¥Vškiw«ö™h¥PÝ5/ª‚y>_©¨þ:e¯ßÛ|]ö ?Ýüý‡›ÿ¿ü:¿þýHo<Ím$vwEq­TÕI±°mË&§¬ ŠÆÅ®xš5™µïÞ³åpe£ÉÝ[ì’Uó|Ž#NΘLUS<…ªjº“. !ðµjL‡ÌÊ Ýd«µ nŽUTI }Œ1h-QRòì§Ÿ[‰xø‰KÖn3ä¥Áu5^q¦¿A†(­¨–R ÄzD„ŠÌ¯Ø\à|tÏÁ OÞæ»ÊÃwC´Œ6Ï0Öpúš‡Fðé_E8‚G·g–O¹6»Ê›×ã³Ïþ6ÿö—þ݉Ç^`„ª<‘µ`‘Îpºª(sjUô%FçèPâé‘V ÒÔý ‚µ½8^  •R„MKûªñ“?ù“'RƒÁ€÷¾÷½L§Sò<ïôÈŽãàyAàû>žçáºn'•³E;k;ÛöøsÿÅŸã'þÎOÜñµ~ý×çž{Žw¼ãÖæˆäNí½5Ô¢M(Úì½ã¦›kз­©Ø,¿uULË´£9(y½¹è¿|èvÙk£¨hUjÎïÝMFøAàât€nøß¡.4çtÛóIàSBˆ×kñùà+î,W=B{óØI(yYw\YQ8R“6YI ŽmÖì6è–Wl¥u•)­ÙR¹D;«—¯ŒÁ¤¯O—‘´EÇy>Ç+ýc>'í ªTZvË"ŸŠ3¨nâ¸lßÖü“Ÿ>™Úp|‡µÑˆliùÚZÖø½€³ãs¬ÏÇ1ÏO¾ˆŒuQRé #3ÞtéQ"7Æ÷}Þ|î16‡[ÜðvN|½@‡PׄQµàù½/òHüh7~KÅv~—Æ‚Ü\îñk¿òë'¦VŒ6Gd û™‡=”#ˆðÁ5ÔJ"‚ ‚qAº'Ðh"Ö¢5Öë ú}üÐÇwVk¬ð´W–šúЇNü™'Ÿ|)%išbŒA)…ëºA@ÇÄqLEø¾ëº8ŽÓiVdT¦Äs]¾ó¼—?óq>ÿùÏßñõ>úÑòðÃã8α&¥·& •µÿ$_vÔÎÑ Zqë™Cv‡ÛÿÏ«Œ¤\â–.Žë¾^À|øÃwK+íiÀú‡ïB«.÷šÞ¨ëú‡z…ìõÝÜRUüPóóŸj‰Üc°~í}´¥¶íljo¢Ò”¦D AZ¦˜¦˜d^ÖöÚZjºÊc{vÒ”,›L<—Å*´Y-€Ò޲Æ?¸ÿˆ S½§-(%: /ÊœEn™Qg¥”ƒïxlOøåg~ùÄc>ô– T™Í|<ÏAºEçÏ\d4F!^­Ùèo ry³µÁæÚ&±svx3½uëèç'gÐm›zèR™’h“E6ãÙ«Ÿ¤ç ÐR²,³Æ5ÏÇ`ø?þñÿyâq7/­è ùܓɂ×)uN¦233µ.¢ÂˆŠY>%Ž{ŒF#6Ö·ˆ¢Ùd¤'-–'eG}/Vg€·¾õ­$Ir,sÃ~¿Ï`0 ×ëÑëõÃß÷ÑZ“W–'®ªŠ¢(º ÿÿ«¿ÂüÅ¿tÇ×{öÙg¹|ù2J©.KoYŒ¬:›v×gdÕÑGÏS Õ®vɪ¤«Û´TbÛð: _8¿Xo]× ûáWù^>tù"ðë¯D+Ü!ZÐþ» Xÿ½×êª×,^3@Mªu£óIŠE§à°yÛ¡{d¢uÛàQš’´HX–Ënþà*–”Ý¢P×Ðp–m!MIm§€HM ¬BpÔØ‡VȪöÖÉÍW^ÒZj>ö Ϭ”=#²¬è°þxÈúè [g¶FT{ˆŒ)Ñ®äMÞCl ¶8?¸È"ŸóøÙ·x¡¬”)¡cÊ<#Ó–.:çžãær—±7âÍëó…›¿ÃCko&t"~ñÿúØÉÇt4ã3·<1´£ŸÑïõŽF¼åÒ[9¬oâ©Høêìy¤vÒÎ=tÉææ&ÛÛÛ'RkkkǨÇqŽÄÔ†²´“ƒŽ>[B<íwÒÓêH]Æ4-ß­µB[ãi ë6¹(î%ÍqOÀùeŸß•&£þЫê¹Wïå5€óíÀúÃu]_i蟿÷*ßËÝégVè£^µÔt VÃé*¢q[3M wë5Ђgë{¬¥îªÖólÖüJEÂÖïäv8¬v;»S%5ób¯}êU<‘”U…£eóó­i~àÚ¥)é«ÿÅ“}6Ö7Æ”¹=Žëjt À5<´õˆÕl÷bnÌ_bc}]JDQsnx7­=Ì™hWûÚ§ ¬ýè %ÜÒ” ƒÞ&ê&ñçûÊsgÎóÈúã\ZˆK£‡È«ŒÏü›gOG6-¦©˜g/›ëvûìSP•†A¼Ž–¶8ðFTµl{="'Â`ø­gëÄã9»F Möl œ_€ó› V»Ý pC—aȃñø¹·ðèÖãl·ð}ÇsÊúb£ÑJ×coÂáb!/ì€ÇÖßÂÅჼóÂS¬Q—)ÿn#¤ÞøV=`0ЉûvXïCçá©GßÃp8ä­~+ïxÓ“Œ#ú½o¾™a<¤”9‘rnü¡2‡°‚&}Ýz«š F£c¼s›=÷ûý¤ûý>QuÊ ®Äó]‚ÐÇ ½ý¸ã9<ñÎwœœ~^¾Ìr¹d>ŸwrÐ,ˬܮP‹Nšº,—"£¥=Úëaê#-ã˜ccä’rÙYÏæeÞeÑ÷¢ øõh¥B|Jñ‡€þä+dñOßccþßcp¾Pòv®t·Y,>¼ /¿@—UÑéŠ•ÔøN€ïv^Ÿß·Í(R7FHe£k¶íÍŸU =o@èDÝ.áã¿pröìz.®ëßêS5½Þ€GÏ=N¯×#Š#¦õqÓ‹m1pÜ[ãâøAP‚Z"/ z ¡¬‹n»{GZÅqR!´ê&yÃu=ÌSØîº‹ƒK<8~˜ßùÕß[éºô·ºöjpjÆÃ1Î\ì.Žc6Æ›¼ã⓸®ËÅUÇ¡ è‡}¤²s"­Ÿ•x­󱿷¦ûw¬<ôP§oVJáyQÑëõºsèõz¶PÅH-í~RÕÄ~ŒPß p]©%ÚÕø^ÀŸúîï^é=?÷ÜsÌçs–Ë%Ëå’$I¨ÊŠÚØvoÛ”R£>¹µ:8:Àc'y½îÒR“6Ô`;ä·jì~ïµñ!¾Žq¨/rKÂ÷´â{¹P4\§ônà×_ ¨ëº¾X×õ'W-š®Dq´[-«Ú¸e߮ު±­›-wKq ˆ·Ô†–šI~HZ$¦³óU(+‰knbÙ.UWŒt„C’/W>@º¶yCyˆæÏ¶»«Æ*;žû“ý{ÃøˆÒ¥Æ‰$ç×γµ~–^¯‡ö®r(³‚q´f3̺fY.XeË0£…ÓU÷ïX( <\’ý%o{ø|‘Ð[£ª jQ¡ý½ˆ«W¯žLÓ„a#‘ÔÇ´ÎQuê0 ;ŽØÔ†¼Hq´‹¡Âs¬´0P!•²Åº®qj‡³gÏrýú½SŽs–e$i‚ôDCÕ 5U}ÜÉN‹[ºñÖé1t"öû¤HŽ©”ʆJ¬êª£ _ý¡oT#H󺯗ŒîD®÷uê+Gvïv‘X  ÛHJ‰jfvZÎææÐRпü¡l97Gºì{L³ i•4?oî*ë-Š ­Ü®yÄ4&ç¡¢”îŠ,+efÆ>¢™e¸Ÿ³XºÏúà J(n\;¹­;ŽãŽÞо ŽõÇ((_ Æ—>yñ`ü0±ÛÉàR³®wÞ&E™“‘®HÓ(´RäEÆ›Ï<ÎfÿöÓH|Ç_‰83Ú Œ"´ÖÄqÄTraýîC5âc=YÁ!µ¤¨2\åáõ5QpqíÒ-^Ó¤Õ!jªÊÊ}×-(Li3S’׳lÂfï<Î zî¼Ìñ£!ßú¦wŸH|úÓŸ>¹Ð¹¾Îfÿ,žç1 X__gk}«;V6Ö6aDNŒ”’ó+8ÊálÿÛ³mÞ²µÉ(ûœ¿žaŒ¡¬JBâºî±‚]»Àtà¬eUvEaSÛ‚ Üc)ˆŠ !Á«Ý«eU’ç9iš’ç9Y–‘ç9¾k--”¥(Dmw£&ÇÔ>¥ϵjŽ£c²JSv]²u]7TGÚyÝ÷ÖD–WY,üÈ­ú›!VzrTÓUM¶Ü¶fgej[º¥KÝ€³9Â=·@¬šnª¼ÊˆÜ˜¢‘ -òù]eW–â° *kÁ¢á°[~Û¾§å±Êø [”£ºL¥4%ó|Jiʦ+Q¯ä#à¸%5µ¨©DÅæðkƒõ r¢ "òc\홪=EAR% g¢Mí2I÷WÊ }ßG+Å4›œÈÝ®b(ÔªZj ¥Úó8Úm×òý¡q®ÿ—øCÎÎß ÷„⸛ì èÙñPi™€!…URg­uw®ãRc¨1vByÓ‚Ýî <Ç£*J>ð§>pâë߸q£æö«( „‘TUÕ Ò=Úñšöón%¬YÓ-8Ë&‡ÇQõT[7©ê#n|B½ÚK|¿¶Qð˜=}ºÍR[ÿ…ÂØ tKu´:èV£i)sìûöA=H÷IŠ…5Ujô¥«zqØü½FXµHäÆö½IMÑZy7Û½ª¶úÒ¦H“ ±×kZiS^z饕÷ã&“\‰Ò’’¢ƒÐéûC¤”]ÑGAÏtÖÂäL’«qmZÎOŠ­ñY¿ðm¼ùÌãw·U9ùK—.Y*«¡njGÁ¹=Q8ælß>=·OèD¸ÊcŒî)}ùòj \^àuçaD…РÝ5¼¼ »2ðºQlXõÂf¥-—.…êv‘wŠd™°HæEÑeÏEÑh«« …¤j²õY>팧Ò"Á4ݱ–Ò³ YíßÛùŸísØ&Pm]¨ª«ÎÖô.)¤û•Þøà7ó›¿ë"a×Z­¬ —%e]!„ìàØY:í@½åÍé’ ŽÔdMKµ©+r“[Ø ZÙ¶H¨ÔǾ*S‚Q¸Hû~Wl|AYðhå(‡ƒä&CoH GüÚg>±ê5=‡~8´*þZlQ‘“"KI¨ãnÆ\ßëï;×-¯.]fÙ„Øíß³ún&ZÍ ]×í¸Ú£”ÀË'Z‡nÄØþç;UÛ€ŠøÜØI¶ ±Œ@Õh­©°TÅËÏÁÞãmk; Žt›ûÛfÑEa'õ¨&QYí=$Eqì ¢ä•å¶¡¶o:=éw…¿6+.ª‚Iv@Ò€wäÆÇhÅvêH[ðVò®³èûÙÿ› W–Ùu[_åt7u;=¥ÖÚÌôÕŒ³«un[T S2I th½.š›}-t ºR „RÔ 7~Ô`§j2y¹ÂCdŒÁkx`Ñ,mżhÚ×Wõõh'™çyï‡ôÂ~Gõc°Ù=fàŒ¢5Æá:^C ¤E²X •Càߘ›¢É<‚ôÑìù•2â ƒKô½=·Oìö»srõ½rºê.À÷l¡Ò6†äÔÒ*:5¦6_³ÀTÆ€YÍ~MVe]¼Õð× çXí}cX$sÛ9XU”å-ÏÕ:Û5àêj›ÀílþÛŽ\ÝÛåõcüöª@ÖÕÎW.l9íÆ}.özT¢lÛ¬ Fm@›=†NDÏ‹‰Ýx¥ŽÄ£4Žv®ÖlöÎRÖeСɤåKS²,—ܘ]íviãR8[ñ´ÒßP€nA¹êÛeÎGgàé¹}®M¯t×Ò¹¨R4eµ}UT뢔þžV yQÖi™àû·xr%±Û§0ù-î_ò"kîoëc®”ZiñÛD5®Æ8w ­›©+Âv³.Šý2'özèÆ²ÀÔ•õˆÁ4Åxa§›¼sŒ<*im3hùê8èûrJÉ7{ö¼2@¿\f'¥$Ïl†á;uaÁÉÓ¾•Ûe³.3hÍøûÞ bi3D©IÊ¥•‰I½’‡Ý. „´r¶e±`šMðj‡Yþÿ³÷æÑ¶\w}çg×Þ»æ3Þù -k°e#;Çǘ1 q Ü Át0C;MVzAgXÎD7¦;iBH›•Nl0`° !°lchÀ6–eKzzýïŽg¨¹vÿ±«êž÷4Üó¤'$•Ö]zz:÷T:»~û÷ûþ¾¿ïwŠÈB_ï6-ò2gÓÝ¢ï ˆt¼ÊPÎØò¢Õr]{ßõqt… -F¹=¿ÈÁ]W5W»AØ ô@V§DnLš§ôƒ>£⸎YÄSÁ#»ën3ÿ«1ÛÇÅáýu]sªéÉœ¥t¯åX¶¿PÕ%Ú*¯Á¡ÍžÜ~Î+²hS‘5 F BÓƒNǹÅ} féµjŒ!+2f錪ªºÁ™vÃ1ÆPS]a¥ÖV¥­ýU u8ÍÜ€#$Iyˆ/ƒnjá‚Iì IDAT×Y Û9Oa¬þYÌÞxås"@/–l5Ös0¯2ò2¿B\¼Í ;5.šÉCåw\é6ÓL‹ÄÒã꒲ΗT±šÒ¢Ñ•¶Ùº&-ç˜zYâ£Õu Æte¡ãØŒ%Їéïw×»,­µÇJ´ÆF“ÝùÎÜrrÛ@ ¥îJØA0ê¨vch+å¨ëœ¯¥IØáÇ Î‹íÉPb¿×È”×u±^KóK8Vø¨2¥uï)§ Ö˜äGxµË40ˆ†W®‰¦úKʹÅå9í¸Ö H¨¥iƒY‘uREY9Y‘aœãï£U¨3ÆtPU—Ô¢ê6¶ï£¥¶`*ì6š¢‹:'r¬¸UWׂC?›ñç3Þ?ÀR+M5»tû@jǽŽÉ4¯i3èEl­ªíÃÑ.¾îhí®¬–¼&º©삪¬ƒ´ô:¦IµD–c•윮CÞÚ …*d=ÚbŒ®èê?Ù1Ì@Æá˜A0b­vîòl›Ë3;Å·ÞÛäÔàŒQ_ÈvZýìðFV¢Uz^ÿºèkÍž;ÆÂB°~²ìù‰²¶§Ð¬zÂÏPÕI>_z¤¹ ^®²µç÷qš¾Hûÿ®þ,­ãO»ywT·Ž§l:,z©„¦1‘Í‹ÌB%͈wÚ8Ä·ÏI ƒÎ„¹MjtÃnj]‰Ú$Â:üdó©Í mß$*MÂGž‘ùÙ€?/ ¯nu~E™§EÖ¿¶ ¿Í!FûL祲èkÜh!¯ë5´ã²ðÆS©^ž?®ëqï³áCœ¸Úªºê ŽV^4P!žò»ÀÓÁñ¨ìn{”’–óN3£Í¬Û¿¨¬xе<¬UaßG7º N#vîIÒM£g¹ÁxߨS–ƒjŒ!о ÑÒcskóÄ÷:ÜŸXÖƒÐV¤Gh.N.°;Ûaw¶c³¡òJ Ô(\Á×>±ß{Fƒ´1†Ó§O/½1,b×ÿ½²é_Tu½Ô÷‘eYËÀh˜9ÊQäUÆ4?"-ÓƒÇn´UÖ9–´0\[ê¦A~þüùϯ=m9ÏBVÉ#ÚeUP.h@·BbY™v*-ÄÑfÑm•çJWzd­Ó|#Á°h\q-dzx@…çD€–Ž| Ä¡šÁÝH!Z×ËI^ÄVó*#ÒQGúo=þ3-5YymÜͪ2H“ íT¢hvk´yò‡w0u}…C²’ºÃÆÓ"ažO¹g àóœg5^殮 ­1†évgº±³÷Þˆƒn…«òÆ9-S»A˜Šßûèɦq?î¤VÛ§¢¤nt!Ž–réë ›l!ŽcÈÎíF½ó*k&gí@‹ã8]S¹¾ÂTϒ㹃A/”Vº]<-–ç6â2I‘tÇ•eC]jùÆí‚\(òÔµÓÊDq¬+…!˜d‡Ý‚K|´šST]vRÔ9i‘\ap›•éRºÍ¢… ¼Îs0ßcgr‰É¥Î ÷ñ‚æÕZÁ¹sçø‘ù‘ëe -ãíím¤”WRÁ6še®ñs\-è´°Au?ÙeV̸ûž»Oø;»¶š*Sò2#ÉçLÒ#êºæ(=`–O™æGWdÑ‹4»æj3çvÓ?îüR×Ýï÷¡k*ZU¼vؤ2U3}+Ž3è†nº8ØâÖe]’UI·Öí0]¯§eq´8õ5`Ð<ÇŸºËZiÑæ¿=åêˆØëuži‘w™b^eÝš,ƪÐ9BÒsûøÊ¿æ Àh‰Ó4m! •OìZeµeD†À²Oœ«X-'µågk©ù‚—¿t©÷ûÔ'>EèE8ÂÁs|’,¡j^Šìq‡q®n³|Æ'>ýq^ýêW_w<{˜ãâÅ‹œÕæ68_M/sÎë‘qûÚ·¬1Tu¹TÌLa¿GI Bi3êVâóñ6œ–f×*õc×-uô—éWN†7\ç»i™0âë ãb»ÊÅmÈ­ÐWÙdÇíZl3èö§½f€¤¡“¶ë³eq”uy­,Žgeƒ°Ñ~îèâh)líê.‹¶õñ$!Í*è,?ùxD1k:ëžrŸ’ WY—>AżL)Îè²G@3èöÏeUpì3Í&Üñ’²±¹qâ{=ð©ðe@Z&„:ÂT†GÎQ–¶{Ÿ7ý'¾ÏVSäMßþÝO Þx¼÷^ @_ø…_¸Ô{~ö³Ÿm ¤êŠŸÎ~é2é_ø…_à=ïyωçLçs.Ïv—ÍQ”V‚µ(Sæù”›^tãRßÇÎùËÝ´k+R4M§v:t®ëÚr‡Åñ”`eªÆ|¸¦j´—˺äc9ÞXY[A*„AJi¡ a5Ë]åÚ™Ž­¬Œ1NjisEmˆœF$mô¢=éuk¿{ê뢽ý,8î}Nè«yÐ]¦Ð÷C!…• uD¨£.€ªŽs+>¹1RHþ°Ó‚.—Øw„ÄQVZKMÏëê¨ÓiŽK|c˜L’ÊhiU{Wô½uZÁák¾ök–º¡ÿáßÿ½Ù>ŽpŒdgºÍ<Ÿó¹½Ÿ*ÈËŒI~Äwþß¹Æy-Yl‹)ÿ•%íš~ÿ÷ߊ 5¹,ËNGbñúO ΟüÌ'yãßø´?GÛ|ÞKv™d¦Ù„W¿þU'þÞÃ>Œ‚²,™g3æÖͧ®[³VÃ^²Ë¼˜!»NÓ"Ái ŒhÌŽ}!yôÑG—Ò_Y³^ˆZY#ÚЋºiÆ kÙÖJtzÎ ý‘k%i[^~]×ÔÆè€@ø*¤¬Ë®IØ>‹mÿåùã9q<^“pq§nˆó*k4xÓ+2h€Izt…™lÛ}v¥¦ª—wTq‡º4Håv’¦i™b¨ééεLà Aº<ã)¿ËrjS±=»DRΙ3¾å;¾y©·|ðÁyç¿ýyâ(b^Íì‚ôèë7 ozB|ö³Ž»_øÞûËï»f|ùɲòEVÀ+^ñŠßóܹsìììtAº(ŠNKb1“>˜ïw¿3ÏgäeÆþ|—ýdûø¯í—_ó‚œd‡ifeÊn²CQY†ÎaºÇk¾òKN|¯K/‘&–­ƒ‹'öY6âMÛÅ¡®ª®áö®w½‹×¼ú5üÕoÿ«1B½ãî;¸÷µ'g»Ë6áZÂïÿþï_‡þíßþm‚ °2WaÏ—§;–¥S—¶iÕ`ÿç½ÀW¿îk¸xáâ5-Ä4ͯ΋Xô…£s„nÈ^²Ë¬˜0É9Jøëÿó÷ø¾ç9Ïìøž ò¨Yžcjk:ÜÞÃ$ŸwãÓZzø ÁñýØ?Y*{Þ8»aŸ×ú )p]—~Ø·ªzuJM§| –âé)ßöBvT ·åe~ì”â8¸Ôh«Q½¨³˜é?<!޶™ÒfЋý@…œîíà…QÔ+ÄÏ+K9ªê’ÈëÙ’o‰Kq„@IÙéA·R‹­Î4ÀÐvö\'f•Æf•ec —Òk= ýq§f§¥f´6â¾åë—¾¹»—wùÿèÿâë_õü½·þ~ðÍ?Äw|ûwòêW¿×uùÖoýV>ú‘>îïþ¿÷Và}É}éè~è·Ÿ0{^Üßüæ7/uý?ÿó?ÏoüÆo®ëvïqîà!fù”yc9–•)³|ÊO¼ýçž;_ö˜J`}cýšª´«í»Ò"!)&Ù!{ÉniÝz×-¼öË^{â{þú¯|€Ï~òs^`e ÊÜ®7#HСލ*Ë'n5¸]ÇEI’Ÿù©ŸåWßû«'žgíôšõ—£ZkzQÏó@Y{²–ÑQ»n=åwœëV#¤¥¢¶:"Ƙ.»Òëù¢£J:Ï?'3hqµš™ã@óoá8¸Ú#pC"¯G #[¦I¯ñö“Rv%¯<éÑ÷xÒC;ª3€}rhÃA{7 ˆÜ˜Ð £†µa5ALy,¥'l6Øsû ;ªöºC/"pC\éQ‹ŠÂ؇â»þúwpë×®™ñ_~ó¿ð›øO¼ï½ïã#ùÈ“¾ö¿ûèõûÔe½T@{dÿsì&;(©ùعó±s~Lp^ Ðßó=ß³ôu¿ãïàÍßûf>þñ“•Gé!óƒ.“ÓRó›ïÿ-¾õ/ÿäïþoùý/ýò×pÇÝ·/ÁHiUä±ÔY6EÁavHß2ð‡¬Åë¸Ên?ðÖïcumíÄ÷ÿ¹wüß<ôÉsÄAȵfJHüf*¶M*LÓ›hé—?ýS?ËÛþ×pâûGƒÓgNÙ{- 2ìОÆ÷C†ñ­5µ¨pµ×q¢Ûõ§µ‡V.AÓ`×R#°ô¹Åd£}nZj«¯<åãÈå/Ž{Ÿÿ}˹z7L‹!WuÙ‘ðÛ#T!³zJ¨BŠFø¥ªKtiø2è/ ÉuÎØQ(©q›©Äe2h¡Gy!=o`¹ØnH |¢Fð\-iöjUødckquƒ–ÓlBÊ­u×ëvò=ë»ø‡oýq÷Ž®ûróm7ñš¯üŠ4'ÍÓ_ŸsT 3&öz¥¬V+üáùñšá뮀mvuêÔ)~ôG”·½ímK]ÓýŸ¸Ÿoû¶ocm}»ïy1Óqoÿã¯ýÇ'ýÝïýá¿Æ?ûñŸ:ñR8Vý­¡eÚiÎ “W6û”F¸¹ƒ§|²<¡?ŽùÞük¼íÇþá‰çø§?þÏøÚÏüe¾á[¾Žñº5{ULUUiºï™Êð±þ?ö·Œ?ø½?Xê½èžÛ¡HG£i7‚ bØ…ag¸›95RKëÊbF´r;q¯ª.ÑÊÅá˜Z7/fh©‘•ýÜBb·O䯸2@: ažÒPÐÙgi\»ï9 •ÔÝT•¹*0·Ž^³O¤#Ò2EI¯Sk b=åã+Ÿ™©¬ÞE£x·Líë©^“8BâË_vôÜí#Õr˜¶l6œ¢.©kËçXÇUÇ ½¡õ±’ê„8è±µµÅ÷ÿÝ7ó/þÎ;8Ú¿~Aº?êóÆïýðp© ºnÐŽËÁ|Èùc<éñÉKçÌê ôÂ>—¦çyáÚ]]òMoz?ó3?ÃÅ‹ËcÅ;ÛÛüÖké×ÿÕø„ïPV'ÃMÚS§1Km2~Ëòñ8Ì÷ˆÜ˜Y9%rcúÞ#ÀwC(_òe_ÂV‚·ýÝ“3Ý÷ýâûyß/¾Ÿ»î¾‹¯þÚ¯âÆn¢ôºÍëÃþþð}×Du¼ó oÃs}мÆõ¢¨Ç 7 Šcâ0&Š"|ßÇQ­+‹ 0R(úÊ'P¡µ¶Êl§Mi¨†õ!›‚7rc7ìèy­í›\ÒÛóª ì̳M°_ñȳE¬j¹ÝàÏ-Æå ¬Ãöã”Sžò)ë’°uÛnð±E?Ãv2«§{D:j¤H—à0ø*²,o@ b¿ÏÀ0ðF¸Rã« ã3?ùûIpl7¼3!hp½–9P‰Â ñ+Eô(Ë’"(xÁ·ò]?òFþõ?þy¦‡Ó§ýE †¾÷‡¿G²¤àpv¸”é@ܳT^eôtÌík/"tcæåœƒƒ=¼Àc7ݦ¤ä3»ÊGú›ÁuÿàŸþ=Þú}‹ƒÝƒë¾°^ñª¿À_úê/e:=ZÊ6Lp,ÌÑö8&Ù!e’’Š %íDž‚¡?Âs|Kÿ£Â9/{ÕK¹÷U÷rßï,—<ÝÿÇ÷sÿßÿ´?çË¿äúÃ:¥ N é÷„aH?î÷zÄQ0ˆð½)ŒSã ¯6•ï ã8 Ð1“å#<›mçU†'½FÛÆ:Ú·ÓOAƒû•<;EûÏñ\ì—Žì²çºáw¾„ ™ª«\[6êˆÐ ÑÊí„ü­ ’AKÝxŠÆ=Ùo2êb|à4 :ÐRi „¸Íû,³:BàºVtF7x n+-£ ÔvÆ×žë3‡ôÂ>ró·ñƒÿ{yù_¼çi} /|Ñ ùÖ7}3Jh&GSb'&tÂ¥a¯×þý1ºqcñ¤ÇK·^Ž1†G'p”òÀî§Ü¿ýq:x#½Ïë¿å/]÷E5 ùªÿá+¸xù"‡‡K5;…‚Øí[q£¢²úÇo`Kz±ZA*_ô‚¡5®é=þæß~+oüÎ7~ÞžW¿þ/²qjá(„* zzQñ`…aH/Ž Ãк»+›áje!=OY ¹õîl³ßºc’ØŸ[I´ØW!=·O ƒŽÕÂzÕµaÐð,zœãY᳸ôT‡l`ŒÖºª)š`x¼(JSá*—²öp\Ûàhñeé8HÇAIÛ½vÕE0Æ,E‹k3øžß'vcÑózøÊgàíbo¦—AãÚWx œSŒÁmh„ÇHÙT.µ[Ñ‹z˜Ò4%If|žŒÛï¼þ·ñéO~zé›Å1/Å˸ûe/Æ•{û»¸¦®ì}ÕKˆHi©q$¬FknØüú:¶¼îÚ!vû¬÷6¸<ÛÁ—ëá)&Ó#fbÊ«_û*N¯žâ_ÿŸÿ–ÉÁäºToú¡o§6G{GHådÉÉëKJ<å6Ú*öÛ›3ú¢ô]=å#Œ…@¤‘PCUÖ¸žæ«¾ö+BŸ_ø~‘ÃÃg䡉{1¯ûê¿„ºLfV Kzš ÷WY­°2Z¡ßï3êéG=ËV¾ð­³8Ç™®§½ns®’)hý i²µ©ìÖ@ímW´¼vdz5@¿›çŠiìUøÎ±-|TM—Ž;xÒ#<b˜£°œèVç Õº­eƒùšjé‡{–Bwº¸mÃÈS¾Å¢•OVeKh©ŒDÚJ¦úÊÇÓ^“™ÛÃàI¿ÉR\”Ð8FRW†õñ:Ôà‹}‡ÇúÖûÛ÷rñámî¿ÿ~&“Écàƒþ0Æ‹|n¾õfî¸ýv„$³„JU””ÄqŒRŠžß'pOÆå³lÆp´‚§|[.7â<Èîü2ëÃu”¶ h=ßɸ³C'e(NݺÅwÿÍïà]ÿê=œøÂS^Lw¾øN^÷U¯¥* Û—vR€0$ÉÉÚ÷"T3ÑÙV1í¿¥¸ÚÅÓ¶Œ1¶b2-j»)‰c$E^òÅñ‹9}ö4¿øïßÃ'þè׺¹÷|áÿ&Ó)³ùÔʸí*â^Õñ*kãuFÃ1ÃÁˆAo€çûH-¤ãve]ZŒYjátîÝõØ0/st#; ¥<ΰ;[Í>µÏdŒùa!ÄOþY cÌ™&¾òª€úHdò©`äBˆw?phu­ÁYr̹lƒêâßµ vXÅ6 !ìÂj³´¬»É'§±%Zn±vVªY mñm¦HGáaqò?¼'‰ü˜Ð‹ºÙ<ø–õ ðd€pŽu@Dƒ];Í?ueðeЩú*d+˜VcÙ‘‚et«<íájMè† ®j×D a…:ÂÕn§Ö¦FP;59qhìyj0•áÆÓ7òÝÿÓ›ø£ÿ¿ó_÷š*›«þ Ï/º“—½üå%9e^‚x®TQ1ŒX_Ù`e´Â`0`<EQá¹–®ÙŠkçio!À>vФ͘µ´bÑ €µßy»æŸæ€Ê?ùyÌ÷o牷Ïo¾ÉóÖ§h,ð“Ígû³ÂÀß |hñÚ¥½oj®ëÌu ÐÇ®ÀqÄcêb¬c ƒ6¨±`D*Œ@HK×8ÝÀÈ2×Ðm ¢—/ƒ.ƒu„«^gøÙ'¼¯½~Ÿ³gϰuz‹SgN±¹¹I‘çEA•Yñ(%5®öÐZã½^ñh…Õ•VÆ«¬ŒWôø¾Öº™°™i®[; ¥êÖD[…^]Mº­r“q·ÐFÐá‘§<¤rÖóBˆ·~ž2æw=I`~¼@ý.cÌ+…÷ý9Ð÷oB|è bØ#ÀÛ1ï~â$fy Ú‘Ô¦~Ü€¼¸0Ú×H!í”Sw ²Ë„íÚ;Žƒ6–ºÌäŸ#¤P–kÚdz‹k^G¸HÆ3Ô^M¤Ìü9™Ÿ1"ʸ섛<ß'Žz ¢xvˆ¦¥ñ)©ºFƒ Ô…StÚ ¾ò t38ãYÝ…Cÿ~Ôã(šÐŸÍHÒ9ÃlHÑ(ÂU Ü•¥5J)¢(bmeµ5VÆ6øi¥Oì :ÀÕnÀB\åvÓjÊQ¸ÊÅ÷¬™MµT±Sø=·Oß›3ðF ¼ã`½ý]ÖâMîºå.&“ Y–Q•µÍP±ãôÍ#„ Êkpl@5šJ[Md/ðÃÐo4[Êš$I˜MçLÕÔâúµ}¿(Šè‡}Ë!öcb?fà \{}í£¤ì6û¶i-µD M&²†?,í$`³.Ü&пìž{xÑwQ–%¦n®WªŽ=â8F˜Åhb]X’ SLe mU¥]— ˆã˜A¿ÏhQu#òªao\ÍzZ†³¬®½Môt™ßBÿ IDATŽŸh¾×w_ÇÀ|o“%¾åi¾ÕÙ§‚• !Þ¾p ÏT`þI!ÄÛŸâï3ððuÁ “ƒ+ ˆÇèC/ØXì'»¥§S i)EnÈ,6¦œ ¾ò—âÊ&yÂ¥£ ÕûÔ^IæÍ9—Ù®ÎãOJ6˘ÍIŽRd¡¹´w‘º¬™¦‡Qㄊßð¶ÒSÜbne½\gœ® µD*[n:@A^ZùÇIvHQ$YÂ,òû„$I˜Ì&”y‰¬4}9 Ksv/1Ð#þð¡âHÈK¦—§D§Gl¶ý˜[[¹Cæ½›IÓ-îÿèÉÍ­ûý#þd÷TÈ`5DmÂ+î~7Lo$Øö©Ý# “âÏ<ÀÀÒsûœ;z˜¾7 ®kzzÀÐsÛðÚþ^í“Í2Þ÷ñ÷àÕGdf˜BR¥tŽ(×!Œ< 5"ÐT¢¢Î²*¡’"”±CÜ0º™‹ÓóôTŸ€‘;$IÒé¶ôë>‡GÌà< P¡ƒðau¸ÎKNßcqófÜÛqfù”@…„ —¾ª*çH#9š²{¸ËÎþŸ»ø ç/Ÿ'›§ôœ!9¢¯†x:`–NÉŠŒº®Ù›_f’2Ë&ˆ²!p}Û¨íù};h"%Žr¹ gúPÔ¨T2ÈÜ龈Ä_£.6¡2ÔeI%¨èìµæÅŒ¼Ê¨L…׸Ån¿ Ö Õ—Ï톽d—˳KT¦"/sn¿€@ }; {=zÞ_ûO7Ø´pÂ[žnÓÐÓ⬯¼ŽÁð©båo}+÷ï~y1ËB(æšt ´.$¾Š¨›E³(8Þ67ÜfŠÐ4]w s*VR[[ŸC«ÄÉ´öI9'àx!æUFZ%DNÐ ÌÔÔnØAÊh¤±.˲ÐÜÜFU—ìÍ/#é:Ôni)n²"5 ³rŠë¸Ä:¦¬s„9ʉu´J:Q<ëIJ5:ÍyqŽJTäyއO N­žame çY=µN¤{ìÎv(RAæ%¬W)ƒŒNÝÄÙµp]—ßù­“á6ÈfÊÌ!I2¢¹á ÛgûÜn<}#g×o öb’jÆí«wqjp–û/ý!w¬½ˆž7ÀÃÁ|À ÆC.Ï"Æz…™7ã%·ÜC–d\ô/ð¹òô¼¤8*XxÉ÷%FTäy…(™•Dߣ®êªÂõ4™$^͆¿Ž'B"ÑÃ)$®Hg(ˆxðÒŸ6vQ¥<‚Óƒ³¬5†¼­)±/¼ÀëÖXª^ìǤE‚ëyÄqÌ$?"Dl8ëÔ™!$fE¯1öÖ(ó’óû9²t(«—¹pݨÓgøC¼FgF)…£ŒªÉH‰ãå+‚Ðg}´Áp8ì†T´«:‘~‡ã9‚¼ÊÈËœŠ’È»*cL§GÓªÖåUÆQvHݸ»ÌòiÛt6mÍŸ¯³ŠÝÛ›ûîeua´LŒg*[=kŒ¹÷Z±è8áíדn™%דg}}tˆ[ÍŠ´H:‚|Y—”¦Qࡨ Г짶 4¯²naµX¡l0äËvO<ÿêêjw®¤œ3Ä:žXW ¯[ÜÓb‚i‚´Öš<Ïq<ÁÊxUi|'„FùˆÒÌë)^àᇖù1®ÖŒÂ1Y¹}²2µì DZC8¤¥mŠnÈ+6CŒ£ˆD9L“ =¿‡(†ÁH6Ö6ØŸï¡&’³Â¼˜±>ØàôÊiÖ†ëĽåI.\¼À¿ÿ¹ÿð¤÷"Z¬\:Š´œS¦’8ðÙž]À•еxóGçXa•­þÒ=n¿‡d«¦cÜ\š\àýO1ê­ P ÔˆõMvö/±¡6دvÑ•K¯¬Ù;Üfkx–ó‡Ð÷l 6ˆ½˜‚’íyMfRFÑMd"ÁqnY{Ù.ÃpÈj¸Îîô2¦´Ú-iš¹>kj•i~Ä~¾GoУ”FüžrÇøE¬÷6˜äGlôOá6ßóåù6›ñ)kM%l0˧Ԧ¶T r‘QëŠÑphqñJ91g›PF3ŸÏq©aoz™Ú/ ‹€¤œwâ0áëGJ<×ilðÕéI´§q´`m¼N/î‡1ºé+Hi+F!eVvSªRJв8öÔlšÐyã¥)°BMÛÓ‹]EÚ*Þ ýññÃÛÈê*çA^ ¼² jï^N‹Gˆïåó§ëñJž¢Ö†â-Msî›®!PŸÃ½¼û™Âç›,ú¾Ç⯙f×aÁ ήrq·Ó¯m3‚Vw#/óθ¨ Ëél2]·1çüÿ>ö‡'ž¿(¬«D¨#"7n&Ý+¦µÔ¸Æ%+kb?f¥·fõ•À¾âË€Ú¬×9“âR6@7:¾¦I¯›Á ëÖ=à0;¤®K¢;o»ù¬öÖØŸï¡)Yq×(êŒKùy^0¾ƒaH^æ„󀜾ÏíW¹ô‚9½8¦÷IªïyçÉÆ¤á(èèZÒQÌÒC0¯f¬7P™ŠØëqaò(/X»¼Ê˜åSÖãÍŽ'„ )çôü>§†g¨ªŠ4Om¶è;ŒF#‚ `­XC •`¦&$jÂ7߉¯ìèµç¹8F°5Øb7Û±tAHßa+Þ"ô""‘‹ Q;ŒÜEQ0öVle–T<ºû0ýAB¸Lë#Ö{gXíÛÌyíxs›=‡E_Aùô”oa€"C)…‚•þ*8ù1‘ì1PCzjH_ 9˜íg1i–à„ æ‡éAá!‡q8¶â^MóUkÔ’Ô$h·É¦¥AiÉÊ`…8°XyèÛ×W¦êQ’|NU—ÔÔ¤eÚÙUµöpíÆÓzzæUÅafkÚ,lÒcDÙ4[}ögðø¦g"x:ú'ŸF0üPpßÒT ÷¹Ú@|_“}>Ž=í{=Šªè2gW¹¸x{´Û—VØ¥Í>–½aƒ¬Û0.i‘qùâåÏ¿~f½ ÌI9g(ôý!®²N(tÙOèÆHGû1Q`½% Ïñz#ÖÃM‚G&¨|r“á»>ãÞ ³jÂJ°ŽR.±ê#‡HÄ݃UÕ%Y™Ós{ÕUcúµ;yp÷Óì%»äEÆArˆë¸LênÜLR$¸¾¢ïHÄŒµÞ®rIÊ9kÃMöó=>öŸþ€wþÌ;Ÿ¼Áª†+fÅ ·¡–½y…j2Cm p•Ï¥ÙæLÙžâÂäÑÆ1$`–O;yM€•hi6éšXC="T17Õ·ðÀöŸRÕ%·Ÿ½Óö Š‚õÕ/fžÎ› N C7$¯s”RŒË1»é6ƒ- rÒ*±£Îʺ‰¸î&eY2ð”e‰–ŠÃä€=³ƒ»ˆº&«¦®¹qõ&ÎŽn$pCúþ°K.Ï·9Õ?c`™Û *Ÿ6ÍgAEɬ˜Qs”ø>Œˆu­ð §zg™§sŒW1¬‡%‡Ô~I¨ÜꌇnÈj¸ÞÑ!eC…¬LAVû £Èê”8轎JØÞË@†ÔÔ̧ÝLÕ+SAM·i¶#î†ú ÷”lÕì<t:7U]^üùÏËqïuÌ\Ÿ¨2ø³8>ôxYý5héH&é†Ú62ê+ƒr›Egeº@‚PGdeŠ–ʱ J/ìúÆ~ý}|Òs{¾×•‡BˆNX&­d-èk«+µõz«(©f%^%«2¦Ù…"rC<×à <"7ÆèŠÙ%ÒJ‘V »ÉN8퀕}ÿ/¿Ÿ»_~77œ¹ÁbÂÒúb·OZÎQÂâó½-²*#öè&»—J‘“3ŒF8Î RHn‘·±ÑÛ²..uކ£#þÕÿìÉPÏÖŠÅL-]Š2Çq¡^Ñ®bRÒ31çŽâž³¯ «R>»ÿi^²õrëVžîuþˆ“ôˆ°!²æ­5®ërëÆm<²ÿ—§Û(!‰¢%4~nuU"¿Áá…AÔdÍ@ÈuÚàKŸ‘;>n€ù¶r9Lö)E§}k‰VXÊW>ež£F‰>ãxÕ#i¯sÌi³çY1A;Ö”U¢çkIRÌºŠ¦­°¤£EcÖÃMN÷oÀÆ©Ùô¶˜åSgŠ®%#HXE´ç7b^ÔC÷¬,ˆUŒp’rŽ/|†ÑˆÐ .C-K§ª*fùÔº§Ôi—¬„:²*mÕ×àÎEUà)Ÿ¢*I‡Ÿ¤˜u¬™PG] o§ ¥£xÏ)‰Ôkþf]åvÍŽ6»:‹nu5ZAò\Ø1UÇqPB‚ñp•OQÙÑã_{ï¯ñ‡¿ÿäÇæ™M0†y1í­ð¹j„þÃf°ÄN:„^Ÿy9µ8·ãpzx†s7c±¾ïcTô$C9$ºÐèÊ}”¦ +Ü…Åÿ÷ôm\8¯ùº¯áMß÷]¼äe/¶ƒÍ=ð¤±n‘U›f´´ Ða°‚p`c°…#µ1Ä~Lì÷q3MQü·þÿ‡NÖ4v´ÃÚ©UŽÒƒnÚ$&vÆ&Å!›þ&ŸÙÿgWoä ÝcV̸it+I1Õ/\½‹ËóíÎGR—i>µâñ^IvHR%L‹ Hø·Òùö½C̃»Ÿ&ö{6Èg‡ÄÚV6Û³ H-q]·£ÞÃÕ®I–T3jÇZ>]ž_¢çö©eEf’zŽëºÌ³[ñi"/b5^'Ð55Éåù6±×#rcªº´A¹œ7ÍÊì%»Ž²’Š@¶ÑçHåàkŸR4P›Ð¸…ËŠZá(=­¬Pþ(YA.R™ª3B •$¯s¤²ëËw-°í¯´›­Ùk]×T .ÞWÒMmÆÞ޶·øIzdïÑlÇr±«Œ•p€¾?ìU–Qm|¶Ï%‰ÔkÐmvÜþ¹Ã‡ë¼3Øl›y•uÞ„¦Qäj¨Õ5üÒ/þý»ðÄó®l@ˆŽVäiÈMÆPð]k´iœc×–¬^Ä4›ꈴJðñ©)E+¤uj›…ÕŒÊT”UEi*‹“7šÕ­Ó¯¾÷WùÕ÷þ*›[›|Õ×}%_÷ _Ç]_pGW‚¦UFèÆìηY‹×;ùª*˜åSVÂU¤£þèAþ·ýs>þ_êþoÞ°f­ÂÜØÂ<ƒ‰™Q)ÃQqÈx8BH',¢õ„ìûCÚÿ ë½-ÒrÞm®Ã`Äçö?cåJ½Áq×qºÒ¾mv%ùÜ ÛKð7âTÿ I‘p¸·‡ïûÌò)®{ìòÑf嵩ÈJ+‘9F¬4ƒ¥±pÙNr‰¬N™‡„nÌ-+·º!kÑÆøl¨íH~V%Tui V¥Uƒ«MÅA²œ04¢^Jj{=Ú³J„Á€y>Å85JK¨ ®§I›§Á×>+á*¾ »¡ÍâMVáè•©•`^Ì:fI«yî)¿ƒ/¤Öä¶šQÕu×ì£Î ¥•míàZª]ëóX™ŠycËÕ÷M€ªžŠ8ÿ³éxÎH¤^s€…+¤EJÙpa sKc±ÓÜf¡­°‹tîûЇù¥÷¼‡ŸúçÿòÄsÆ}Îlži˜"Y‚c¹©=ßšqv:®Ûiæ–M“Ïmdgù´+W+SQkc0V¿JHË9ûéeú~Ÿ¢Ì)—‡/=–G~ñÂEþÍ;~ŽóŽŸà+ÿòWpÏËïaãÔ:w}Á¬¯ZøB ›m ÏAK—K.ñé?þ ¿ÿÑ?à×~å×¹tñÒò÷~c@4 »lÔ—>Yžã ãþ uÀ›/f»¸@?ð‚•Û¹4»€§=.Ù„c‘ÊtŒ½F[ûÁýOsª¦Û`7´e¹ã0ŽWmÿA)”RlÏ/’•)ý``7G¡”e#8ŽCß5›uìõŽÙ@UFY—\žowŒå(hšÏ£pÌ8\%pCm°Ò2µp–ã0ôÇTuIìö™3²2µBW¿_eªÎ䨓±×c¬t™ÏçT°ŸìvÁ )ç ‚QרjµÁÛ¡Šöu‹™sË„hËûÊTlO/^áD]›Š;Ö_ÌÙáM]Æ;ð‡¶âj¾»y1#l4FZçëÃlí¸uNRØá¡¼ÉZÛI»¶O1Wíx¾ãâi;âÝ^sZ%݆{~>@?¶QØï­…¥¸-bÓÇYtÕñ¥}€s¥¸ÿ‰ÐÆj•Ò¤@8s¢(ÂÑ©%¥(R ”bqµ}(´ŽPN“‘3"7¦6•g’jFRΡ&Ía~€’ºq€Qì'û6pTvrË“¿÷‘ßû3ùr¤ç°yË*nàÙû+ÚÑxžÆÈŠÞÐjxìå¹ûìKÁ¯)EÁQ–³>Øè‚¥tö“]²:e{v‰µhƒq`°/NÏwXoY—Ló# 3+õ+Ô»óË]ÜŽòƒåèfM¼Ú¼Ï"”ÒnÞÃA2éÄ‹²*³¬ãpfp#ë½ NÎèÀz[VÒq¨êšÈ³Yª§|Ò"A:ŠÚT ý1³|JÑ@ÛÓ‹Ló)¢y­§=V‚56z[8Žc7‰fãhG®Ûßw„ìÒQÝz.\¸½GÙ¡Íh•Û ´ð†_ÒË|Ým‚Ia1ó¼Ê‡«¼hkRkŒéØ®r™d’ÂOšŠ`ீ ¥FŸÏžŸÐW‘³?·“Ž‘W>ˆ £À±”·ºDHa]0Ìrz´9`ã†u꺢r*+–, ÚÓ„aȨ?fسƒYR•‘ã)Ÿ¤á¶×³Ÿ!…$Ò‘Õ:¨,¶×–¹¶Û¯‰t´È˜ Kkº0y”y>çܹÏÃxåÆ>+§ÆT9C ¬rr Š5&¨ããÖœÜH%+jQ²¬°Ú_ÇU.éëñ&`™ƒ`„Òþ¹yàC±3»DäÆ~ê7cìÒq˜æÓÎ §•^Í‹”Ýùe»17þxw„ã8DnLYvŒ¹ æ®rqq¹4½ØñÔ»¦g|Š•h­Û$|e©w¢¹¶Y1#),öÜzðÍòiã¼’uëî0=ÀÒ*6pCëYÙêaÏŠ ®²›Ý,Ÿ2ɬä¬ÔöâJ]óÂX)‚ýt¯ƒ(´Ô¤iÂJ¼Š«=ÂÆªª¥}–UAíTLÒ#vf—¨žr[´H¤ëºî»+=æù´«B˺d’Ùfb·µjWÙ¦dUWÏéçôãgÑÆ’rnñÁFH¨ÅÅZ-Žº®©º+> âè¯÷8sÛ”¶ÙXcékÈ ×wöF„aˆò$BjÌ\]ÏŽ×feJMÝ5DZbL ${=Ò=ʺ´¬é1w=§ÇQvˆ_T¦b²KUUüÐ?ú^ùÚ/â¿~ðwøÐ~æݽÈåô‹¶è4uéàͼJq;•&„ƒö¦g¯nâF¿ïÓõpCE! JJËÖšž7`^Ì,¥ùL³|ÊVïôç]‹68H÷:~zÛcˆÝØ:¯7Á¶ÍèVÂUŠª ç ˜d‡Ö¸é9dU†.çÔÆj9¥GÜqy¾m×ãòâÍ/`«wºó–lï}«-a5“JHÁˆÃd¿Ëtí´…&Ù„y1ë´*\i9îVŽ5êàš–1Í&äuÚ5üò*#Сeuð]‰ãHªF¾ 2¢¶L ­ÜÆèXæ¶û•o9Ï ž_5Ó¶e]²Z©‚@…ÕiŒ Œ1ÔŽ­’"a/Ùaw~­\ÖãÍۺ=œŸÐ{¤eÚtºe×\r„À˜c±—².©ªª ØBèÇu¸ã%·sæ®M&âêrÍü(§ª @SÉ’J Œ[ãGVgа:X#-µÆf"I>GIMU—„nØ=€‚c5½µhƒÙ%„]S)ÔÒqެ&¨UNZ$¼ôÕwóŠW¾œí|#ò±?ᡇâ¿÷I#yä‘G®É¤ÛäBMo=f°Þgt¦ÏÆh‹£ùÅaFQXýi)¤[®!»T2#×nè²µ¾Å`اrK¼ÐEhÁZÃl¿QßB0W»Ìõ(= «²®ÔO‹„q¸Š’^Cãjÿq €qhvç—»*¥ls›9÷¼UÏózìÎ/“‰å*7YºVnã+iY-$¢Kmìù}ò2Ç©-41Í&ô¼ûéIC;kñݬL» ¶ t@ÏØIÉüˆy>ï>Ë4?êhoy•YBÖ‡ÓfïE3/çº=¯¢4m9ÜíwäJ« ¸=»dá—2§¬ ”Ô ý1®ôºžOe*ž vNÏÏ`€^‰V™ç³Îå»%ηbm µ)q¥{Œ· MiJ~ößü,eQ2™NØ<½ÁÞü2ç.žãÓçþ”äℲ²2UËUdP~?f<³¾²A/î[·)ʱœÚ2íÔ¾Ò¦dmƒoV¦ éTèÓƒ†aâvx_äÆôܾe‰Õ~ÀáâäQbd©8LöY¿aNŸ=ÍÎî·%7‘æ)ѳùœí‹Ûà@ JªN§¨sò2'3z«1N Æq0I…(Ž÷q*AƒVŠº® C7”BÐÛR늜¾Rœ^?ƒy(_1©Øžb/½L?°Áñ(;´Á·D̲†ÅÒÂmVÜJ¨ö¼éUPDV¦\²ˆM·GÏpڷ윞kjÒ2íðÔIvˆ¯l“ïÌàÆ.ÛÔŽ‹'ƒŽžé*÷xlÛqð¿Ãg÷’|RVE'ÝiŒa–O»ïoà»MøÆñ-]cÐUeœÛê©­òÚÃ*4úÇœö2%YàŒUAQç ›Fi[ vÊrMÃržÏ1 |–—9EwA8l\w²2Å×êX»Æ©9Höm¿¤¶ÂJ•©ˆdŒtʺ¤×dëj´Ažo> ŸìHŠä1Yt»Ã{ ñïµÒ¼á¯¼ÙlÆÁÁŸˆy6ÃTa Bk”[Q¶¤¬ïºë¡7AŠ7µô±ö˜fÖyèè{”ÔìÎv˜¤G~A•Ûñv)%(ŽÃ¬<¤ÀþÝFo‹$Kp=Íhk@Ïïw–_³b‚r³b†ßPºjS‘§)u^B­¨…AäÖ}Fjò%UQCàP» ]—p0ŽQ‘äÅgîF’›ÖomØÍvè5N$£pÌ0ñÈáCMµ3ï²Î¾7ÀׇéA—%§åœi>%vc²*£löN³Ÿ²WÇ%«’.µ}†Åàºáñp²A7/ón#léÚ¹²d_d"¸ÊEàt[-+§ßàæUqî\¤EWù ¾;ð‡¬kl60Neªnè£n žƒd¿«(”C'ûÙN…:Btú-Ûã0=è$AÛëm¹Ýnã¾ÞfÏ—g—¨šß˪ìxskÄú5­ý™1†‡ì6À~[7»ûê\aoõ<¼ñ|€>ápüM3ü4T¤öáªk;Ö답•eyeÝLù®Ï8^%É2ö«ŒÈ—øhæéœrE1ö ~lm”T#ùxÖ1¤ÍŠZùÏ6ká––f—•)^Ð÷ûÔ¦êpçÊX§“½d—q°ÂÈ3ϧ&û(G1)÷HóéhJÇjGª>=·O–däYA) zÚZ6µ¥p^Ú€&Çs§©$šª":HÆÕÓüˆ•Õ5´ÖÔNM/ìƒ6ÄqŒëid Ù\Ùb8†!§VN“Õ)ÃMŒ0„ží”UÁÙÁHG±—ìài¹àéÈÎÌÈ$ûlöNÿÿí]I“Çuþª2++kéªêžž…`HZ²è%ì+^tú þÁŠPÈò…¶L‰$b–î™éîÚ++ˇÌ|Ó3HJ¦LD^€ÀÒÓ]]õòå÷¾…ŽúU_"` ÝðPŒ¦UUiyË[*ŽE4§®Ùu’»nC0Š{G¦R²ÚUIDAT›Gs£ä‚Šþ ‡¶àP@ÜQó|ßG*Ò{|ç]·3Ŷ½F"RDçŽÃì^ã¶¹A«Zlíidš&RDHÃ)+}‹±;Å#Ô}jØ¡ìKt–ûÍöžN}˜†³t÷ü—²ý|ã ô<>@ÝW4ô¡Âm‹sÈåK1çý¸'JŠHÊf9*Ubà†À¹F?ôI€8 D `ÒG–f`Ã0 臾U‡9~nÈ%u&î=9LׄB.Ъ–‚FÕ˜‰ìÞq7“Æq$ûJîqxÚÇ–Xc…Ý´ó=D¡D† ™0îh±k·S€dŠÉ£â0u¾òÙc.DFP#b,ý%"!ÉàRJøÂGƒ§ Ø€£å¢Ø\3F˜FezDe&¨û'Ù)ZÕâ4{LráY˜CO# U.ËsÔCeèo¾ÀYñËÂ(»dTOÅÔurÙ8÷!úCW_4  A.uÜáÍû”¼iš‹˜0ã²ßb V\[F‡ÒЍ}.P5Ã}ŽæXÄKJ̰6víÛnƒfhH0âøÚ! !¹„gY &ég ÷^÷5vÝ™,PDs${ŸÁ Åݵqi)­…úZUÓçóìýgTŽ#]3¥þ°þ=Á(U_lYH$` >vÏ_àüv½Vëß¿Õ}¿›f4\q]ó<ËÌß÷íuÅÙå×KìÚ-<æa Fª@©¶¨u‰h QO;ŒB!ˆ9t`òçzô‘†ž;ÉðÏ1‹3ø#CÌ`4´©ÙBkM®oÌcŸ<”ý92êÚÝÃó}œ„'H£¢0B¥Jc‘Ê}DQ„Áë0Ò8E‡‹lar ã»a„&UÆ¿3Y˜”ñ ¤Ï¦#Þïžà(=¡”nl¨»£»ÖšhwnPwÒ¿wò}TÌõ4¢U-2YÜc„ÌDF,Œý”ãÏ»劑ÃÊC.±iaáÛnCînY˜# gXÆÆT)B.­HDѼ¡¶†JíÐPè„™s(àGŒá`s4{Uœæd©~®hº‚[õ%.Ës¬›+‚VÝ# ¥'÷Œ”<ö,8)µ¢C\UæhÍ § }×A&!‚)DäEð5ŸŒãˆÐ—†ÝÐÁ›L—¸k·h‡ pŒc“Ž¡{ô[ÌdÎ9Šx° Ìþ‘ŒQ†.7ú ±ŒÑ{Æ7D†&UZMD‚Ù”7ˆj‡†äô®ÓY@¿¹¤„•ýÎ<^Àƒ²ß’Z-—áñ©+´CÁÒpF]lÙíî]·aŸ¤§¸m¯ ÒpÛÝuðàÛ¡îkƒi[.wgO9¾-†$ìØó¿ˆƒ§ùCÁÓÌ3ªS-vÝ×õŠà‘}¿˜…µ¢Áœ£¼­ë5½êÉAo&2â!;¥#÷̽Öëžœj¡´¢¡¥û~œ‡MÈ%"i˜ WÕª¾ÄM{ÛæÆ86ú‚`!wê0Ô>Aß#)Ù},úüüÜœ®lÐî8ŽBàööÖxV‡á ÿç5ZOßè?cü›abût†Ä}Ÿÿ.6Ï‘õ’6AÚ%è§·Ãñáv¸A3U¨FãV;äº@ÄZêyDeò=Ÿ.¢ÁPþÜ„¾*šÎëh¤k¢Y~·àÆw8àà(û-á{>¼ ƒ‡\d,Â,Èq]­ åË Ðô5R•X/ó¾<ø(‡á7ãØ¼GˆÃQ‹MÃó° •2|î~êP«"ô™™Ý ÜWÕ½X$·öÄîQê|ßG¯îбû•†q¶«uÖuÜûݶg!&š;H‰²Û©¥ë¿nFxóm|‘-Œà •NµTÈ_ ¬šK\×+R.ÆAb†Î¾0t>™!—±:\7|kénÌÚzêi‚àóèe·ƒöïl@ï>w=˜‚+ƒèÆîN%îqŸã³›Ïp]¯ XˆíðG모¬(Jò»ËÉÞ]QCÓL§êKrT¤HD ÉîC®KC¿¼¼Ä0¼;ÏsœœœàÓO?EQ‚Ibš¨õz<Ï_šŽþ­ß½‰…xš¦'ÑýJèÃz)ìhWLìÁƒ”ëõ^3!c¬ÛĸéÖà-C*3Äab2ÝtOž¹nªÍ¹Áç ÜŠöʸ’qŸ“)úþçqþkËê¡0h…ÔcÈãÇé©(ÎÌ «ê*$647 ™HâÝü=h­Ñô5Be2§i¢B7Œ–8 †‡ "((d2C$bÌdøÀÁl‰AXLОa#ìzc(¯¡Ñ =de·£Šðk¿—‡§ 6v;˜È«ý‚î6¾^õ(û-1œ{¡+˜®ØnÚ[pŸ# gÐÚØÌ:ÜÞðà߃4œ±}"Râ»âé  ’D³E4ÇA¼D*2<)Þ£÷,´]VÕ…·xwø¼H ú±ƒ´†YŽ_Z˜¡úYíР ÌãY8Ÿ' wBîs¬ª+ ÚpçÒú¼¾g<74Œ=B&„UŸïža°ÔÁÎREß¿ÑZ)0KMDJðýÓÏ0 øä“OàyÎÎÎ0›Í°Ùlàû>ÎÎÎÐ4 ¢(ÂÍÍ „T°ß®ïÕú×ï¤@?ìž]ÇLcæ?ŽXz‡¦c‰%n»5NØvľɲ«‡ëÆ'‹ÇB.È…NO#›ìíºo¸.çáñÖ᥮ˆV±§ít½S-üdùwøôú°ª5BaìËöÜ 9à…Æi@çÈDÛæ±ˆ‘³œ`ǨúòN1H5ìÉÜ(#ã#!¥DˆœqL0ÇúBÏQ÷5Ê~KEÒ³>ÙnC,ۙ铵_¬îvh¿²Œp%Øshs×ÏS=à÷ÃýAm  îs#ÐðïN9N’½®WX×+H.XhcæX&Çxœ¿{oC‰E‚›z²ÛaœF”vè–…ÆËYrIôPmÓm`‰Uue!Ó@H;( |AÐ~6§ïy8ß~IÔ½VÕT´7úqò޹/í}Ou¾Ào>ä1¶Ýs²å>G*2Ó9ïÁq¯NÓ„_üâôç}ôÚ¶ÅÙÙº®ÃjµÂ“'OÐu”R¯»Èå?~HUûÿ¥@ïwÑ‹ô>&½‡˜J Z¯Áq‚7S=̵2“õ00ÇÆ›öQ‘Ú®¶qG®0?üý¾  Q5uUn–IàÆš¾3ß§£|(%Ò`†wç?B5Tx¶ý‹äa¢U-º©ÁM·ÆQz‚0 ˆ9ÏÁ½€ðÈBÄ(ˆí`,@1 uFH!MjxhT€>óÍ Õ¾ß÷Áˆ=’ÐD&­êK²­œI3„Ûï²¾¥,¹W=u·û³+Ìn8æ8ðZ›â7ZXgƒvL iÔ}M*@wÝ›¾Æ¦»Á³ÍSâ°gaŽE¼Ä2>Bæ8JOÀÙýÛ¸lw¸Ø=Ç0Dwsæý‡ò˜ ,w/0ßÇóí—”9Yö%¶í-Ù­æ²0ƒe£ø– ä['ºZÕ”êâN3Gé 1fÜÏr'7É83ð×Eù%zeèyN|ó'?…ä†r f2£¡ä«Ö¯ýküò—¿Äf³Áåå%~õ«_a|øá‡899¡gï ~ñ†ÖâûÎ;è—uÒû].`Ò—FvÓ® ¸@&3”ý‚ #€hSœ(d?­Ú-÷Ú®8º.šûw*.çUY¨±í6Yˆ0¨ÓŽ¿ôãñ0᪺c “Ò¸¨žC° ›Î"¦Nƒî“YèixÌCb㡆q€ðLÄRÊg|ƒóè±ø\ß"Î8©Ñeg¾ jã·%bpL…Nµ”ã~¶cXh­é½PYI´3ÌZÄK1íQè<ødŸêŠ~ÙoÁ's¤_U\7kk#0Ñàò09ÆÙüý—çËòÃ8઺0Ö£<Æ {¤"5³S‘f¾zhh€\¶[r¹[ÄKÄAbyüŒ:j{e0M¾Ø|†UuÁf=ú–ì¨$³E:“š¾¦a±ï1|±ýI0Ãmó9ª¾$ßëÃäØb߇ô:‚…4Ù:;;£"ý³ŸýÌ€µ¿ûÎÎÎîQM߀ý¦ §úeXô~wKL‚d«ò3?3‘NÒ`·3iètÿ}õ1)Ínêk\'+ð™ÑqÏxè;ã&m; Ú°\Uç>À&#G/¼o=dòŸË'Ù©‘1?o°Ûn‰J&¸éfáM`¾p’4½wB &ÙÚ²\Ì8LRæR=Ú¡… äK*æ3Ä"Á¨GÕÕòOÖ~ÓÅü»cuI…-“ýùþf0ꑎä®:ˆÃfƒWß™l©q@£j\UXUW¸m¯Iáwœžâ(1¾Ç³w^(ÎUWâ|÷ ÛnC¶ æšÖ˜[oè›h®D¤XWWä~ØYæfhHô²LHÆbÀn9–Æ•-λnC²îwf¨ipT¿]»¥´{nùÖ{xõM³FÕ—øÉá߬²ˆ—ÈdñÊîÙ]³Çͳ$ ~ûÛßâç?ÿ9~ó›ßàƒ>ÀÑÑÑ=8ñu/Òžçýî -ÐO¾Óýª"ýOsÝnsdo,Ñ¿êKÜŽ7xgöèš’IûŠ;Nîó>ѺҊ°fþ ÙÅ$,@«ZLИ¼énÐ8vˆ…é¨ÊnÁB<ß=ƒ´Æ9›Îà©Èp”žPÁtO™cÈá=zš`†³ìLŽô4" Ó¯½ž*úKË~]áõˆ˜'_YÐ]‡m`ú¥X³4zÄ"6¸³í–{Õ“Û ûÅæQrŒ£ôï-~üÒâ|±{ŽM·!O‘;/åCë@熖î~\ *K²ŸÝ´·`>G. «6HP²or‰gëßSQvÙŽ§µ6>+ j‡†8ØQ`ÛÞ")¾Ü=¥áôA¼Ä\.°ë68™=‚gïçWuϾïãââëõMÓ€1†¢(ðñÇc>Ÿc½^cµZa(¥$ …ù¾ížß ÿdØÃ­"žcÓÜBieTa¶»ÍÂU_bÓÝ \U n¨hìwÉŽÿì||Ññv‚¶¶Âà6!Û˜­ƒ†Œ´±ØŽÛ¡Çù»diºí6””ŸÝþªq’žRÁ|AŒÒ yPñŠ7‘}õ²nÞ½—ïr}“ àÁ½@ắIôR÷µawX¶„+ÄÎ^ôªºÀÓÍgĶq”ÁÃô2ˆðþÁ_¿ðê¾ÂÅî9®›+ìºAÎçc?¬ØÁ&Uü’è–äÑ14Ä ‰‚¹,F<¦ œû—å9¶ÝƨU^™"]È…I>æ”Ò²mo rCâKÛy—Öÿy×íð'?Bfñîy4G!dŒôÒ‡—s$‰µúí{JzØø Ã`6&!ŒU/ço)vߣõ*ŠÝ÷ª@?\yTÜ9Òyf2C5ì0“&oBk¤€èìÉü»àM'ùÞ7Fw7¦ë¢‰ŽomF]AYˆi2ÉÞn°³ß=@çH¤á©~øì?ñló™åáÖC…Oo>ÁQr Œ~`6”Y˜P’G$¤"sRàïSAþ¿.7ìT‹Y˜CiuOÍç8σîQZo ç8è(xËä§³'÷¨tû˜óùî™±4U5ê¡BÝ›È+Ïó8 YYW:ßΜ²ÒùIç²°E:³tÁA›.‚qo\×+ì,”¢-SäÙ#²¸x¶!PZ™ÌC{/ö–nølû9®ë&h,“üÒìuá®SöìûFi܉‘ :« t÷9çümýštÏßIþSnŽHÄÐÐ8LpU] VTÒ©·–qá{ S|DÃ÷ 1¡UØâë:¨ý¥º¡Áµ÷L‚.Ê/ò'˜G÷î˜ÇàÁ¨5ÇiÄÓÛOq]¯îqîÝ,C°Çé ü\×í⪾Īº@¯Œ¦W=:Õ“J‘,°ˆL ¿öÔ¹ŸÅùð$ú2øð5þ ðçïuí ŽNµÆL^d¨ÂjPDsR©¹Þx{à ƒ'´´1¸<;š­YÏ0¦»˜LÞ›ä›nƒØ> ŽqÔXˆå Gþez„:ýg|tþ!±UÓk;þí¶½¥º°ÅºQ5 8kÕÉåkÇ1ŸÝ‹Fsœ`İÉŸÞ|BEcná fSàŠ%~rø÷/p³ÕhÙõ ÍPÝÛlûÞiöØpуƒîóµªøƒVèÇóhŽ/6OqÝ\Ai…erˆe|„ÃäØÈç­dÑô~ù1.Ës&Ý8îíð8‡É1¨ë{ZUSâŒéˆS‚=vý«ê ÛnƒD¤x”?¡÷ÏlVÈå½{ã›ÀƒÎß¿í ßvÐö:H‰ur‰y¼@;6h†egìgaNÝë®ÛüÖºšºcièKÊëUo¹ÓÔ8PW fX€<̱é6HíäÞ¥uºYòB1:̓y Ÿ¬ÿ Œ›$éÆòc .·ì‡NµÔARáÑ —o”ϯƒvB.áô·»dVr¾µß[d éQz‚£ôä¥þ.J+¬«+”Ýîž W"Rò°Œ1”çyЃ#Å<6¹„,„Ç%ÖõŠ:a8ˆhó&f6+Xˆ+åÖÓDẀ±uÃkfýU2 çôªGÈ"È Â®Û 2VêÇ!ÆUOµ8ˆ r¹ ø¯·žÏ÷6š·ô÷mqf¦[Õ" sÜjB.(ìuÛmpQ~‰ãô›ö3‘á 1‰Õe·£ì¶Ör]]ÑhT 1…÷BF7Ås²žëà•VHüôžñá:ÎÞÁ"^âéí§¨úq`×mlòÈ]Q7 Ç2½£@¹Ö›ö@:EcSÞ%´,,ìãDB.MzˆH žþû*˜§J)@–¥ÎÃÚ™%¹Ám& âçlf¨°io)´ S-NÒSLÓDq^¡õ¿`ƒÌƾí6¸ª.î˜C–áq<{‡D%/0Á‘faŽËá\ â1YÎ:§B.Ȇu`æà>'ªÝÛõƒX¯oíº¯erˆNµÈdfº-›c§¬N§Z\UYh„žæsãéë cÌ;ÃwnÍßµõ;hU{wÄ’f‡\‚YïßA÷HùWK¨à(=¡á‘³Au0Ǿ‘‘£ú¾<*Þè;ð05ú‹ísr²Ëœ(…\ ˆç_û:ípç¸U-”•æ;¬uš&SXíwÆöìA[Û½:†3,JÙ±ˆædPäºjÇÔqƒÈNµ­£ž»?rY çĶ`{b•\ÐZåo]¯ð|÷ «êŠpvÇî\ØìNÏBo»çúú_EÍ'h½IEND®B`‚jigdo-0.7.3/gfx/jigdo-logo.xcf.bz20000644000175000017500000016247407701377616016477 0ustar richardrichardBZh91AY&SY˜~¨+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÐÃßB™¶}ºÜ[›zèéöw¶ùömµå6îÛ|…p²¹%9.¦k¶¨^eãPna»Ý{APT£ç¯½àJ©TDØÃÖ )ìДn㊇ÙwnÔ—v}êÅ=öê@_YMµv5 µlÞÕÞöÙ¯y"ÖܵÏ^óÆÛ8Ë—_|÷Û}¾ÛÝÞ9W®öØÞÝ{{ÐÚï;Íë²âˆÍ}ïz©÷U*ë{ƈãîï^.«=»ÞÜÓzíï±ð>š÷¸î,yï>Îï}ŠﻣÍß;ÏV¾ûÝíZ¥÷œæ÷w³Þ;vz÷¼ªhæážçyGoÍùß-®Lޝ ›ñýϯ.»÷~÷«8ˆ£1"!ð'g²°ã”“Ê’QÍ¢ËùxŠuùý â“ub„‘\–À* ¹hæ ]æ!L "€«…uíc>\$da)Äî rM¢<°©û¯ÝX ü0?¬ýoäÃùsüX|èY>æ´¢^çà¿V;!uãÑÏÿoò¿½Úb8Îg3¯úúÿÓy´ýïÍåÿý/çþÆ¿»ÿã"Óðu 0¨°ªŠ¢ßÄpV—š2ˆRr‘RÅ`lJÞOÙéüŽGÊ` ˆ!J"Ûíó3ì°&•AÓøú,0¶•§íq@ÆÕ(]ñ^º. Sn4‡µÖËŽ×}4=oëw–“ûœuômâÉïì|k!ÈàI?d~7¢s¬¾âOìDYÙü/7FŸ­Ø§O?ü?[¦ƒüîR0Ûœ¥‰É&peüš °ï`ßïxHÕd¯~Ÿål—êéþÙ ›®G5‡xû©ëCë[£fý+µoðæzý¥ìíñ|lïð?‚?_È‚\!޴£ca)’2 äC-Tíõ?×÷šÍ¾F ÿÇ?‡ü $8ŠsÔC–bþîߨÜþ¶¶®¼w§` wÊ@ïæ2?¢$y¼lß&ë‡à¾nô~^KC¾£‡êXæ9×\€"<Ä<ˆ Œ|RD0$õ0Ç£cTÁ=„QG?×ý³¯oõ>”cóo÷ÙUAˆ6íb">¾Ýl–^Ž•2_^J©ØvuÁìj«nä°Ÿà018b)ò¨K %šJmD(Œ¼ùh”i#å›C`¯'ú7ü¾ÞËØÎ¿÷ÏN§WAà鵘né*NÐlý•í-RÖÙúµ‚O½hÆX¤ƒÆ)ÍÆl·”Ȩn} «|#VýSÍ%Z[ó£Hÿ™Œæ&DÞoåþûDÍþ œ›çYu˜1þ®ûî³þ.5¬¢IqßÏhDMOü ^æ|Îj~^7÷“FžGØò~gøz¿¡uû_¿à~·öø·¾Çñ§ÏöäÌêµ7]Î~í×'Ëþowú¾yxúLìy|=/¯Øô­-$ïùŸ«¡ãÇÓhwsÕì|˜–iÄkìþOúû ôü3pYôÅd½­ìðûßE€?A=iº-žé¤^ e<ÿn.wâ‘0{R @÷/ûz~g—À¨]~ž\›Íü\óÑ$œÍH–¢¤îÊK6ÔÔ\ YˆãÿÑò±j3lw`ÎG²+ÒE˜$}’ÜK¢ŒqìÑð½GÜØ[p+ó}ùD dû8, J!'äô_©9q”´8Ä|yƒF£ÔC € RçRî9¶ËPÙÎÜ…ò\ÞXb1…v[H¬)7L MŒ@$0/‘I“nWå”Åjmc†Éd^\qÈΛò`«¦q2:ÒükGÉ$4 vóEÐ`¸ˆ Úv¯åh5TãQGáÀœ×ƒ¶HóJuèâ‡6WSæuZý¡^ÃÓ¨&C!6*¹TÀÜ"®×_ÞÙ–M¡-¡i ŽWÕFH|\Zt¥:Ý}—_KÓõ‹ò/ÁÄþ^îÀÄ‘ûa (§C1‘å¬ÁÃÄýÏÇî²Ãiäó¹þßOØù?Oº(P8±œ±‹ÛÚ‡ÜßœŒ?­ùÄN<¤Ç¡ éþ¬É ç57 ‰)”ÕUvÛÿOW£=_÷}âô<>uy¾1ŽE{²ÒÁ²+ïýªØ_Ö•Rõ%JÛ¾Ûí ƒfmIM$•ÃTõ þ0Zç5Ûn%…ŠWt©¾°$§0Jž¾Õ÷ŸÏù<éŸgÙKq¹¯´¶êmN—:žo«NU,±Ej†ÕÿlÃú¥£`¥Dƒƒá/*,.òôóÓÎ;Þü}ºÁ鈞u aØìv4¹ãeëi÷5–Ñ¡ÒËÍP2/X^_À|‡ï›y]z?ÞÍ™yÛ7Ùé>ØS1£¹LÍ)$·½6¯iÇãÚQ]m•ÌnØï‰ïm_Ÿ—ŸëpŒ¬3CÎ)_“T¡$š¿¥”#Cpøw¶ÖÑYLÖãÛ\KóorVT·eB†Ä!.ì#Yb¬t¼l”­nññg—Ãcx…¯õSŸ5¶ñÿ´Z‚à#|Š$ß³ì¤a…®>­ï{õtmOæùP!.]bòb»êˆßÔo€ãÍù´{z¦¿'ŸU£ú ;kŒ(L¾@ÛMMtëN]?qãæIíl·;;,g]ɷÓ ,à‚þkŠT¤J8é%ê Ã9oýrˆE0AÎCë.Ûb$êÞþÕ_ïÔÐ?PShæg{(^¾R"‰ 7škÑìô«’ÚÅ ¢& ‚ø¸ÉYmŽÐñÄàP…¨\L™îÑ?¿Avpìæ:˜íf³´1¶øïâÅd}úäG;%dï«Ë£È3t^ªælöÔTÏøCdÒJg/÷»ºYUÖ‚Y}ºyà`LvÞ¿­¸Ñ—‘àý¶}||U]žµÿß V½]ܸ°“/¿ãî:)a(¤ DYÿzà8AÊ!͹N4Ém:\Y+ì“J@X£Iœ‹éÔ.è\mn;6Ù$ +œsF­?ÐìuÿN¾¬oßäÔ›]Žç£¢10ù œ$ýºçÅbjŠ€NæÂÚ"‹$L…€}1ø•« !ö„2¹$cߘf { vy|ÙÚ[À!tDJ0˜ bÛÊ;žáœ€>Ïtߨ|pÙ…ª«¿Çô²t9=ä燀Ê4‰»˜GèÈ@V¾PÓð†€ˆç¤ýÿϹoÚ¹5%+xª »³Â—@]`ì_Ã¥V–/.ëÛ†ÙœZ´6´ÑR+ & {ly™Õ·(î âÕ…  Á´iH~`1²µ4È­ÏΚQ/@Y£èíºâ!<ÒRfÒmê“®¢€ÈƒC%ÖiVE¸4V 4c)¬ˆ¹€‚Èæwµë´_¥&íÕR¢H–Ýb-!€T).YÄr^ïÁ@Ï;Ï$ã8é¾È øñP¦*‹ÀFJZšn6x 4ŠÓ]à.ÒhMI‰MÅÅAIl¬w$~áä#ñ™düg¾?¤×$=3þÇöŸ¼|ÃìÚ|û `à7 LËó4À;CÄ'Çd¿ÀÁ21à’Òͱ±ÅJ©S 5þXÑQÉ!ß­ƒ¢tOŽzÒÈ:–p[«E§j³æÍ™(בÞ(}!„cålgp *ÐØÿ=¢LÅÔ h:·…ÛDXcPwv›ÙàrA€ˆÿªE^½]e®úf¾«®­àðÞÏ ¼–`#Žp+£X£"œ-I«–¦Ÿ¯‹%òF^õ_òO81‡è& «P¦,k¯]¸!T" ‚Aì6gè–Ké-2Ý6×G̲)£‚Ï,§•¥wMhÞtB3K 3€- +ɰîy<úy5×[a!¢ æØ5qãfý?y[æyc£H¦¦:z¸háůg …¥kèu¡5²˜ …Pœ©>Ë!:f?TB°ŠqµÔ12W"¾ÞgrÆ}{/úE’w ÄB‘¦ìÅM¶¶aKÄÑ‘z2­v2 ×fø…ní]²J³„¸PaAAp þŽ®ÎÍ hyqцŒf56Õ¶V¼*¡l°°Z'4Çr1Ýû!F‹ÖÐ…27>Kò)¸\ëæT‚âœXÌÝü¸¶Nfß¿iò˜ï½j‚ þ^ñ·ôïâèÅñár á¿V'Òé³—^‰/}äÂõ&7ñý×”b=›g6m »i²Uöˆ—ȹ€/üß~¿¥7ýY6?¿?ê°²~®®´m§ TˆµcÕi,Ö"é쾆Þ7 ¦•‹þ ò¹1dæ 2£Ê…HC(ydÂÈÞ¶Îá7‹â‹‘E[ŽÌÓ³EIÃÕ.HJµ>Ÿ°Ú‡—U`ª«ÂI'»EêòÇoÖ&Ì0Âú¹C ´ûM^1‰(u€§ NÄ'i0—VСª Q3>,s WTR9#ªR#‰¥¯rûg” 2(È8béŽ$°o <¶j7rqï0qsqÀ˜ò .l~ÑÐwÏjí<>uõI¤2 G âì…Áˆ A ;À<¡!…õáf€g„ˆGœ­okXÖvHqæõž'£Iî p£¤ÐîxÙæEÁ÷á…öŠùr=Bhz[‚P=¡@@€ ‘†`—âcŠ“êC¡d¸†Ë¹‡ QóâPCl6ÁºÔ†+»½ri³\ïÛ¶R»h¹´µŽHøWVÓ«ùÊS PMÈaÊu;zÏ<ÏŽOK÷aï0ó›ôly·‚ªLŒ-™!–` .QýÎmüñH5}SkÜŒ¡ŠV¯Ù9HÝ `R¼±²X“±(Í™´®'-‘)_¶¸Ú•Ww†^ežÅl·,ÇÌÞ^æûl½‹ZÜÆÍîýN×ëh9öÁZüœnIU Pß#ÐÖ3™#¾2í²õ™ˆ¹ÕÂÁÚ¸yXÞ׎ß|-RypÆ[+yUÿ€ÖŒªcEw±×cl÷~ûÅÎ2#zDÝâMàfáGŠyeå¨g»@+¼…§› ÈéÆÏfÔ^FÔe6”‡G€³ã–ÓÝÈTÎic)™QæyºöõñľvÓEñ›ðÂÝá0ò;Måƒ~ù§P­Ó‡‹N›[Åëò©ÝÙúïg­èswȪ}WO$›´š2qhªj1 o;;nŒP+§šÁx¸p ¬q‘tG^11eci)fÏF^W€äÉhçA`À&( !IŽ+íeùu2@ª ÙždGãC™›EU–Ó ÖbÖ”y²ã™æ/Q2A[pa±P3t£ð3/P?ê1µuRRYm]ëËWFv¢§Ecï¼pò´ö¯\žx:øøƒY~ÕëTÛnשÃÞÛ]yÚ¯_mš«Í×–>>þí{ ¥{­Þ46†*ƒrC“ dp9à&…Pω‹´D)€S) ¦&ª :Ž4÷h†taZËeQMKÐË’ L·Û`v /WÉ€˜Ô€Q=½‰´õ¬ÊR¶…¡Šu´Ì;ùéêW3œc0ÊÖrS±!ÔÅ’[ró×±aaRÛ”o0Ü‹a€ 8C0òNçˆ|OE 4öB1Ñ«ÔõçµÉóöácÇ:ýŽÎÞœp®·Œu„™IˆQÈÖ¸m5ÙìáÌ&Óia0ÜEE)#qãÑl8ôµëP:&‘2˜A³„îx+Ã<œ2¶½Zo§Lp½û(}Íû¥! U±_$ì·ç¶]2ú¾8ëËohCðó%!"xwöîØˆö¤Úîs®|é)1 T•6F£L ypï=Îà}‚O’à г`lùG¡£†þßQö5Í0·v+ž×⣓§QkX Xz£Ó]©RL«ëUª7F«‚@€ªp„mc“2+Óp¸Ù¿„ Õk“"-ÎGš‚‡—•I¦®Vaä@ï'Žqä°PÅ‚Á` B=n ›ûöbzæÎ‡•2£Õ¨Õä@„³™sºtM¤$5jÖ˜i˜Þ­ÃYä¹gtµ–% §œwxÖ{Ë•Qó-;Ü=×ÍÃo˜¡õ7p<ž“ˆ(“£ÈÂá@nÛAÌh·\8ºz¼õ{fï1Omî÷GÎÉÉÅÅÀn€]»èéä߯:Z迵ï^gQÃ#£*zO Lm„jü“H2 …0˜L&DV}'1@×[V‰Jg´ä{è’ er²T7hvGdX¤Hµèx–åÁ!$RÄÄÎi$½œ€­×ãÜG‘Êäó%ëÌqf7œü¹Zš !˜ˆu|¿º¶È|É?¼­ð8¢‚¤Êu2Üg$™ZÖBLIŒ1S¸Î"L™¼ÅÁQýƒG“Î)4ØÖèEHæY³ÜÛ|pÆ V£nþDõ<Ä®?SÔ»«N®gZ@^qˆ;ùë¦B2 #‚C½B²1ƒ–t!üX½¨‡$v!’1b AœZ.ÜÇô¿Cþ ÀW=ºýoÖÛŸÜ~§ñ÷“éÿû«ôzÇ îcì!êbmúܼ?_oñþ®ßú»©Ôì!§|cgëWôû:° ˜¹!h6#D?ªûüý§ü{3¿,ÍÄùðÇP…›/–ÑÔx8VyK)¶JæÂàeeX6Õ~Fû6M¥zµúŸ­î¼ÿCFfA¡• ›ZÐû*åÈ|ÙD=ϲ–4uX±' :¬^IxÖŸ_Çð^ÇCL†9ÚÒCEy“§.;tÈrv 6 ßFÔÉ9eŒœ´R}Aá0Ë¦Š›j¡¸éÍÔá¥E—”½©6›k¼8žÉŒKÇE|ÇÑŠà%>iøfË4ÚÙ1iH·‚–û{ÐJ<§°ÓE†u§(µ™fáÁm-„¡Þ öeÃè†Î{íO ã‰xAÜž ;² §v««ÖIIÓ®ˆöë¾íÒQi|˜w!ßCý¬Šù }ÑVñ¾½ß£ñáìý%ö¦‡|û¶.·J6‡¯<ÖéXüÃ’}3ìªNí0ôbÿlô¿h¯›éMú_ŸþñæX(66ÚhØ—éDµõ¼ïúIsïá#,íÏî'þ{¹è¼sH¢ ¸¥{! üæ“ýö Òùå¿8cœo3ªHÄ´@ð¦&´¨H„ƒ ©"È€(íñ×{ÝëuåpØ”·RO —)‰v8Ûï)H‰‰p”®Ìígü™ªî 6üí¿·tb8g›gM¯ô­ž `S1œ“:Ç-˜U}¹U´ûþ?­Å·êrð8;õsr—E3€4Aâ±JžíÕ~N£'“"¦˜€³Û©Ô€)âpXü¦E\\²$7ââY°V–šJ×gãLGØãM€¨!^p&ÞO„lß ¤p ÿ»1¹לˆ)İšñ®Þ/“Ñê¥Ñ˜$£`cé&o‰Wj{æ%šg™„%ÕPFåD³,ÔQG:kæ!" XB!#!"ŠÈI$„’ƒ HI$$]é €HHBB1þî…ñµR;Ф¼zÿŒilÚ4Ï·ž–}|c ú<±£)›Só·Ü÷o·¸¦S‘˱2œ Q/½AƒN´„$FBBIB’IAI7nø›5ìQlˆ‰i< "Q•é"Dc 1 bPÈ"C¸˜1$C“ã¤J‰ã& R/I"DD@"1 Œ€ÆŠòDL& 1‰@cÐÈ cC N) ɈD"EBÌh1Á³N†711 ¶ppsc@Ý4àÙ×gÒëË ppiÁææ=.¾\O4 HÊ"j#vpH¡)FxÈã,ª2B'0³‰hÑÒ’[Áœa‰NÓ¬p §ãN¦?µlÛßÞÏÒÍ ´Cl.†€ØÈDH"ˆÍszHEXú$¤NyžDâ‘$2pýµŒˆ°Dâq޳b!@Åu±:V˜ÊZî¶=Ê’„Åe±‘G2¤áÛW M•ø; 3“Ÿ×%0¼Oö ¡¹{W´|ï­¦POûˆœZ;ä€âø¯]Þ? ˜ £–DY$R@‰‘Œ’HÆI!9ŸÑ0øX`}/©^Ôn ¹¤ 9ÐJ­ö–lå½£0pP#+‹‰&ú0IѤo–NŒçΟÊÞœ…íËÏFÕïñï|Iñpß=\Êl‡DB@I„dHY”ËëtÇdt°z\Æsu°ºí;u7l„¾ƒï C¨cù ÓCf 1ކš„BÍ40“%¢$Q‘ŠC§) Ɔ4p†@2D†‰È¯$"%)€@dá“D±Z÷·äêÌ}F1ã›|ÇCý‡îyôŽ`ÄPD%Š3tkÑ\Zîf"sHëÈI‘ yÇ â’q(æ‰ iõÄÈ$J4%ÿA¡+1‘3Ž<:à ÒµÞKCmHc.H€[̽Þc€®Šn*ç¾]\(g9¥›Ñ7>sN10€Ô{è <†{vßø;AòÏK5(8yÚ6ŒÐ9APU½åøz‡ x…#I„IõÑ¢2Švý~­6ŸWxÄmñ%¬Uü Üùþûèl ÓËóÜ4þWÐÄ;ÿ“ù[|q x[’wöéóþFW›O:ñ‘cyæÙÚlM0,õn+ã½»0Š¥í$dö^ÍJþ¼¿ªé«ç.$/84kĤK8 2ˆ” á‘1ÐÑÕÈ"C @€ÈBRG{ï99\Ž,rcfšnÓM6i 6iÀ‰@‘DFXC"V9D„I †n΋œ®(my½>-#¥ »û¶! yhi\؆n ŒnžÃ:£HAƒôã­åÌu5•ôèÉStº&¼öÓÙ¬4¸ÅLéhtŒ¸Ä.ÓL )ËtOŸ18fÙ€?™·‘€üÓð7“êÜk§—‹Îº°Å§¥EÀgÈŠí¶ l|lœ*2M°Ÿ2+Ý(êvø1ÍÊ4¿P¼,#`Yœ&#n„ i6 ¦Äi i_i4êta‘¬á¦óAÀ¿M¦‚b"î’ $CÚ– ¼£„Þ>ÏiG£©»!Ú\ß Jg_@¥íu9·ºòݤÏ ùÚíI3Øùϸó·›cgkf¶9PÒ£ØÌKÎÒ£sœé'–ƒ<óF(®&2aJ6ö–T/§¤O"A üãE~%:JËg- :%ŒDd©SRÑ¡_­ ØO<‹óîØ!µÅ¡ŒcÚ¢¿ŸN¾¿%gËÅ¡‚˜¼-ë@•b£q#Ío¥šoxœRLè ' ÝP+êÕ¯/w;@»Úx9õ3YÎÌœµÓƒË‘_¸Ð‹¡?+sƒÐ}"«e­½‡+Ï‹â[»> ~óA¯Ý{xž{àïFo´PÉ’HÈEF@€E›°‚$SBð•1´ÞòQh¡=e“‡(&žZ™Emmþ i´…Õ·=KÆßØ[$…I€›HÓi´4ÈŒWùáåç½®Š¶¥¨—]]ã”C©ÂÉŒ"ÂD4^ŒJ¼chc”HÐÈ J’G³Ü®™µÌp&Ά†œ:ž­œÜœ˜‡åm¨mmŽº1˜Æ²Àmg†UˆÇE<.Ø|ÏFI¡ß¯gÆÝ“—/§a³fŸÕöøÝÅ·[ ´†Yæ@Ùš0&p¦‘EHé–Ô(Í1nË¥ŠCDÒÈ Õ;V¨I§öWÉÍ 94ÿ]Ù®Î\µZ;sSf:Øò˜ œ_{Çmrq1¦ÜTc¨ÁXž¢Î:ÞË/i‹M*zº5ÝX#¿Ù”;E³àòd ¦L#ôégåâJþõt. fB‚‡Î7^;:Ðñ¬H<"¨Bf(¹„cƒ$€D"F/I#lZŒ8X âàjrm:R$A‚ú³Hø(÷Ÿ }‡sw¥¡ùùœ  ·è­È?¡ûJz»4 ·„,›b`Óm±¡3ƒÓÜÌ6»J]=±l]Ýí ˜(¢z¹FÆDš$ÉH 2BB‘$IIuÒ¶q`Æ1Õî±Óˆæ7n4Ù³f6q˜8ïÀq|×ÕB vöcó*×£‘Å|§Ôsn1Ç ýVޏäÖ(a†S¢†4蟄àÝÇVcà ¦|> ûÛ?k­Éˆfý7ò5a¯ÃÁ»™Ó+ +Å»gg¯ãVM11ŦÜxzÝœÄrplêˆxÚü{!€„æµÈÛ³7Öó¿a¯µ÷øîáɧ‡Éäú̽CÕÀ½„£O›êŸB¨ðùƒÖz¸öðô4FFÚZ³Z_Zæìµç ÿmÌãgY#j±]6±¶}Ú•%1g€î𥷜*l¹󻎓_G¡ðç ]pÜî¦Ûbcm æ«=]ÖsðËûRL¹‘Áû‹n\/‘:âHç±ìjo;[*kJ´Íí ÇÇYÏu:aGÃ}Ì’ºµ"p˱¢ŒH ï÷¶‚W²)û*1}ŠÑmALcSw@ày—ø'c±¡èƒ©³NN[z#¥ЖZZ‰:I“÷Ío<L» (dñŒ® ˺lüÜ»›÷˜‹6Ýþ~ŒÑÎkÙÛö=sÏûnmyðÓŒíf]Kd{Öf*qßÆÌ€ævµGn5:"Û vÍN'k)ܸmPVUw –Îĵ\V¬ "å^6ИÓcÞ’¶6ЛOl •+,)W{4æ]äÔàÐH Í6“LI· E¡3m9+[»ÛåÌžHR7ʉk¿4²%&äÁ)ÈŒƒî›·iÅÇ-õˆÝÁó¤3cê]î >ó ¨P§"DxÔã"®ÊFtDÁ3ÌÊjM寓²,R+‹H%ܶh í}—-Õ^÷¢Ù›âçwN¬g¯ˆõøá®a Á ØÝÆã M lçOq³{7c]8ܬàÝ¡¦š|±ƒ1ŒZl(çŸlÙzùݼ<Ìm£ï|ÿ¸îî'Ðõs<>ÞâÚ'è%›m®’í—gŸ‰Ð™k·ýÙ—“¨â ºøQYÛŽm…„²n(RHêyžlÄeñ|‹x½9e€cé¸uÉ…qûÿ³`€ˆCÖ#C’ H’kú>t®7_ϯJú|¯ n{×§ãeˆùøwÃÜÛ€lô»BÞXÏ ‰ ãÖÅ2²À65²‚ÖaQÑkHo;î ö:#CcMVxIUqÄû <,«ÔÙÖP4ˆªSoíáæ;˽2Ù4 ++ZóBŠ^I@CvåàRŸ¹°™h­îÝu/ÝòéÊ!÷-ª±bŸ«¤u â›–< ;øN‰ep¶´ÈÂÞ_,Z5Œ@ ºZÍ8NI¢÷åËÌÜÞ´›1ÄÙkà=™Y°×Xõð ¡Ñ0æsÄJø°UjQWmƒ¾”‰NK 'c¥Eú ü£(¯Ñ†‰gºÀ!ˆµBžXZ&UÉá#ˆ¤„Çã ý¯r€°_“=Ùڙ̬ 6‰kLà!¬$9éE‰ Çí¼`@Óëå…'” ñøvBQAƒÈô3Œ]±)œ·@J m-¨fìùö¹¶ªe´›Š)IOoK£ÈEý óØ˜Ø‚2h\˜ÑÆŒ—Ì£—N´ZèØbê2›È)œZ¶P@WäRW/+ب¨jNzݘh{ñ ,M¤äc|U®µúô;ù{Ÿ§"J¾Þnþ’²±#ì9^þÁäèQº±exÞÁ]ÿ(v4!IÒP#m à2+ ü¿‚` U9€m’„Ñ×îvê;µèô½mýcÒãÇ>é|±{¡Ñ~·§ly±ä{«qž¶Ëžä3·ƒˆs<8¾'âgË·yENŽ3Æù7`ĤílêWЧ “pTaL€Á `о 2œÍØØ?%· 9Ž­[+]êÚþÏï­¹Ò<4뀢§I!Æ@µ%ÞF_ t©ÆkNd>¬ƒÃ3…åâö™4îCÙçïͺ2´~‹ùÉ…‡;´ž’†p 6xp‹ÅÔŽafŠeícÝç\8þÎvlE5s«ºzXÕXIXÔ ˆä)ЍðJûÅàëVw‹1O“#gšfˆOÅL3€>2>m‡LO¥ ?!Ëbg2Å{-@䃑E Ñ"¹­_7áJMRaí3z Mœš»N²Ú‡’*"¢L UJ·uõ«àLÕ㉃1±´F |BŒ&PT ÝLBð¨œ"„é¬Úˆ˜È|f.l†©Gp€,S¥j_ŠC)ô˜ýˆ€j“@±¯óø@´Ð˜Nf‡åxp³;§Íprz\}~÷|­7¯mQ^C+|‚ÍËÆ_gÖæG5ÍÅ•{-Tú{\§¡ƒ>†Ï6 àSùHÄÜ~ß¶v6ô¿¹6£VûD™HþƒO¦ôVn<í*ޝ>øž¼óë=µ{¾µ¯†Sì%È!`\4½PÆ`,‚á!…m‹ ÝQ H"¢L¿ž=–‰>YED€>Np tÞ,¤”¤× %ã²ÛÌ=y¥5@€˜Î 0ÆhïÄ4U&Mò¤(ÆQƤ€Î‘P Æa ²â8¨R"?ETRû0ùÞ¼Õe¦U¯“†´çÊS(`DM$†“JLð]Ï;ˆso3TqVâ|4ø\ìßN€ Y“º°=et¨ÏÒ‚•¤ÚEª®²Ë•šS8PA/+Ò„â…ÍyiHSÈÛN¨&ÞañÌôilÞ½06±¯"O @¯€Ì4fí¨Ýmf#6j,Ê' ‚, þ¦~ü¶$Ödèkxz¶Û­ßº‡nZLh||‡O”wë1Îk Ç€²o/lôóèB´‚¾ ~:Õž "/c§Â|qj<âá²a¾d γߢ–B%œÍ’¤’ÆdoVº†‚ XkÜ©¡$a1+EP•+2 b° –8bÞòŸ-žö£¹¬‚ш˜9 §% È4¿[O»ž»N3 à3Iõ¯.¶ÔÁ–‰(gm›ô ò µT·7kýÎ[´èÆñAñ·b}ÎÃåës8¶umϸÄmxÍY‚@0ª€4e¤÷ÊIžü+›5VØR¯àárÚ¾JSh-›”(tAVŸªö¯áö8–ò¸à4P\C<)„L¾0‚„ Àpƒ~Â"”ÌöâqG¢7×·µÎ˜G4ÄWP'ñ÷ÜR"À`‰Xâ$…`¦÷ò£¦©ô(LÇáwú—3Ôüó>E‘¹‚9f€Æˆ ° 2†™=3,šJ41”ëÕqP EÂT•(ÆN'=õmDª³!:Z‘D0¬fäHÀYÕÓŒL+U ¿Íö“ÎXÄm¸Âi¹`@ €²NÂ#ŽÖp8vêÁ<¼µ z±,Šó®­u¶fâºÌK<ÙFÌdŽ|ˆÚ²ÞíÎJR‰YÞƒ;'lÓ@,²{” ªáë0 @ZäQî‹Fqx¼bé¶æˆ  „Ç'Òˆ ˆ„ÄÆ&Q=kÌ\™ÛUåJÝdÈ÷z‚äÙ30L@˜„ÔÞ\Ù¹™…3"C»ö w¡ºlOPBA}“-›ùú¹zgc+·ãü=NúnæLìL™*WÂLLô]YCšÉu #' " 44†DL¦a3Ñ–¸š^n5]”£” Å „Qdèš±xF,#š;Q›¢tXâkº“,¤b™xÌ_'÷¬hz3h”‡eð]ïq׈/ ‚ZLnª…N—T§‘zl'z‘ô ÃŽa Mâq1”. Ì@ °Âuj“ר°Ë@¨>³y"!°CŒÐÓK6DʰÏÙŒ6Xà‰ïxĸ(U|eH•&Yåëëbï¶’À) y µÖ µÐDQLHŒ£·‹”º5ï N,3ºÀÏ7tŠ h F¨5Dö'wK&¦øDÆWN]*'¾ÊÆb®`°]ÌŽN=}†Ò÷”m÷<}.‡KÌ^ôä¾®J\ø4³Ï G?fV5ûÕúx¡]ìDz‰Z«0d|ºöгÄBhê-úh€j ÀâBM C¡áq0;kɉáï¨{Ä¿m0›‘ Ú+ï´ôû{ÓÁ]®Æ]uêNdÛlá»Ýñc’þ<¢BçÕ˜-9/4Æo‚³“!é(8A`õžš*Ú{"ÆöQ®BUB %ÌA/}îò{.½˜g1¥dPæŠÌò úÀ›ÔØÍ¿£5bq‘l†W 1Œ‘Eîìõ'Y†‹U³DDp}»Ýê®Õè8=g[ä´ío¬Œƒ¤ò¯A¯¿)ˆÚÉÔ\.ÀP"0<Æêf¿ÉxÄ\ï4 ú…‰„”£ ©âP[#¨G‰ iï eǙǬ…òS ÆÐ7Hƒîv94¨j“ÊFt!%¦0 ¬>•ÎBõg”†7œL÷ƒ˜ ÙPÚûî~×¶™>1eñêÑ¢‘I y³zÁØé6ø€¸kǘ[Éé® Bã< ’urÓXî7…š¬€çU?&n-8²¾~½u–û튑g:S ‹Þ,8΢@9³‡'j¬J „Õªò bȱðx7³…ùì¸éC'ÒùÕg¶0iÁƒž«½šáåTªïÕ„ÄN(D+É’koŒ¦Ûõrs:µÛjBV7ÐPv˜y³Û‰ÁŠã·KçûÏ´—1|ŸmÕ£ñp> \9º¨—Ÿòiä1;/ú»ú5š"W£¼ãð·ûøBAŒ$$Û #D¦ò†À`4›8ü^\’!&Ù¿ÂÚê½DÓ¿¹Ñávôì]!£'gW¾ÆT ²í -Ü•ÓÌIˆÆÛ¥£… ‘i\ÈÊFYeFSµ…‹šè"eCâ5ZüïòlZäO ¤Jä€ F”…‚‚Îó½‰ÊšækH畦çŸëF~“à;à0ÏÖXÏZ` „ä*!²þtQ0¯”ÂrÈ€"”ë)J$ÄOCuÛŽ9|oKÒ=M>~¶áŒëÜPôDù” ¯H¯OÂRqdm¹Ñ8-9J}ß}úô­b¤Ê… i4"³QWš:>; ° þc"ØdÎùs¢Ú-"ø˜J!O›ÌW³ÉN§Ùsh=îRºÞ6/‹†ýg&äv&ÞCTÃÑéŒãXØÒp˜×LÁž¼Y$,!¥8qVúEì @Ö’@ 8%^éÐ1‹$8èpÉèQäÇnjoÈgŸO§2ˆ}b‡ ¡¥s[×íä k\ÇÀz%PLmQ¾êõØÁ^1 tKÂÙ‘°&LÇ/=¦fõÞÀ V,3ÈÆÕ6|é|!PÃ3lœ?ž$ؖىҮ¬ƒjjV?›]Ëa6þÊV\~¾wÒ¦¸Éà@Â9en÷ºÙ`ìD=¡ÌB`0•4쾃KíèÑVxánšçG-u«£Æ·ÜÕkœúÝÐf–$˜IGC@qûFÞ.M+ñ?U¡|ƒ@€f™s(q€èYÆÃï/n^/£Ó§·Ð&ÍÖ:hqcü­Õ¤˜´ËiÓ&??Cù“±bhËm4Ù'F[hû§½è«‘BtÅöÍb,9‚7‰1{F¾´ƒô3dI)‰0“á=GÞ÷+xÝ]¼ûˆµ‹\d 3×Ĭ$Êó%7ðX&á’ãB=9ÀüÑø5T)r¸/g4ã<ü¡H¿_ŒRÝÔ½b‰¬ILÀå-.N€–E̶ÞRªªp)*&€«™¦.Œ­›€ÑçH&)€fw@ßUcm£ÃŽñl"µ 2áÌÑ`N´£³£+åW¢® Ã'f¢R#ÖÌ¥*3‘)aa¦5—¥aâŽþ—õpßö,ÌœfïŒ8üÚèõÊçÈÃÔ|êUèŹ%ÜŒºU±w4æ%ëæO@¤§§%ö=î­YÔR4­ ¬+œ\Ì¥ÌîQ¥ä’ÛÂŬT‘&ýËåo´ÃîJ…«äÎ'= ­™mÄó{™åѯëÚ¾FŸPù?ƒÀ¹âßâö\Æ?%»Y»M±Êñ1¸÷¸·ãn8&¼=Ì t¸'xÀ'ç¾"™b7³ôÓ1k&9§Ea¤j F°uÓµw"‹aC|£Ãzô•u“‡á¬mºcä Ã@ñTú5¿¿U;Æ…7¤À6bƒ,]l±†SÌ´Uæ+qeY=„Z3èTRðŽ D&2÷ì‰ æŒ`醺ëQfiªJ/iÖ yl¥AÓ[›g'µHÅöœïtJS À¶‹".J \Z38ù#‚7$•s9ø“9‹ê™ÿËIÞFv7J£ú ‡i†Ú²°4Ah±Ûï8•EÑ…Á¬k ¨h'¯a›6f÷gF¥Ï“>3Õx¼ÆwT€õs*<æsó·×C_JÌqX3NYX;tâª|6Od;…Y÷#jÇÔƒšÉ”úñ5y“—¹.PÈ÷A¢%¡ØFXìÂ’[xïŸ×!’;)éjªg³=8ÕÆ÷ö PÓ›Œxv­T=—½CK2%*!dq€Ú› ¡`»âùt+sèöýS‡€êìëxÇl?ñº9q öÊGù®}eqæõu¼™/Z6úW2ü Áëîѳ°†/b¼±Ã_p×ø•YÌœ•Æ0‹ÓŽõ¢+yÌÝÌ‘X3¥éahrÕÛYò-;­éÏÕq=#ó¾×_Êá”úÖz¾ÂÓNî.\±ü ¢eO¶€}°~Á‘‹—QN–‹Cþ,úqQ$‚›Ü–¶KÞ¦ó¦¶Z-HôN;8ÙÚ3«qH"x”n&®Q!šhKq<DZ£Gl猯DÔŠCHœwÀX*œÇŠGÂhçîõ¥²Pp’!X¶²1~3°ðÃuA±÷Ù<” fµˆ½!9DbX@ú–ƒÅø÷›FUÌÖ€ô¾Üqy ¦¼å‰fEÀc˜bÝ\ÊoZ¥ká¢d<ºm°ùlòø’@kߌ¤HX Hø .ýÜ‹±©úZwÇCHÂL÷Ì`øD·Ža«ËYíÝÓÌ$:bÑ»5ó#ªÐ¯~v‹È]Žé7ŽÑ¥~b ¡ >eÄŸ/q¯q«ï=&J°)4*¨»J§ˆ##OÆ2åÜÓQI ôª•$¨*mZAuÌÌôänÊajö%07… [!PlÖÐ0€Y/:ÄÙËwÜ^XÈ%0¸ÚäË)"Ñ)ZÛðüù8³ÎòòǤT4˜°ävF-^lÜ«/ýƒû—WÐ!ØJÛÝo0Õ…W·ÛShršÆùK3¼r#q`.çp³M”©f´–‹IØ[,¬GÀ`i M(š=Lß{Ü×vÀ%/ñvR<í—ç ã„Â@= WÀÂ_ ª¡` ˜¯ c߈eN*º#œ»l„9k…c!éÄ.×! t#æZO=m\hw g0ðÜCœT`³ ! †š]-Û„]h$¥)è(ƒ ÆR´JصÊO¼Òh àæ¦\¾ :nËo2xÒÚž(Á}Æ_åhA¸ëÙA1Êbc‹fÈmxS # àòÞË—ªúÕ‰’3e+¿Äª¹Æ¡r@(µe&4€Í—ñ…e†¬)>ØhØ{ÞZFÊoLlk*?± °\…îÔn‘r àw¨€"Ÿ7±Ã޹ÃÅဦ4uí*õçE7•a„L8¢ ­ˆ]RÒ»çŒPà* YVáñ\DJzÜçt z(mÚn?Ñ¥² “½¹(¸bÁ¸e@ªá”|@a@ì L'ÉIÀP1oæüü*Ngžy»œÌ½t´ëꣿž´wh4Ä f\ Ô£#ÑÒ¸ûṲ̈Òò<-µô 9 þs$K¦¸²È˜„Øå´•Æê*7œyé'ŠrvìÒ(ÈFr2=ñt$Ä¡ !¢mÄùMcJq %`{"…Η¬§_,€W@Ñ¿+ú…K(¥ÏÕÖTHœf÷ÂçòéûK+’çæÇ©+ÍhL;Ü›‹Íuɼ 즀.å\ØÀåå-ø¢¦ÊWÌ2ÛtÇHÔuäyÍ6mžyagp²PÚ™¦0kœS-füÛoF´ºÐU‚Ê&Hûà¡?þl ¶ío •@m¬ÃX %V;÷k±Ñš…Àã}k\ò3G¡Ðp‰Ø`ǃi²e{4̬}g5€&L†„ uÄ(NšÒÜ*/„f”åHA‡iãÙ!: é-|çTÓÂÒÇ9nÙ­€VFh(b#I`„(×!ÅÌ©ËAA¬ú Ñ(:ÒqŠIÒØ1ÞÇ¢-,¹@$ÝÓ[(¤ÅÀ6$‘Õˆ7[dzdû†Ý;g•œ…©?EFŠ â°Æ=ˆI´~qœO+5ñí J—^K‰š§6Èd­rGêa +‰€5XGšD§ñ Lœƺǭ—ìûj®É@p¼¼fH †‰+¬X$5ÑæÐÔy:(ƒ¦ô¼À¿qæÓ%üžñ¡/,ô¬=¶½[KLûÛ›Ë5ÊX8ÐÔÊ®ß'ø¯8œ¤¤i6“ËåJ¹ 'ÏÑ‚G¶L¿»€ÎO.yÔê÷ü¼\Žß†µx’i‚™‘ȱ™"&Œd£&! ûN„…TÖ¹&îr8v— NwÛ*öË â÷G¾ç©ô#¯éùù;´í·¾áÆz­Üná‡'ÐŽAäy2ÁmˆÞò£üþ ´-FzzVL³A ¹l`Ð*‡•É$:×NR—€.Asô3Öñ$£éÓ\EÇNÖ›á–gÒE9ͼËF©Ã&{ ¯JDm  ­íö‚šA ¬½jÝK‚±£jÑõ ÌšçPž«µÔ-½Ÿ›ÛHÍÖ’A!Α5Ã%]""1/÷÷RØ_MÕS^Ò¦†q¢„³Àa…¯*~?§Ö•lpáI CÆ#kyS«¼ûY¶þFËEçIŒ£¹Î›—¢g#DMSÔ‚UÍ#1œi“²‰Dzkg¦<‘‰ÁÜ÷Hw)°b°zx6eiL*Ì,½M*Þ®}Å”é×;*[im=L°7³‹!I(pÚ‰¼s0€È'00±iÒ]ôï_Ź›6çZXÕ†õèMo]Xl²‰ªcßZ L¼©ã.ŸÌ‡ÊÖøS\$î]Ù­Å$BV†å‚T¥*°”BXj¤ †P)h*×ÇøafªŸÎ«Ü#%¯q°VT”±/p*ÕXÃØ°\øóÈ2¸cÁÓ^¦XéîaŒ3+ D*âÔTb¹(€ `ž¯;ˆÿðž…˺Â.„½ª†O²OvöÇÊ×D1 oðR 4A,¤KxMFþ3º˜°Õ,µøKBºšÖää_éÚɲæŸá„ª€ü{¼–öƒÐšóI¯&áNò¥p1·&;ú9uJÀšLß0>$+^Mm /fþö‰|ÛÒ±­ fñÙPF}Œ‹ûë]dxƒ7u›¡©ª„žÆ€ÃXèkáôV¨ÍÛ¢:™ªNð¿¶RÊ`AÃÎ!»T.aõê6÷+ °‹pÉAp’£a+ œ,ËhëÝIgˬŠx è—ñ£„3¡}~/ Ì¿ÚÎÖº·MQÞϧ—ÈášÈâ%ž ’:)^Æ{~ÏâÒ¹ÅÆá‰­X!m%þ4©@˜ÒlBŒJ†<` ÀÌúŸy?¬ÃÂϦ©ÛouÌJ¸HK›²)Ì¡SC!b^™·¦RFŸk‘¾ß©¼ÞæF¹jñó»C—‹«wP3wmPk\¢ÄÀ9Êpt½vÄ}±!^B.Œ ä9bဂÀ’Æ·8vÍ 7Ë~ù;c=,`[TÛ9Ô!³:½è Ÿ&$©¤ÒS‡ ×Üb‡»É±*» ðœciÓÖ»ÇS@d }Ò Tæ¶šáÑ@“€ÀˆÌ2¶¥ƒe@“Q$PIGvåÈ¢fS ™œ€ëš‡^4/Ò§–¥ÇEF€Ü$ Ô-Tà )nšû %Ž¿ƒè¨HHÍã‹P¤rËb©ÝžT„ÕJ¼Å³…7MÉÊ‹ýH­²8 uް;Lt¼ٗ:äe} €xUV¸¨%H9Ö0Û¹[s“g×R™+bYÓq±_hPSí‚Ø G±Ï%  À0VR„z1-2”¹®Lµ2Øã†ØÁçDòœŒ€#‘È“¬£œLJY,ŠÛw'¹V0Í0éƒä9ã8:„J¤û D \†> yóx+:óƒ‘ܼP5GdÔË(î9•/€Ï±BVÊ‹ÈDÆ É=ÞS” !Uô™B ]ø ‹À4š¢äS£Ì¤-‹mù3$H0Ò–Nmn±×Çäc‹ÞÚÓã ‡ŒG¾YgwŠ€LÎòãÔyrê§—“Ý®ø%¾×OUK”d€Ckàæoõó÷;‚<­1 YR ÀPÇB8LI'ÛØ\–&f]ír&—1ã'×i£l’ÃÔhTöVKŽG%v„ÉÎæ¡K <;©¤îé ! ½Íó#…@=Á ”3*N9c hpöŒëÔÀ Id¼¦)&..· 07tUUÕkðÖßµQîãš& 7¾L…Aë $SE†©°ê–OVô“6;{òã Ä@ÙJä„­6([E0¿[ÙI%‘ï3jæ% Æn%á~6Aæ0•OòæM‰€Å•…¸„³ß4úHŒ\Aè•0ðk˜üœØÀ{ðŽ »Iµ>`¥LÌ.yÍ{ë0IÉä,,2í M7Yçffzâ:^m¶ò²b+Ý|޳–ãqhŽžðãâŒìŽ–í@ÇQÐBš8ú¹²Ö.æÂËÒP:Hyvr kO{°®}Öubíiwæ”.-uM\¯3s¸¶òž—ìiãï0vˆyÔí„3˜0uÊiÜ”Éå;„¹2KŒˆ¸tÛ7uBŒ/˜¸Y”™!øf=èÒ¶LS=œ£pL%n‘æw{[ f¿ u‡,ߢӮ¬L"ï)ª2¸WMìF{ÍWŽV€pùü4m˜b3˜€†î¢B·4Öî M'}Lù'Åò)û‹Ž´ããÇt'’fl~ìf²k¤FmÓ—Š}NuI솼’™‰áC¨ g/éA€€ùBf=Q4­‹=¡«!Øêòk?mÝ죕Ö4†3‰M{ltkÁÁ „ÆZ*cݤ¤£gåU±¹"¾ ºt+² ‘á ¤äHQrÏ®¢•”øZ?¼.®ýâæÈ4*u‡tDZb†Sv Ìëéí÷me„ìè“OAºØÃ^F¬Îþ `쬊‡j»årG#Àé]‰ÍE0” 80T.a„ÈœÝKrx–ù{zóYÌÎ/-Ô—s]Pµ‰{( ( Ì=\Ñìùšv°¦8@d,Z%C †W´+]+y7Ö=E½˜¡Þj©'WÝï¤ÿâ$ӸƜ°ËÅÍ›8iJ‡S#Øõw»þ^ªÍÐ¥©mÆ5Ù¦êïµÜÁ*E­¡>»Ð²Ÿse^í¦ié™9d­`ú_–ÐŽ‚ÔæÙÁE5>!AœCÚkuQ,yû¨GwÓÑÅÙ˜h¦:4„¢“c=>\w±-”_ÔÊE£a™¿ .Ø2¥ŒÆóiµFH^%‹Ø@_µ…<,\ ‘)GQuŒhç0Ì?EuO¸J"aƒ+XìSĸsB‹ûzÛ)˜Œ×[™:±ÿ$dL³ÃF·À 00³ÑD ¡cɾ¨­>KH¶õŽÃXÌÉdÞ Õ£#•bÜ"A QýÞ+øe%(ÂýHØ57¤Ý6Ћ‘’ B+VL"AÙhÞ Wmù‰)LîiD*ió§êûžÌºÛÝòõ'rRã•ÆX1€ˆ@‚Llj 5«÷Un×Àÿ>Ç ³gßn¤X䃷dÆ$•*ã„4rÉÞ1 ÜbSÖ’&'ÌdÝ4c×jm8s$8«Vº?2]9QŽ9ÉêµYd¥óõ¨<#>#LFCu×—^…oÞÚ˜nû—Ö¨nPÊG€ µLgËSE§Ö ïu¢ŸãÓé°iöÞ.¡"瓯¦O…¸7µ€/?‚sÓÝî‹ðÆÃâuhãÕ¥øLu4‡SéÐÚ m?»¦)Y •"œSK©Ð“¯#ÆC‚Xä49E@èF²$ô[¸kÈ$ZíªË*zoð¸qø‰Í(ÎþÉÎûºÄÀÏQøŠ2X¦š‚rPqß8"LNN}Í ôw7ãß•í!(+ßós*ùûk_Ò„IÄ`D2vúÆ 2gѹupd\Án3whE5©ÒãÜFå(Œ^ƒþZö§£à7´jBm7õut«ÚòJXŸgèPbÛfêéí ”ÁO¡Y¤)†˜«Ê€ÏÎHføÕ5þâ£s™ÏÂnHñ‰šÏÆVj é^m­î5š—YN#`Ôuýó~!Èäi–2ó”¨>iA‡ºqJŠºÖOD5èžÆ¥h ­ÆDæ9Z–[☽G x')¯:«2’ÃÞâ­ÎÌÖ=f'™“–‡ž™ØÏ®½VP ˜Àbf@EÛå¸yCæž0ù:p¾2ÚÅnY2â<ø¾}Õ+ZnÍ P×Õ2¡ƒq(gNË÷Üê%‡F. þ'©BÚê8*RbGØGÐ]\Ìñ°(RêkÔtDRQ ùšµ8€·t8.ÌN Û/B›‹`Jªì¬ÇéøÜ{-ÌÔƒÓ¼-EÉÌ7ÅKÊKA7FÉHň‡à³O|582Ï“ã]C»è\;76d€yÆÙ2kòN¥Þ°*Ù q]ÊÙD€,ÞÏÕO4©õŽ`$ÄAsbA[+ívŽF¯R"Ê=_Û°]ìS4#z`eý¯Qïx—ýþç‡)‘±cÈqÀm ™Š‰ ``…¹ápb4Dù¬Ô¿`b"dy–4$žP}K ¤š#î‹@O„“”Ý_³h¤@–Í,n¾0_ÜÅA4£¶&yÅ2ì,¬TóÜ*=R8ãxt¤ŽP¨:„.¼{»Ò‡5XkÌ—›«r†[ïî8fÉÑ,¹€T?’¡÷ ­6™Åí†bÈK·2rYX´‚²úÆŸlÁ‰ôÙ„*Ï7fe*k§Îí6Æ|„¹K×0z$<±¾^qÒC2TvO†‹VlÔ|t¶BÎÅRîöûRü,ìPûT޲ì5ƒŸç2DœF‹S¨çx›8Ü7ýªM˜ŸÝx}}[µ}?®x‡>[cŠ$O]øµ²b£#1±¯9™º×]?©|ð †è?o²óGC¼@ J[¹-ж§ÂSlÌö!!¤i¦Ís_g9­ÌÔOÒ} #-)aΑ ôñºh±D2éa΋Zf4ñØÊ…*!ÞÖÜÞð·6”Àl»ÿˆnãÆð•¾}¥Žr³Li´¥­í~–¡Dæ8|{æF~øøÇC"=å…:po):lÜRˆø‡&Ë<œ¯/’`I·ÊǦMÕZ¿{ë–@M=ªóôyXÜ3 'vpP5r2NgG";«m£Õj娰žÞ„Ü)¨¢1ÈaÉ Æ wB‰èüï"޶Úi~lÏÄØI ”_¶yäJý‘¯æW½2UŽ)¡+6:p–2>ðœü/ÙÓ–aÅ<8ü z¼ OÌìfìv…Zæ½)WïI6O:ÛEI„”´Š$môÝæÂ›€^@• ‘^Zsšèrw!®ä&|âkp²’énfŠ4Ìq@à öèÏl­ûýµÙn«zpÞánôÞG™H[޾AœZë%k Ô„¢A x%r®¼Ìuþ/Aåü-l)øgï;¾fÝìÐ÷ùÌÇC ÜÕ‹µIl>9vÄâ6BSXf>º]Q(`±Lu7Œˆª ×h·'¶*£~ ëÔ`~ð2ËqYÞéʲ},|™‹xÇ”[3"ظ,êá¡.þ² P·/ÉìCñ M­hÍgJ ”_)ZFN¬€ìÕBX>°°ˆÃDM1<‡EÛ…ºÊY dÙœOÖ’µÕŽ#°‰vÑï+½;Î$òm6YŽ°Í‘NëNe#_!r‹½æM–9Q~Qh–®X! Ëá•¡òz£®K=ÍÔÉÏ•·§óbÓ jO–d"c¹Õ*ûAµ]A–ys„° ćnOMdÞ†óþÚ×mæ©Z«Sq1 D¤c£?HÓ|‰"E ðš"LHןJP%à$¾ïÄäwò£¾Æ#]ï¼P õÍ<@Õ¾ÄARRv'~çSÃ#—©TÅf%g7\—×15å%K,b&É,¦ ¨e W½8˹s‰6en`ñ]rÔzó§*«¶GóµÚLô¯u¦"ئbóqàÓœßnò(Ã\¬¸ ’y7•»› y>}›)$·ñ§Ç(($)DÃÞEǦ¾ŠlöÇ~ºôBð¸)€æ=Xrœ]TP¶º¡gaΆ\ÛÛŠwÞêŽ6Ó‹˜‹‰‰(ˆM€ÄÒ"!þ$$Dø´tû¡_ÄÇF^–,"•=#ïyd4µ p"Õ$Ïý`KPë¢S4ájf4ön¡)n¹Ð©Ï4H^ +‹T˜ò&à¼kçzeMU`à©oµGšmÃ1!Höa2Å̬⌱uòf@«°96sr×X°YÁ¿ô$ë*¿9õo‡†×Ã4óŠ+ ;éàp³yÌ=ÓŒm ‡Y¡ãåHX/Ñ€¼?OmÄZœJ@[؆Ðóø€T¾aÜv¶(GŠnëc|ÃÉ ‡lL è¢d M„ûy{þašûµÕƤ…KŸ:ÆHDªf6XV˜j °YÜÇï}üøHz†:®ØŸ=º]Ôéb:cÀÍ¿‰æÌ‚Ìu½®Äòúw¬6ûýÕúU%^Œdó•Öî=*~O¸,¤[Ü^ç5€ø'‘KÛ-Š“ØHÏ-sA'¬^È‹±«µŽìúªÚX(ªíXíÎû­žÚñ!–¶ª†M(-Ï둪$q öMAÿEž¿' 2åÙ™Q·ö‚8²®7e‰¤>( ¡fåÖÖáÞzøNI1Ÿþ4³¹û6 ‰ 8F—icW¶îîU¿ —é Háâ‘5®”­˜•\¸ì>†Ž­ä•™ž0ãP{ýaÍp€¯yîUÇHɹZøîæ °‡°*¸!ž ‘DXNÈ2«Ò8好d­±äTðÈÒD‰NT՞꺺 •{ÁßAؾ€q„°±$q8WñŸÚZÚùJVøéÊÔ§§³ÓVŽÐõÓZ÷d—áLyõœ¥¤‹›©¯í?¡­ß//ռ촇7‘á·šÄaÕQD†U7ž:×Ôœ‡_®'¥úÆ|hÙ±»§Å¶9OFÃyžsÀqጙžÅµ8dtÀL‘†wr¸¦u ðÃmK÷NBH²ylÈžÒÁý1ô[ŽfPBk3¦5ð %ã^x3ïÛ¹Á(É÷SéB›²‡…ÖKŠÀÈ9=ŽºôÄ3°÷êbþÄO+ÿ`M£²€¯D«ôªZß¡–$–.”ÎJžŠü#‘±Ú„§ ¯E[ÙH’²%jÑ&%qìhû‰Ôo{UwÈ,µÑÈ2F5}m ¹ b(˜Ñ™ç~Ëûª»‹Yèæër3Y®4Mg ±–zîe°š;ö¡¼Þ« 5ãÂɺP@R*@ÌÈg1U}…âäXu¡vÌÂjÆ|–ö2ôD1š9Fõ\Îw¬`Ô‹Ö<Φ)U°y”3¸u“…š"›[Dî È55›S*˜ HÈ('›tÖEÈ(@KNiî„›ÃmªK’»Õm2¼G.&ÅðýdU£†wˉ ·'d[³êíλ‹VJe¤d²žÔs¦ðóVO Nðì1¶’º¬ €ÈˆóÀ‚wJ6„蔕nËZ‹Âä–1P ëTkgFV6ýÙÂèè&¶³‘1˜îR¿+uÀC0 ·ÂÞ/5VÏ—G6øxçO‹î}³ýîPSÃ*½ë\òXBtïØ`›*¹öŽM¥/¶¡³°7‡ƮՔ(Mez8x$ JOg÷ÛoÏ[~ÖÌ‘²žÄS‹$&µEF¦ÚŸXØ<Ã{ 5ÉÜÊßÙ<$1JG¬ˆïÃ5¬ÔYµ”׆ºC÷éX@kß±C,Íʨô,¯½ç;C­1Û²Gµ†'×cê|_—ZIıP®MäI ´g@cÃé%}Nðk£Ù%8ˆçYj‡=*/9­Ïv}Psé£bI¸,D…®¡*Ù­•¤iYMG0/ܬ#yã¡B’Ó­ÀÈvÛ£§\W}ÌnNB_i'@h [·ÊÇáÁ‡Y:Ãÿ&{é‘L-<«²"÷ÙKmÛ¯ÉAû"£~WûÉJ^ò^7Lbx¿Mvƒ‰¾üŒÍÊœ+¶0úÀ¥®éÆ´gÌ_ÃF`ØjSw@ªô8ÞÒZ¬{ÌLji};«#{@ÞUo-üçìMûm‚WÃÏ‘™-Ø®ï]N‘S:t;Z“TÒóí­sP©âÊæ)Eç±øÛ#<ëÕ¿6¦ Y4Ì£Ï %Uå„ÂДg¢qÊüÙKƒÓ-^ojáAßÓŒý[šx6y:Öö†'M*æ†Ñhwk̳äI0•š,†¬€’‚Ý/Š{×øü_áÿ[äö9\GÑ×ú&nwP§©·öp߬cë@`<ù<·‡apócsËB_Ažï©üŽ£ðkÜ;€ ÐX›”{& Ì?·•ãPhßÍõ4±ÛgE§U®l…ä•$ ÄÝlÝyØàêôúºÓg¶tÚû=uÐSÄcógAÆêw‹Ì×eAšˆñØdŸÚÌêü^=¹SÓžqMѸ2÷f-U/žø €÷|SèÛ •kžáë2úÛqæÕ( ú+^o¡áx³.8x÷ßnBya-µI†“'YµÉÁ.%f\“—+lùH$§œZ(h_7‚C ¨"ææêD+¬5ÃúZOw¯©G¿øjtÎþS‚C{°DÈU)§u9Ĥ+TcòåÇA‚ïi˜0@²a_%9^Ùtéíä{¨z_• ‹šrúÕ'…·Màîð;"FQá‹»Âh=C:eˆR—¿ÚšÛ…÷0eaÖrž1 {T³­ xº7éAž‚6ÕMa’#QT]rÏÖ°>•Á}þÿc«æÙIÏ ï£]3Fe²S¿Â£ì¶{‰(·ªþ ªZk @Q¼T( ÉS;^:_kG{gÞàålp£*¢Ð¶PÍéœÈšø†ý<û‘eÌÌ{Jà8ƒ-±l¿SyÇÇQÂXí:æÂViqôwæðVwJY;ÿ}E±&INâæO ÈJôQçZe³ÎÀ—Žå‰g†;bH“ByÖµ)Ý"*N™Ì¯)H&œO úËJó·ž)!äedepæÖ`Á³–è[£*ÖLz•(¡óá„ÜòŒ,¸yÓ‹DZ° ½Ka¦êIQy×pí%/¦q-p³ë–Ò„'ûûžµIÙµæv¹ÈHZícI´v.ÜF%mKÉÄÜKˆqÿçÞw9Rl~³Ü¯*K ò…lœ9Ó- §Íír•‡1ä°EËŸmù'0d)r×ñä´X‘Î0¨ù)pš0L˜¢†—6#«ä‚¦/˵Ná+D)\Øy׎íÝvþlm©I׳.GAIÕŒ[BS»úøÇÕn-¼Ù[½ßp¦'X8(Ÿ_¤Þ'ZûŽ“ñ›& å³*r9]u_6'i ã• ÒËøìTÕøÚêí͈œÙîk¦#k¬-rƃ…Å&•g¾$‘¤Ñhy'؉°¹’® —c T…GOŒÉ½Jh´0”ƒÃµ‹Ÿ4‰°µsk䞊«Â· gÈðuBM"2eÓ„ì3/!þ:J¥êHñŠºvy°|ˆÆ…·Ã®·J‹¹áéªøvܪì~'`…âB(‚Ÿ™pH–w[‰0‹”ŽØ¼Î%º¤Ö˜°öDB7†ãiˆÞ·öon¹=" Ë‚@ ߪ5{²hœá i”(vïŸë$mK/#¥X gLN¼øåuoéûùfÁ”ÄΘ¸ _ÝKµEÑŠ6Dã;SQõó+Ê=žê‹œ¾/¼ €"‡ÏÜV½+Z¡•F£3ÎwˆåÓ÷Ù/t^‹§Çh¢×h.˜“HKÅI{} ̹g¡ˆî.¸9»]lÍ™Õ\›E½öÊ´9uý*ü]öêŽÏš]1¤†4€êÑ^E"#g]*2÷N’¢©k‰D£‘UÅ(Uåq-n~4“®Oõ܇Ó.*ýɹµ»Î~z=;Ó-ïR¾üÌ—ÃõÒŒwldj7Øæ i(@­´áFVWkuFÓñšŸÒ÷`{“4μ/ï—„+BTB;ʱ»ûÆ+?^  %4£êvko4vVî!'ÕÛT6 ®×H+š9Vú=1irg“8bS¯2n¹39êóˆ¸È¤LÙœ€²å)§pX˜—³hâïr⪵€ÐZ_¼Mçqé бbw<µÒå›~s^³„ErepûŽ# Ý²¸“±Ûä…Œwc5ñW/‰­]>öŒÝè³ÝêRÇõ ·õä𦡥9&5”ð tÀT‘ –eU:·ŒÏ=¥w~§®†ùYOï¢Sœbß™’Õc i m×rt"\›óéfÅ= Æ”+}¼uG¯šhÇMbk,¨ÕÄÇ®¹éYe¾Z4ïF6YéÇd÷ÀØKB©V¤„¶¤–rŃ­^*(¾g–©¡WJ·ÑƒøH q ½T²PhžÐYFsÍ΂6â+°<¼«= §‡fª ¢ò;”B7\˜v¥@-µG1¸WOò%Æê\þIó!'rAÙð¶ëõI³Š„¶& ŠaZKfŒ)‚lºyÖåÐÝLr‰é½÷þ«Ê¾àíDìI¿¤Í ØÛÑœ¥Å‰ògk³®—dÅžã±”MùÖ÷ûÏãçߊÂmîJ.t’šÂÐŽ`‡¥ø‹èt;|µždEç)‚‰fh4ô&OMïgï&4f’‹_mSC†Fé§.B"%—B(xœí*”8K‘¸Ž»‡Æކ¥¯p#Ð…% DX²êœ²íóÛ!ž‰ÿtrÿÃÒs…µ›™ˆu"ÝÁpGhÇ©•k§ê€0}êáÇ/P@"$1?Þ½c6´žRŸÕ„ë ‡Êï;½‰Ö1Ç Óß`Ff{¨PY…®%¹òš¿' •µñ‘…ø캶ԫÉå¥Ö;å`Ë%ƒM<îìê›Ö:·"yx/×!kQ’ö~¾ç#‘HÄQF5µO€rˆØµ¼œØÉ¢üÑ8O”r`I„’zÚ]á©ÊÖDÖ“b0 <§«‚Ä¢J™Á´{œ…æÌäã˜Át3ÄÜ¿p™YÔ>ãÑ›yÏnÓí–PhÏi.b¿¥ºöš´s 5 ¶PŽÈÐŒÿY]ÃÞš~E ÄðÙx½¯7HS„ì¦$üüÀ-ÄäS>ëuå·¿¢Hšm¹âà&3Ncuê· l“€•q} d§·ª#DØhðù©qYTó»hÓíØMGôœÝ\1ÈÅ×2Z6?Ò«ê1ñ_f†³É­‚U;]¿KL"tQ˜ŒQIóÐ2T{‰VÌín ç”Äí|M/7Ò¿…ÙÉ‘Xð÷ß,½«¬Çž 7Û”¶ßMeÊóu²ŠÝwnáMž¤>ë³ Œÿ?ƒKWM)_a­›×¨/©5ûË÷4xÜûvMÞŠs£È?žìý‰48u鉪Ïmí°çM“ï2&YO„NŠæ ²ÊIÁ¨suµÓáñnæ €„Åïá¥teêX;A}D»Q˜ )“6I1Ó ÄþâQÅWG¾[&¤;ãë *qï ƒÀÏ#Ò4¯:ÿOxÿú•Pýô}QêòºÑ]óÒ|Âqï‡5¹œ>–?,ƒ¥—d3¨³æÆ?Ð.øigm=þÝí,ûTÖ£ÕÎÇW‡°è0W‚"“ÜÅiQª’@$hBN,ä_"¯ æ¬Í4êa­)ÎRÕ¡Ä„t- qâë‚ÇÒPéøÐn5Jjr&‡aØTÔ8U£6äU}¹|ó_Vbe1r÷~g‰Ð¼•Ö‚©¿‰›%®¨Zп uA g5öì7¹ˆÛ]Ôø¦ Öê È.v·§ih¼q.ëÔ|¥%µ¿ÊkŒŸ¬›‡C|›Ó5ì@88§öz®¶, ʌ٠2•þ/&Ê&£áè_¢Î—¼|îfǵ\¢ò[q=5Öqˆ€"yÙ‘´}]óŸ]<Õ`ГšC84†N¶ ;è>&d^EÄÃÚÞñ‡Û“ùªß“Åøhbê‚tz]„ÈÑxl&$&\YÀeõ·˜mÐfÿ¯ý dLZHóû7Rìñ‰½å¸J¾„n,ÝöåÃõ»I4™Wéåôט¬doÆ3oè¦ßYññ@«3Ìýe+Æžþ†eª+r» Ø—šbÇmI:&2#ÝQ]UƒEUJD *^ð·ÔdQg »ÇŠØy$}+îí?Ši9ãàôOhØ1àµ[¨oÕÈI9q!Q€ÿ+(…ÉâèàqHœç+ë?ÆsÞî:o»‘ýþï¹µû?“ÿ_ŸµüþgÎõ³ÛÿÃîuÏeêò|ïÇð-`ÜõD—] ˆšI‚hˆªw½ülüˆ—Ê&þžùs÷)=þÊ=ý¢ ƒ4ŒqÁˆ2_ˆ%ê@C¡ßꜮ–úF=Öǯ"˜vòîCJ!ÿ²×rKsç¼ân7dÒ—Í]ðâ÷_ÂǶ1{ö,·c³8\–E-ÕU6°rUÂNIˆ á!=›—Æ ŒÏŠ`5ÿ&{ÿ*™¢|äPN@p2ErzÙawH “*ýó®ÂFF(\§o@U;*å–j]ZþhTƒ–åÀOææŸØ‘r¤î£•'óTÉo¿]¯p,Ò YõóIŸ2Ž PŠ{·ÿMe¡6ÊöÑjýÑ{ ÔÞsWyví;+…‰‹îÇEÓ*J+n@}/àÚ45bù¡)þêY¼U¤ @Õ’+U'޾‹ZñÉɘ&Ø4ÅDá.Uœc$Ôâ9˜‰¢ÂH;pî©Ò¯ JÜ]FŸÜú`rÕAÿvŒY¨ã~úsWh„Ÿ,¥ üXšEP힪u+•2²#>•¥<“lNbÚžR°ÖÔ‚ccAà2 5ªYoÚ7£’ùW2ñXIÅ*Íž½[ß}z-Ò?´Õ웩/ÕÁͰN$4È.ÓŒâ¡ð ¬±²§¸XÎßÐÚùêê^ÿ½K™ÍSýo5áJBÓ.QýˆwÁy…å|Ë¿åùRþp>܆@?¤^erO>ⱞX•k_fUÆÊäÑ* Èê/9犒oXMæKb z:ˆÎÃGE⊘Æ(ˆÕdô²B½òyîë…~í{$´¶ø·•qvÍ}yïT¸ºù;’@%QÆ 8@:VÊïhÑgž;bæKpó’›¡æ³:;ê5ƒ¤T\áû¶LrÁ‡vüe‡†H¦ÃEm,!\ŠáˆÉ+ñâÜ[¢*©؆˜#3&ú¦—>®œîÇÇé¬Þ46û˜AoÜýþ®Ë¶Ùsûi§êóóÁÃpƒpƒu²  ¶n1x|a¶BQRGy!3O0pí£t3··÷¡h\»øîn~¾È‹„‚¡E:{nÖ—$¦ Ùa.ñzsŸ Þ<¾,wýé—¬\4™¡U}“˜ÿ•}ñ¿XšUâ›DŒ¾öÉ£-៛ ¡´®«FlüAœŸÎœ1<ø®U&_ò­ô,«º…"/yª±(}ΔÄbiÎQ*7Oж˜ z‚bÙ{+,oûpn'h ²%Mò%Kï5,c˜)©’óÐÚ.À,4F¥w÷G¾f¥t9-LŒÝfÕ+t¦VêõŠÚ9në5])n¼Æ™Ú\,müúö†_—…\E.Å&ñaµîÌÏàHUÇ×:ÜjjŒ­œ¸?ÊÐÔ8|Rõd> ±õðÞ˜5Ô4àúô!F ]?+'íî Ü4ø¹˜m:ë²- '=^%CtÖ³‹Ãñçä©Ó»S*/&¹ÍšLÆÓ)ÃÓäÍ`ZÆó?„À›3Z¸à58±Mò°® ŒÅ¶Ä‚9¿¯•ÒFøHÜeªa±sN‰^Œîvš–¯â+Øëéý˜¿ Á°U¦…ƒÌ?°gz¨Ð*!¡’ €Æ1( ZÏé\»–§=[,Î.±€‰­óLD5Á%…D@$í¢òÀüŸ-c-¯”|°rM'0á¾ VÊšYèÕÄ4ýA‹)Mt‘pšéÔdÇå5U‡ÈV&\ŽÎÿ…éÔ)òÚ7‚qT[³ñHÓ‘ö¸™ï”€}"OÄ¿¬Eîì¡7FøV Mý£>õ•´ðED˜(D‘&X…ÂÚqì2¦%ºX¶ÌçìÇÓ|Å´¯7/cË3I6ÏL3&K§T¦…7˜¯±{·“Î, Î@Æ$h®ÔÖ]˜°+l±ÓX7˜ô ¾?BŸ,‚ïðÌÄKåH¸ð‰&ëTÌ“å3ÞI3¶âpä‡ãkn&¢|`ôK…Ç›Iä¨Éçã0ÇB,‰´ âCUàb'7-jx û”šˆÓ8f™íFÕ¦ • 3…ó€/…ß9óqÉïÆîöÌ¿¤-ð+kòä½9WµÑ“ưáñÏŒ´¿]ùšÿñ½‰Š2ÂcRÆH¯ñå¥O”8Ò^$S®!nx€a B÷¢óUílc–m˜$GWàü¿Ó2?ƒñ½ìøõöLJãÊ·}伃”„ƒ’4%½€²B 9<ã\Úò-¡Lfi5Xz™ŸÏ²”„š4$ß#œIé›:Õ}W"Áfñ,4Vš„í&²Žlè\ëhª(;…B_¨¡Û€¾‡kõ1é=¿/îF¡Ÿˆ}h™ö=+ãFм}pöoØ®ÆÙ°œÑ@ZÌ3ÙKþ†h5y•%1î.f—”ú|‰/f@]ÄÔãæž`*×yð7 éÊp”’¥œvNaŠ>W˜!u\±,…åt—‘ÈÓ9˜MÙÝCçeƒEã¬òz {0t(]¯X¿2rêY^eÜI5.çqçÑ.Ž@ÆKП5÷øþápõ=%¼“ÆPÈ»"þιËO›ÓéWX0¯™Ã#a›.\`¶&Á WŒ$Ÿ -_Ê„UõÁ Ýêå„0ç¥YF’Ì2îQ&$3_,K$ˆÀ{péžVÓ…[Õ î+lqx¬ †%¤ÚÏö„£t­zhµ½Æ¾vxá[` ÏvÚ;VÞ ¼ò5ûÀs‡~xœR7yðª`§žGìÉcª­¿¤vY=†Å!ìôƒ-ƒ ÞŸóÚù<žh]=åI‚°¥ô­I`TµƒÅc,ï´‘<:Æïô4½UÅ‘DûÜ9'ìR|.y”Á87w6Ͱéè›"11îfº€‘⇃ðzóH•BÊJ›¾0*o—U¬+)2AúïºU#Ó4Œ¾¤·@²ëIþfY4vP©éÄÊWäžZ… ¦Ð2Í<µ‡Îì¥A9VÉx-5]µ[ùñèú°S–AhñÆ()ˆË]4z–6D0ç<ò %c ­ “´:íTDá(h0BfëôqPÖSý™äu┃pž “¾{±‡‘ÆÙz°Nç®èÃ5( „)+wzÏqSX %½4OÕ£FZý¡ZHô¥Ig×JŠÐ¯ò³(ædÃÅßt¾ êcÐg¢ú‚K×såÁÖìpôIWM bY÷;ßëËûy ß# ,z=ð5ž°"CUSD{7ÃAc`óÛ°6©éml“M-Ëô™ÉÔ&,!PðYíÂ{«¿(¿Skˆ’<±!7±0j&ⲚØÉÚ¡ëu°8+$>êg”S †Ñwˆµ ýN¾ÀúVµº84Irº>‹F¢™{çØÚAyu—뤲Ë0©çwŸ@§•F ŸF[,FcqŠŠâùõ£÷£yÄ<‡1 £$Z>¢x1yéÖhà’wv½›ñzt´€¯9 °Efara–BÁ7ú­î;JœRÆly Éߤ¾Çý©ü)&Ej%ÓFȦ)Šf"Ý84Xð6¼–å%%ÔÅ,jHÓÎ ÀA‹`²!¸Nà ÒèZ¹K†Yet#Ä:´Û¯FCv>m…ƒ¹Ü*eˆ)’´Ùn7‚Ë·5â{‹XYïú0ÊN*coXêH|z!¨¬'[z“Hê‡Y§™fp¶›:‘üƒýoi°%LÍ™þGÐr¾îi^˯ᚳ–ƒrhM2޽o–CîA9-îê(»½½·¦^Ûà1%N¦°AÚŒŸ\f×»ŽÀyVwVGùl2ÅSÃGQAÎ2( Ó¯WÂÝFÇ­ù¢çuHÕûÀ=Q¸°…zEõ¦Ù*61ÍPß7ê×ôÛ­Oê³&‰ÆÌÊÙAL<ÙQªüQ´@2çô´‰>”šºÅÃ%75ÕfÕ>K:ê,aorìÉ”Ï{~°¹YFÌmÙ våkü$ç%$T$hÀ^^ß#+T\Üæ~Âí ™|˜Ž€QlUm®å\վȮWg‡¼Ö¤rY­Z©•ؘ½ƒ ¸”õŒ½ •¢í­Ÿ+oÍgøº³ãYêþZWÔ²ËO¦ññ0Rø>uî'ºÎ5nå†Ì»Ê †_ŽÃ˜ëþëwƒb6‚Ìak *s,U‘f)ñœ‹:M·“wyenåû{¸—È¥4r[¦Í¨iP”‹B66LÏKe˜Ö 96ÈPÜØ¸¦“̃yå¯Ðt„caÚ4Nf†ù¿,BbÚÄó^¸O2å“Tõ°Û ·´öKŸ¯»%4dÉ+«Ñ§~¦ý"¯Çi¬|¥‹õpUº˜c¡1¹¥rvN¿ØSˆÉÛ”Úkö3ÖÏn¥káíÍѾê(æ’º}ÖóK–b#d¦­xÎu=9óúß¡¡±{Rô™ø±) igûoNÌ(Ã…¢ á(ql¨°_–íDYŸßðSâðüD ßquÝIJðó!ìM© ‹Zä„¿ïÖV;ÇÏð8'†@JÌÆB"³Í)†¨ ¦9B·ÊÒ«WšP6c›—wQfoš )dá¶­ŽøRˆÒ[ ¾ò±ÝdKnp]â¦[ª©­ÐÎÌä·Ð]3ðVS¡ ãg‰%h³ é¸h‚ÈåwuÝåä|¶aáÁøÌ(3"€ ¶IÖùóÅB²èÔ0""B¡¡šˆåâùÖ§Õý_OáýáBé©Qô§G'ŽY$*ä,SˆYíŠÂs“Çh2U]D÷l,£TÉ2Mñ)°*ÆsŠÎñšÉ%Zý'ò–šd€UÌGŠ&%ˆ--¿»Âj¥WÇ]ÞM•ý¾®9)e®²g˜ãïÚ+c `%)ë „©0J9MÞåBÝUf"ØÍtMésp„ Y&žÕ£‘pTœÚÈpÌRaÄJ”6p½<œÓÿÇäyeãÒ>1èÓùo!š@‚Fq#j3Ÿ Xð­H.…æé­ÛÂTçUàIŒE(¦ @ƒó'_òü_æÞ”4©® ®Î…­t†ÕÈ¿îÖãµW#ÛQdmû`É®XHÖ¹÷ä4~Æqƒs—³Ã½Èæ>®>˜? Ì,ÖÜæšÕ]êVÑÚ,UƒX?N[cˆUR&Rw#h~èx¾ZM™9_éÐ;«K[2» n{²¬bD»±1$GA ™’0&Fý]ä¬ûN[\­R‘ ’RôKà2ó’ô<2­æwd* >~ÇÎÎŒ¬ªóŽc¡Ú‚[ÒeÑ6DÒ>ºÉ]ìæ¦ÌF²c%ÁELäû’’¾•fŒ¤}°³R¬Ì%C|fïÝ 1aÚ…iŽh HÃÇ™f¥dÄì÷h¹xA?*{‡JÜ%Ìê4N„ÇÇꬄ7 ›VùÛ‰·æPìŸshÒ|ÉI«ð›tÓ=|q]C Ñê+Š.W ê‹Qv OºÔ€/ž.ô^ ½Az‚õô…ê Ô¨/P_PVǨ.¤/8_h^ ¾J5>GÛ‰'·þƒ¼Øõù9ù™ÈÂñ¯1áÅÈ]ïTÈÞgu ½cº„4Fµsh;7€ÀŽÖ’×àØ"šõ…gM™’çÁFmà&ñWïǶðJÀÿS^ï‡÷ûjœ|'5Þ8gLö{&ø4a ˜èIçÿR0I‰Ênòž¾˜X[R,¤ ÷'€ó8»¦T }kDNG~ê}Êýð¿õŒŒœ¹ãChš›ù#’»Ò5 N9ÊR~Þ¥ÍÝÖR·0mì 2‡3´ˆ~×ôj·6©ÍlõÑ |Duq¶’þq¸ +?¯¬­WJùìP$|ÓQoÚ÷ó&àÅÖ÷O£øëóf÷œ>k @‰bM&ÔJþÉI´ÌCM‡êÅ™~³©ÞÓ™ËÑ—UÕ[x߈ó ÉÕ<«nÔ¬3dLn¡%ÿR°h ðnväAXn˲¾#*àSEƒH˜=בÍEY5©ŠìÖ% 4‰ÑñÑN0f’³rôà˲À(¿†Pº ç µå–Ò§Ö’·YѸë«f;â5u2ä*“³+Ö ÜqÊ\99­Ê±”þ…ß’À1p¢2~¿…´áÍk 倘rõ:='Ñ›£8Ð$ܬÅwÇ»OOSÐ%n¾ÁCæºe!R&}½¤?}ÅH#žŠ3MèéÑdGäU-fðBźôøN—ZX™Ê\Kòû¬{Ž&UóÉ ÇUº1ò^f-ó‹lgW˜¸Ù›jWˇ}NÃG3k-#æcQã¢Þ0 e¡‘d“,»ÎŸ…JÃ]òþ'îGö—'”œÌb ù»ß¿ò&©[ëwܽë–UÜvqwÄdæ.±rÆAf§”dŠcÊÑ—ã5ã<"]šHžrLs4Œ šJeÔm9µy~8Üþg&ù~þÖÔTs ”-ý­O—€÷ÌN$³'KŠ;©È¨óÐ'ðgº•'ø‹I¼œxv€»Ðtß ³ë|t³÷:+ÕŽ¿P÷ãïï—•pÝíÖàÌÙóÅÁ‹ÇfTT™AGvgââä̤èpô[G÷ÒwYðˆ®ñ­‚“Z\æYÿßy´e¹~úÀ(æ×AàäôáH\‚yë&9ÉïM‘{ŒÒ‘¬ºY\í˜~-wšYÌHO¾©5 [@¼ú„Í㨆=ñœÃh×Þð ¶Ñ-ñ/š+fœZÂæ<‡PÍ›8¿óþŸö]„älw6úŒ£–I`¨ƒ°w m¡ÃÉHdÖàé]È´Þ†.5VÓÊb*Š]Ÿæ½j[»‚ë%jñÒ¬ûžŠ†@M]L]ðœBŠJ“Q×(”e$ÕfÌl³±êÿmxG­÷_S &åöv«ìEeÆUv€\$E¾bYeÓ¸°¨Û3Ä•Öò†•Ë. Zæí,Ú„½¸Èã‘ëJ)€JYµm+é?œI%=D‰ÝÕ˜ü×]¬¥Z8E;¹Ç’÷¦ž—âåì(:Lã÷-Ùõ© pJ˜8E¬ˆ¹òâcŽnÜŸÇÑGùkå}jÍC5:TPö ,œc©r°ß9 MN#j?î Á‰¼š/Ÿ?æ8AƒQÚÔ<(çW&µQ—#¡&¶ÃV¾Cÿ=9TÙ4³g–Ù— s˜ÞÝëùù<·#/Œ$Õ¨>ðmBùÛ-G>·äj­ù(»ËT±ð,U oâ#ÕÊõÚEfQK8®ãLÁÉŠ–u\Ô‡ÈlFýØ¿y7ü¥jaþ•‡6Ú^àeÀeJ›;"ËÁØø‡ÝùHëo‡Šß«°+8iðé$¤CYê“ àiø¾>¨³æеõ¨ÊÃùXð Œ\½™ž™X7ªMï #‚O÷Ô=ïÇÜìeZ¶u3‹.tì_Óo&óc 9xgrë¾™þÆbÇû|<5I垥hÏE¦ã¯nøvº%Œ¡žz»?‡Ø¢Ôá^½.’ñà–δ ü¾õ/s#sEÂVðZûuyæû?#¶ù.µÝæ$6 ÍŽÎÍo-“H#¸Œb²ž?å¥H ã³ÏÒ¿~‰–ÜV@n½…ž‹¶±že_~zìnyÛÈFXË9¥Þ®Oz0ëVZ¡ªåyoÞÞÔG ü sÈÇž§·|¦P]8±'°¯Ù„Éìç]”{Ðîã’áa< ’ÐôFd€ØPJhÜ€n[;¨Ó{ñ“O@÷N>G˜V]ꜞ…‘ê²µ·Ï¼ÙÀc´ °ÝLO0ouŽ ×ËE*KÊão6¹;OŒËO30;à‘ ‰ 쨙 DZ1Ð!Á¼A|ºõÁˆ¼¯ì­¯S«ü?P‡™\ƒŒ1¶ AÕt&«BéõY؉{í…QŸ„¹Ø±S9ÝÖ;!SžBÖÐŒ¡ë§Ä®ÙL'àp›úYóмÆYæ·WNKØ&ç Ä»•3¿ßãKhn¾F*eæK~§úVï€âòŒ+³¯ŽŸd®$$9ÇîüÀí77©?Ó“˜kŸw2å<,ê×ü!€<0`úB…’G“8û+îiq£ 4b‰|KÞRX§âñʱeƒêæì¡ìÖM`hÓ×Õ•Æ$ûNü]˜PÊ/«¡|™ŽÉ¶{‹C§èÁV䍿¨paû#g±§‚Ô0Å  ”SÍÅý¼±í¸ä²òÞNf.YT¡)È…Óbú±Ù6– «w<Ü0…·®KßÃtQ‡†ë“`æÊMÄ 9VÛÙf3§¯²…²_á·i1vÝÎe¤!ÜÓø­4Óóä¾×E•þ_w, öVVcTAHÅfnRàLÁvËy\þFå0¥ð{ž¾m¸>—!  :,Vú¦Â(« ý7 “&epÖ EÔ¢³S¯¦Ò „]Kø~ÓÒS×ñxmŽ’žìû¤#‹©/ÊØ6‰í©ðÛ‘g‹ò0“ˆl{„­³—a+û±{ú,[ÿG3äùärMÄ“âë¾ö´¨D!ŸË£iޏÂĹø9ÁíÞªP3™¬"ÝœŒùíû¼*XŠÙ™yhå–TÂ?Ë›€¸™¦ºMƨ|J\œÉräf$pÃaI«‡k°RŒqð*%ÅŒ* Áš)¤Ìçí[ßçöß³Üò’º¸öŠakÝ¥«ÊD§›7“¸@òŽàª¨pAs¹9ïÛDàz“3ÂÞçs‰&–Æ À#ÄžiCj=r QË»ÐÒPÁK{ÑJ1B€ž"™Û7\4@”Ξg+ŠIY›ZÀ…¿î5"Ê*rŸAÒ²•ÕrxNž^*%Go<‘[583Q“w/ÊÊes7±xrJ^.×.š™­N72%‘UÈ™dµ¹¼/Kh‰pèÙ ®Œ{ˆ‰pŽõÍD›{¢¾Ùçmkâiµ«0ö‚ŸCnRÏÀF-JoàÕˆò”4pÉæ5ËÓ4½ ÷êc‰ ˆà‹`à›˜í:¸]o*Ê¡MDj¬²+û–v/¢0P).]𔫇Œ¬ÞI=®Ö’ŠšhŒÇúø Õ¦Ò³ Š4;–…p“g×Çj¾‚fýg9ÉÉÚhóyÑ©-}d Ô÷ È3Öì“c@M½¹´‡³0õ­ô*ÂÎl÷B.pMÏÞ+"Y‹ÛÍÚö ð[ùïûz­•ODy;Àˆ6íúC7eÔ˜ùíòÉlÈ|£àëÇÂÁp¦çîz„[:»Ç5FŸ#JIHÍžÄø€ÝL»^š¹£[3ïIvÌë’hÓÕ¡·]ŽíF—>9Õúº§ú7мÓts÷¢RIËÒªÊ)v'\ó!ðPûAX¬}„凰"A‡"¦¢§ï Áé<&Û£¥Žwµ£¦@ì…p{®BB:F…%:×Â?¥G¦¯.À¯»²€ùÅ MÙ‘=xúØíÙSqyôV£eƒû;Ô;âi¶§dCOañÖÏuµˆ‹I ¸[{©Pïó€>²þdhS‘ÌÛJΘ¶îÙy!èsÕxVÍ‚—9êùÀ»[5ßÂÞÈ€B;À8¿ígîCZò¾š8wÿ]o§šœÝÛÜ…¬4ÿKœ ¶ QÇ‹Tóm¹–ɰe«u½O—Ìa¹É’·«´”óõ yLDh.TÇ‘éK®nÒ»Üþ}÷Q4ý1 ûÉâÝш­OHõWŽ*Ãö™}²Q²' [/NbÑÑ>ÉRIM¬“oÙGïq?ò|ò…ÑÏýM`Ime3>Ã(ÍŽýΦh:Ѽ媃»²êñgÁ°°+VNñ‘! {ØŒ«6ršA »‘A£K­(Ìô3=äo@”$Y§x\{°OzÕÑ Zt²ì­ÈßzBi…øë´ÒûñV‹ÛÑLð"°Ã–F^x%–PöãnÞ9|ä…Ö=ÏèãñèO'Ƨәt±,ÕôOÇžè·\:;3a&ÔÞ(—öÝ»ùðúþÏ¿L5WÈv!Däx±á•ÌvÐU·jô%ú¶Øë[Ó-p½¦ÍÔÆ=UÇÆLgƒÜ$&AU° H70’Ñd ì"Wmæzfã³»Ô/ÝëÄý] •B±mü Dˆ1 ‘@fb¡î)]Ñq¡Å¬%˾ÕïºþB¥'%®¶YUÞ'\Ç] jé?ŠdˆUr¼MÚlÕTåÅİEGaêÇûÊ÷p9Ú|$'àX í(fä[€w×–8rŸ–[æX)';’f"lÔ(sx0Rõ›˜ 7äÈ á`ã1åð¯V/RHƒ½HóaI R»Àç³,À=0(“¬1Î`Oó|¿â‡äû)ð6§TyD p‰$®«ÂUÅqqaÕ$ûÝÉòQû‰ÕánÍço/ ¹xºä«*m)5A ÷‹ž7éoöI±h«¦Y c Ó‘•\ƒtIáÄ'­w9•Åo-ñiéoBüHgT壛ÈÅ^¶Ë¦¢é˜ªûxŒk™[jì7ç®ÐÐÿ4© â•öЏ‡„pM±ØÀOjÌï}øçâOÀÔþ×ÝhþˆèJ-KR€CÁìž§ deR-´w¥½Ð•Ãäë0ö[1ycÈ ºÛÉÓ U|Òû“Æd‡†Šd!'] ¥R\' .¦5ÀÕN‰ÆJð0íã zJË6-)°´ø‚N ²T±yÚÁÛæt¨Ø=XÁu4}á>þ² :¥!3;õa`UNYÖ,ŒñŸp¹u[æ:Ë _EeŒë 7P«a°?8±k4Åê<ñ+HG¨³gŒö賆Ñí¾sÿUÎ-þzJ…ÝOjÄeêÐ}‚èn! ÔH©p§JdþSL²f3S7”Drx›¸ lF@"¢×?»B^µ÷S#šò¼&c†Xeek#ýËïw(+ò~„Ÿ a%Ë2„×´<䫊áaì"Öýt‚B]œø—2•õyÁ²xpŠ!w•î/4›8¼ŒX²,{™ÇŸ–ho4FÓ´F¾ò¾êKŠÁï:ª¨¢±Š¾mJïWÞ=UhOlwÂy“‰·àµiS†ºž\ã‘锿Wí¼¤ÌóiRºí¥û4Ô§Qõ/ûŸ7aÉ0§M²¾%…™þ©³?ÉB™KyD 9oÎ)÷¢t.#飗Õs o‚¿µ}B/ø×0ù¤“ÆÅ*館 0ro!òr¬Ïë«çäîI½FÕCã"èÃû©*(¡L_Quts»BË9Ú?ŒÖœ°–.ƒÕ×ÜëvÓ^‹jcáüÛœˆ o9×#œ œï8©æ ¸&ñ¾ª9ÍÝ›ò¦ÕÃ~î”×É$ñ”œ†¡¦äq©DMZÈŸlÆ8²/‹tq|H=1#+½ðÌòNÂîG( ¡û}çrxÆ£ˆ5Ö–§š“6­µˆéÿ&°äýZ»rž?^üiW9oÜå5»Û6 L© ‹Ä<ÎP©xcm§í‰Çj‹ñtàž} µz®ù]^UÖG’`€È,ÓeÜÁç#{yì)‡õ÷c@Þ“a†5=¯W- ôô¥ a“å£Dˆ«R’y8Ëãï, 9Ì^÷ŠÊÙVä Eô[í#êõ.¯÷¬˜tÐjý±WÑÓÀÂ~×,3Ö.‚œ—Ú£Qdv ÑÕ´L,´tJ\ÛèM1:dBjH=Ù¨$Œ´‰Œ›–™ífêõs–ÖÑ!nô2go.‘ŽŸÙÞ ;µ@ÍW+ú.HGFÃߢÚÎg0:‹Å,¢È­&ªí[q3µu C>²’ŠtÖáÙ\f@ÿ˜WËÆqPKeØeœg õT]ÄèXA@V « È:+K™âõ¢ìê¿#òõþ>ä¯n•aÝÊ ÃÕ˾µ5­© “*°¾ös}´«Ã¨½µÈ I¦ ; ìFXÌ.RL‚¸å~ÔÔ²[ý7pyvYovŠZˆç¢Ët‰£CÏØâ×Zß~7+ŸÚ}¼B§Ÿ‹„0ˆ[2!5<Ôô ÇKG5®®³ d¥.ÁF‰!i¡ªS¶ žh¨û:š&€Êòì;ø9‚À¼×[–¼|¬I»·¦¨Ü—öfA5†Ž—Y†~»PÖn‡,0¥CÞ·ø‹Ï¿âÖÃ:L¼²?sÊ’`¸$ªíŸr›yƒ¿MSÏ…¦ûU&ú¬¡OÆyî1!‡%²¿ž¬EÚ==&­l¨„Ù*ÆŽ÷à\>ä`ÜÏ bàA»n²!‡—«Æçøí|®Ê“óö0ú¡üi.õ¸4’øÍ%߯$­ˆaKÁJ×ЉRAGôÖ*Ð@SŠZ?*VJÒmQIÂ%-×:ç/´Ýå]s†¼'Ø>]Ù3dIô«GçóÎE:bý(¥C–ô ;¤¶’è=é³ýÚþ½?¿Îæòl&é>t[]Ò÷‡.»uâ¢A¡ 7Ï_™EˆžFô* ,­˜ƒ¾<[6Ì£ˆ×u<£W[W"«á·S]Ø¿fýµ•|]xÐs 5WõQÌO[êÉØùï빪DXÅi§Q10 9Q-Pz`‚©bv]Ì´¹A#¾ZôÇ·¶•ztûQÈMhÄÆq:zWoç þ:w¯hž€j]õÇe5-¥ñ<†Ð0Iæ‰LÄ]imi³ò\»'Êíð[¤`DŠDϤCY ÄGTý>ÇÏÃE¼) àQ±!?«ª*>̲ڣiü½Æ˜·|E ´£j5Ê„0"AŽ>–äÍ™íE?'¡~Âé—ç%=½ÉHèŹð:eÕ@¦ö 'máàò" E»ÅiÆ*v)JÐ,"›¶ÇàYɸPßšÍ\³åwúNÔX|vI² @í¾ "Ôù2¾»_ñ>N¥gÔg°Kc©&3(~ˆ¢ƒ+-^zaT T¿Nf÷Së¾Mz‚V_ï7à<-\™ÿ+/"¢I¤m3T­‰$¤„î|\¬ŠÖíõòCÁ'c*—‹s*ÿ Ò±^Ú„ß‹è^¯ë«÷¡Ý›îÖý›ž»Ò´Y%<§r®UR$–€Äœ5M:j‡¬>Ýy?ÇÀŠˆ.“O¼³ƒØO4׃ü'f=‹¦Ñ€çͦSxÈnFÏ%,fz³P«qV¼lÞQߘW2³ïço¸/eœo¼³/Kƒ"lWË1 ؘ TpÄ€C¹qD—°ÓE`,¢lSY+o"xV«}á@0‡—ˆ/’!Ÿææé€@׆J%š½ì l k,Çèz%À[š·@Çmýc²èd5½•ˆ?q|Ç?Šÿ SsuMV?L DBÉ–ªó\KoePÅÞ-]ƒÅæ2541*!Óe—)‰J¤¦;z(O‚ÀÂmöv=†¯üá¦y{šòÀoºÆx÷6'ïí2v÷x×O¤°Éà™Ýï– >¨ûò¬Ä‹ñMÏß:U'~Ž÷…Ø×Rµ»ç<ø1$'‚é—¥6ÀE1`á«@­Â‹Jf‰²y½Ötj¤UÎ¾šø¹†žw¼r`Úc…Œ¯Ô±X"÷¯Ý•tûŒ‚ù*µ'ÑÙNÿ×—öÏ•Çyß%ÅÏI¸z€Bµõ„.Ûv!rvD£–ŒKZmµˆÂö‡HH,ï82DT0 ßÄŽo¤4'NFh“3ãcÒw—¥Íª«WssOп¿öö¼¤r0g8G¡|Ñõ:Iâõ5ÁPR‰èÍ4B…/;5ýèÊ;3¿Á:‚<–)bKÇ®Œ&VœÚI1–b¢É‘“‘3ÔçñôAÂT4‡[Ñ‹j³›Dß%äýšý2Ò±À|²Wµ‘ÂC^ò·^\À{7¸J<`$lÆp¨¬:’×Ó "céµãsñv¼DIží}Í?›ku×eŒ®3?€ÇûJÇ¥a *Ú¥ÏÚË% hxvÎo 8VM¶ (@JÃ'Ê ®$@ðô èë¿åDŒ»¿º´s­½˜ì«–ƒ/H± Fᦪ©‚MDKx\’ËÖM¶½•¼–ȰIJKGÄíá sþ}G[Á¨|ÇæÅÁÜL0[ª(ÒÜ’ÄLë-εÏ÷±&f¢¸ïBÊX´»ë£f€¦ÔD!Ò4ùÍ2{B¬+í°u(*½nVŠ\qw´íáƒ9A"EÎNo”<È Vs¶žTߦˤ1Ö3f“cÃyï Ò00±¨=íÝ\S(gu14‡øÈfl m­› Ã7ä[£¬Ó·u'QA½aæh ‰`nQPa:DàòZ±gT@.Ñ‚”%mcg²÷óc'ŸÙÊå?Ë÷­9YÛ^é3®jë“F3X€·ÐX°É­ušCÑ© u/Èh"¡I¼Ä¶iˆÙœë „'>Þí»+xEnIæT8g_[YÃãííé\ÝU'éy&Ꭰ³Æ<ä)úg ‚°X×Î^ò,Þe=ù!Wy;Ðå€(ìúðéì%ÃÒS¼øÜ“Òñ¿ä1­ô3!npµÔÓq·aaNæ­8 öò”Ù¿ÕÄn²r /“ðj 2¦Á¿úï»ø¯PaWÑoúÖdÈbšo¥=Ú ±nl\F6]울a*þcjàÿIOR²ô¶0áC´Ð^h—å²D~ÔÐo ž5ʲ¤¸èµFíܧ{0yX—¼9L> ßÔñÿ4£96›—û¡û|™šê‹'\mÝ¿À_Ü ¼„bá5myÏ”ûÑóø:­ÚKZ ÄÞo‰¤§¢ò€7,»,üàäêNc¿µ°í}XðöP:7Z+³R¸¤voúKlª ÷›d¼7=$xZÔ« ‹¥7c—³4Öñ›ŠzÓE¾€[f# ôž1Sïû£g1)I[„¯Õ~¾+P|O‚Ó–x­¤KÄü1~ò¦G³ÅY»òVÆŒ¨VrZçñWŽ“ˆBðq¼ï¾› â«’r³Î  ´†X¼{GŠ’ËNE&ã­š«º#VêÚY€/¤lWÎü^&*ÔœXˆ¥H,…Ë…-tÙšbÀŠÞÔ$ ®ÝYpLOXƒ&$nÚL$¾£Ø´møå×m‚+Õ}´—ˆZW@6=ÖìM!ÓÒç¹i4gÓ•ö!Ô¹2ÌV3d ÖK)a¤°ÅPYbêð2ÄÁä¢Cë.·U¯ÕîäÜ+urmœ)ßηG’Rù=ΪWü EVn§ÿÌ|à¯G†9ƒFƆ{Ê/±x‰åègn.öË-b?V’W¶bí×ð4,±x¡ wÓ#|Î.{bº¥¥‡—ªX˜P7oÓ×ìh Ñò#ôaÙï¯3iäêÀõYÃÜ`flì펮ÆàåD°PAë–ÏT¥L?´Ò?ã/[«cbß}^E ŒN»ATÍ2Äqªšò«»%‰/ŠßØl̰|ß=%Ò'gjQÆzŽÑ¹È°ú¬N1²hÿ+rE´øx+gˆÎFJZéÕõ=ð=æ‚gy²S3œÝFÿ°`IG݉oÛÆŸ uÝœYú—$ÞÃ"úó±#MÛŸŒÞVï¼w#(18úo¨åS–Sõ‰3’¨$]ßÜì ~gÞ-¢ŒÓ+ôɶƒÚ§rYï¤Â.bãCnÒÊLÉáÔÏJ”N”†äÓ·•C*V3HŽ´qÃ6;qÚŒU*xl‚Ô {qP8ÆÁ‚Oq¿?ñÑÔ{+Òûå‡àÄó¢Óñ»6~ BÌ(ìÞ‘b¢"ÎÌe)Z[É¡ "ì­× IÛÀ»C8L ‹Ä%J ÊÚÃ.B-™C¾¼¡CKÆ œ/PU~ ´i›d^z”m_cnÔ[>ÒßrŠºwŸ²_©#½‰ŠMG CáQ¬(S‹L*TÇ|}C¡nÖ9kjÞgÅé bp3°îä[cOq5m%Ž£ÝËœ¢­g@0øQ®'jˆK¹jŠíP´eDxçNzýKlPÀ:³L>ƒÇj¢Ò•âTû¯AìÆ—Ïù.œµFqX©**¬c³9Ù;x"áLƒ·úÇá™ ñߘb[“nöF/ž5,J`º^!ƒ×8®Æá6øÓh?âÛ1Г%“}Rò‰{ÿÍÄ×FÄ ¦ê½4u5[¯² ¾¾ð;¼­Ø þ@ý¡2×/ì‘ÝM.N:þlá­ëÕ3õÉXíçn\˜µlaýIßg!«é@-1—ÓW‹ü„Ÿ”SÃéÇCÙÇšdƯ_>þ`öUiÛ\r,ZVé«·Æ\G§=4;¼IxüNGr¨«Î/+’Á¥—"Vî†g†d†ÓgÈ<åmöõ?FÑo,â£äé˜Q6ä®ô´0cI7ÙÓéã|½ÅÎØgìVÆXíª•Œ—³z]Üe¹wé5×ÀÛŽÖ¸½Ñ³ q§•!#d—Ê3…Ë×µYg³ÖÒ9Y¤Â º7pi5.Ô„½Øá¾›ø7èUÂÐü¯r—ùÉ[_xƒÜW;G‚<ÁâU¤!÷ó-"*“[KîG±Ük–Cå~uêäõ›+Ѐ’¼;‚·@ß3š[DÅÜd,Ì‹Ê&šZ•46ê'eGk…(Šá­–x¨A éJ&oåÃ&Œp@#<·E`a-¬ üXCB+ü®ÌM‹ ›š>Ïöwœe{ö9W_«–mÑÛ@™e¿ê ÇÅÞg®!˜zo)ºUÛ°¸­a“R2ŒŸ3²~Átϼÿëir£Ê5 2¹!ƒ]|';üÌhKi+ßÔ…FýÍÌàÛrÍΡâõœ1û3÷`Ù>ýÚ"ƒ}»Rvl,Ò¦.&°:mT’oIêBoƒñ ÕžCÑï—uª4Ðjø_+¬ƒjˆ'T ÙŽyâ™1¯<‡C/¿ÔÚî²XøMþYK¸àž5Ïd/Ÿé×´ŸsÇãa+1¢Ù á·j£»üc,þ²–*¹1?¦€gNû‡®;Ut9žS±á½4·¨n‘b¤ ­iÆDæZæÑÔ¥ð«]ÁÕ@´§Ær3Òœúÿ|Ý2- Åœ´x:HtS vwE$ßÕò0Kì—Êîú}Û{²gOFG ¥ù>°åÓס‰øK©æÝxÖ'Mæænf#¢g˜ÏßH¶òWcÊ‚HDY+Þ:¿£,€<]7Û¿&Ÿ†8DCS O7ë}¢I_¼€ŸÒ¸ýÏw”õUT9üØ¢ÍX6äÌgAôæÚúTh³`êðv§.V WåÛ·V +(œ¹U9x3@’Ï)®qߌïÚYÓ·ÊB•P"Ç( ãt*KFWà‰X:Ž˜"ÃÔÿ?µM3©Ž9™B'·¦Ú4I­!ú0ØÏ—Õ^ŽZmP ¿µ/ö.ý+Þ|Ç¡¢5WÂö\à¡{=y^²%nª­ùÐìHêey%y¥øÔf¿êGúõøP'›Ò…¼ ˆÕfÕ“Ÿ+«Tÿ‡á÷7#g8¢éö¶x-÷­89žŒ©"¾x’–&¦¿aù¼#9UØ…ØEþº:tàä±9Eø'yx‘:_ÂBi¸Þí ¾süKõ² _Fãܬ,v7Tš»L<…\o¹Þëƒ8ÛJçê=Ÿsd¨M„ªÌ]$à÷`ÏC|°ÉaÌÔtxç‚*PJ÷æ¢^Ýýã‹ÑÊõd•—åoƒ–¤/Oí½ðí©_ý¹Ö·ØµpQ5¿,=¾B öæÖ¤®G–s£bÓŽË_SÞq{ËÿB_˜¾µÐâÃOÛ.|ݼ mñ²ð4ǹ'ïÿ[ Îõ#Qn)GÛÁÝþ_Ÿ©ö$ H æ—‰–ÍבÅâ¶{âá”6 Ì¯t"¨"\uŒxŸI…ëèvéH‰Å¨)ƒÑ«ôÚî’×_äó ¸‰ÿâýÝ}ÚÏáövËT#0N…‹kŸ+l!ƒ€Àö89#÷-#ÓVº;«åó°Ðê˜'dæTÚÅ—‚®Ž?㺹ÇÜñzŒkJ µD$}Ç^ŵ’úöIõ}Í,Z;úÊžIŒôÈ?º˜õ¬µ¬sµÆ?:Ö+È*S@˜)]8Q˜Ò‘ß™ôyû»Ùî‚A5ÑXMž£œØ0˜q…R.òž7M<ïè §lNS³È!Ñ„Íú“Gp}á¿üDÁ€¤Ž†ŸdÖO„?q}þ1»ä—$cŽ ü¦ò·Óëb*dì†jñsîYR?K‡ly\Ñìï"ù¹×sx‡+vX÷Ø¡ßúç=z+Ó¶2µ°´]BlG*Ù .yã¿X@`î•l²ö_PÅ!¶ ³¶—ïvŒd÷ð¼Ahó„}­IÅiøbilM Cµ8X7NÈÛÓ?o4bcÖr¹¿>~‡ç¤!Çjt²tþžÚ_ù1‹øìÉ9ÏþCrLýlÑÁ©(¤äêå÷›J÷cä*$Ôâ‰Ç”[kϧn‹Âq뎩È„hMoü]L#73t}¾Ae›$†yÒˆÛ,)ÉŬZ „ 2i¥«É[¹Ö9uÇ ³>ííADðu›ì¿ŸñE²*qóPY7Æ^Wù$«–$BÐác5 &‚È/B5fq0Mâ °DÌHt-ü.φI+Z©Î>/ƒ™PÚ)%Y°#Ç&2…’—·£„é2ß©óH…¶s̼D N»r¦¥Øfàú‰Æº9TÔd'NÜ‘ï.§ëˆe©ÚúËç·Úa+È©ÿ æ¯ùµYµ‘ ”DKÔ1æ!írE.,ÿÊÚÛ;û®›X¸êh™Ð,×qûtn‚4O†Dû’â#¯K‘]´†pRœ»&+:¥ÑÌa¨’ºŸ)yjŸ7Y%›ûï1ÙM  z’N@¾É @êÄ’#ØÇ+ç+lnc|fX ^Ed¤žò=››¸"d´q±òœ´jV%´ì3žÄ>IGÛÚ[˜s=!‚ìV` àdÙ[PÇ‹q1 LÎ><,n¶íËoáýN>/Ÿ€ü"E¾‘ ³š Rã«@Òª®Bvb‡"v$l¡”µ‚ùE+N¬Z¼ýŒ‹å÷sYÎ"Sî;küöUÈAŒàËš«3– ,1ĈaZ"€NaÁ«9b`DrL" ‡ ¿ÂíÿgC‘Èä{[xUþ(Ù¿FmÅx–±ÕÃ?z+ˆmüY{˜¤£žøÑ¨XO&zgŽÇÚ=à¿úºvGí»dùÌ Ðê®%J—=>.h'­ivYHz-ÁŠç6ÒÞAwƒ‘­ØE”†ÛÍ-à3lúÀµ|xxöhÙFúK-Ó˜`þÿ¤x3³œk¢ãrpŠèð Àý†L7µ†ækb1¡E¥Ü5æáï°ìŽ Æ‰«'=’äöh}Îç» Éy Â<ö5ò3½¦ÿg78Í·¶8ƒoRBΆT˜Ùümdg‘]Pۉ때B€Íª}1Rhé¬î¢ê ô"®Z Ìæ×ýÑ댵Œ—àÀnò¼äBON÷kϾ3¹=U!y0ó¦*²'¸:÷i*…¸”&ˆä¬HKÓroªèu7ôhW}ã’¾æùm(&&œz“"Ä#ß’ÔôfÍS`ø"»¸G×,ÏRòZv@g÷XCjή ' ìV9uÕæ½‚ÃЖåvÛAN‚ sUäVK7%ÂtŽ÷T‚˜šÎë3X‘âEµf1‘fÊñÕ|X_ àè—ש­*‹ub˜4~`¥†³KLðÖ[¨›µ©SÍ']˜Á5úLuÉ&Î4†â­0"Wc8§ºÚväþ)BϾEz37sþ/i¶ò´šZè~âôtãôò¬Š|êük|“ð¯EB {´#øpÅ·üà~@8Fð þ=b¼í³ùIqú8aê 0§rÍ7'˜*CeŽM½Ê;HøC棱áþH§„J,1¯×oñÉ5ÙÝGÑ–žiÇ}˜êÀÔtÈÊ&à?2]®Ç9ûϬ\6Í^f…2=Þ-±ßg¹Óuÿbónt%õâ½Z³„œJ–ÛäGÒòµ_6*I—dl>¦<‹‘ËÞôuë-ß»„|à8ðžÌT›_Å5=¨óp¥,ÜïšÕ r²=û«Mi…;#ØÙîh ÜÏéà¥Þü•Ë\d~`ºêòR˽=ïª/÷*}çß5œsކ3n¥cÍ¢’,¸‹Ì¸O›rªiÕ°j|î @2j“)MGÅûgb3g&÷RBy v½‚ûѾN>˜™Ü 'Ç-•’=Ç'ˆtLý;ƒ£Ü7M%„éè5ŸÊ2³I…ø³ÃC2¿7!»ž!g‹±«Ó…q?B¡ð¶0"ÞÕ›ÜTàW×ïu„áµ—7{\mGØEþű±KlIswÚï\Eà‹Ä:Þ &"±—_£üñ9âÛ{EÇ xHëlñ Ñ0<Ç_5ñDt—KXÌ>2äÊO ²¸æê÷“*¯‰:PÖÍÀòU̽¶ˆ€ëûK2{ìæB¬VTÊ+ÍPïPeܾ%µ­@x7:F¹¨.]*Íc²á†šÞ%FýPe¨ŒàÜ0¿Ú¨y‹ ”9Å’?;þÙ|âË"ÚŠ¡û;Hš£Æe2Lšc¾cƒ48½ù/¯Ù±¨ÿa®s ~@FÚ] Z{äàqwfènz­Ù`âdöv‘éŸp%R÷¬mwD²¥¨êê†û‡!—¤kM’I^xæüŸÆbÖÜ{j8¶Kî´æ£°fÂ^û£õ—¦¸ÓùP#Y÷§àGk€?EÞÉ™’ݾocÌò ãT(¦_èw{\~ã·8v;£»{ý¿w†W˜ûUÅè0‡ÉÙG|=ó3_ŽANCØ-–1[6W1¦Ôi¸ÌÙÜa³y+| Ž2 p„&ÃqŽÑLÓÆØ›iAX`vÆèãuH1ŒeWhû‚'Ú«—b‘—”®qùäÕÙ÷šÑ „‚å…Ô›¾ í°žj—¿»KºäÏÊÏò¿…úve•…ñâ3ï]Ï¥/YŒVv­¢íHš—)Û ê‡†r/Dª"ÅRðq—n›R'¬,Î<>âªà‘€ª[ȲEŒ×¬ÏvÛc¾ûビ›±ž1„àqE§Ÿ­ÙùÝ؃®V¯Ö3Í$SÑ9ÅFiÙÓº¼ÌºŸ’ŽVÄ ÓF«9:û‰hÞæïiá*7}ÆBÝl©MŒÁö&ZB±4î ?PöæY”†^q»yÿ Õ¡€;”5<ϧf&ýb+ÝvÇîa ™v©oÊ"çfŽŒ8›"Þ~—C>KLõt;´1ç°’Ø_`Ã=¹OlycReƒ5=ŠèBGGº›¨Û€ÆØq¾'ßBî„xQ\RIÞ-þ´Þ~YmlÈæàùFB ûÑ@˜Âô-I–~«[¬§RØûÙ‘Å¥ÐhhèÔ.€Ì§iÆCKôœÖ9ÝB@6¯¶–>‘Ôé°dH™"v®²èÄý¸˜Ç§Šw]Ïg¦¶VtGöõS·<΂Œq8g7 €Y7f·/ÊÁ«ê>P;Àcgo'„>‹;aÓéüNÇmÿ\¾&ó¯eúúˆá¡¤$Æ1‰1‰1ŒbLcĘÄ&4¾9ëmdÃàÉÛNë/Žd{ƒG«,ÊöD!ÚU³B˜ø÷jcÙ±ïe_¼¬V”ã ŒF @dØ@¡ŠVP(ħ×WˆšlËx€håOûYÇ×Ïå÷?Ÿæý¬Óι.z]²ÓÉÙLç„m†¹]$X€õHÂÀŠHùhKû×Aòn´³ ™ƒÞ6ü‡šãQÌ#æ1T‡äT¶”ƒÿ6„ 6 Æma#ßOSZ% ý”qžôì~½œ_Pf%¢b\–ˆå¥žÙòǹú˜'»SŽÖ"¶9н9#o¿îÜP–Ìô÷=TµémÍ=rçã±Ï×Yã÷ÁfO1Âk‰‡cû+¿Í.·ŸMš aïµû{˦ob»Ûb³ ²×Œyâ·¼8Î…Œ1‚!yˇè}DG‡/ãqè–ÜŒÚY\ò õY—Bpäœ.šb¦ZšÆ¢Û–ƒÂ â`º°~s¢{ºÑää—`åhäœ.c^ž+ŠÐž0Ù;'ÛÐÇ”&È A¦V$Câ÷¾iý?ácõK£ìGº«äÞ_vÄ9òD‘ËV-‡úXúö)HÌ¥2³rÁ©;zZh¯}"D ’,$$ЯYÅÊÁÊ|ù%gs™ÎG–ïmG°Æõî±æŒ=T·8lSqvY-*Û©wà?¿²圬¸>³…zB4®‰¸\ þAo©Ra‰¿.wæÌï͵íOw!ç¯ú}¶/¾ŸlÅv~ä%$côƪy¥?Ð"Y‘Ÿ¥( Æ&*ÝPQùŸ«ù7 ±$„dR?"(KGïªB@!$›!kQHЀ5èÆ½zh b‘)ß¿Š×4ST…AÚI¯œ¿ZÓóRÔã19÷Ä·RÓÇ2d'ƒ’rËfvüD8‚äÑ ðåÏÁˆð3 †oâ 0hZ9[¹„úJ,/#DR ‡Ös$Æ€¾Ó}A3 †bæŽ †¨~˜± Ùöh7$Ðzk–\‰³j”NqÝ—D¤Na@¹Ÿ1é<ó´G,‰½;Ä^–kœ’$®‹?]Òö _m½XSûOäœAUuÏài$¾ÉÀºl™/×CTé#ì³ýÆ ø?‘øÿn e‡¬=ÐÄ÷ÂVItb¡dë0…íRêªZ*gøƒ¢1tD¢é6[³šÓ ã §CLU%´‘ª{ä{Mx oYµñtHFÜ&UX‹ìº¿û H0ŠÙï7ãC=J$3"x|_«Üö›År!ˆZÌíý\mÿ«Š¿êv{P„â›ÊetÁºfñ„팼G-ê¶Æw¾úõË—.&¸FóΜ1i›²{}1“ŽGÎ1½ša†•dJ+úà;:k›ãuýЋõlqÈ0^|ÁŒ0߀LA¶Ðç¬BXëpMC0áoÌeTÝøfôÅ1ŒE¤3(Dù;>Z”€’^^çö–UJÕxníÅ y˜÷¼!”¥LЧæšðSI¿]¡Ä…ñzàçÅ^d} Ϻ)âp'u¹ü þC"tÿ(aMÐQ§‘,žìš=8çUÀ³ž(koÅõµÌ5–ÑÄ×`[c²÷ÛØªüîú)Zíb틃@>ø¬¸C“ÍÈ•óÍ ÁÞ¦Ûñ§#D' Âðw‡Zëþ!xb2pi3€v Eîs:Öøä³K™2—öòt;¸ïýÓ6¹¦9g¨d¡,Yžînz!kßìoƒûþùŸÑ`åYâVy‰Yo8ÑÈ}„мJ‹f{<´õÝÁCDˆv;ÍKî/ßW1 ²¨$Ö_§Åc)ô£Ò óŠT)BžÏ2û4wìØNU?ö˜=ubúYž†%YtÔM{’¢ÿé/=˜CÇq„dŒ||ï’ô—:u¬×îbN¤Þr×P½Ï†9t•ke½î€·*ô]_É(ü|ãáß*ò†=‡O< Ç4SªÜ˵Šqè‹âÒ =á¹j¡àˆ00bÁÄœ7T¾ÇçáÏ“ðÿ@Ã0× A…!Àk‹º@Ø,m vBTXì )¦ a^Pì¬ñ*”Ì=pô9”i¤I_5d”‘Fÿb…b,t†É$1¼}?¯ûùå¶ÄŒ5í/iO·è[ ƒ QH*9¬De /ñƒ1o‚wD2;éþåëQ¹û}Pã]›>H%óRC¯Ö+x‡|n¨tæ]kæ×_ç#â§³Ûí-õîw%aûn‘˜7àMûô‚zåÞèèQý^‘ÎöŸ³¢1;ѵ¦ò‚“ËAõAnÚ ~Yû è$ª´€Ö¾ö×{Vu÷"½ÓRI­ù!ÞÓ<+ÞU~lõ#[ËM[ÕÙ›Ï>8L[òmäãë²ëáirOïoðš1÷Èd%è˜çí 9þÀùC&j” FuvñÒÚwßsÂŽŠ¹½ü¦ôK¤Ž«2º„ v:—]^™œ_ôÍb¹QË*Ä4Héé™âTÑ~?ÞýÞ7Òäuâv}öo«‹uÊ ø8­ªâ\íx¶àÖ éTÓo’s8WcCBÁ«ªäÀƒRqG·ô~£²­|ä‚a»-ó7¤˜>»ÕåûéX‡ÃSHÖ×”ÑÜKå ª.AÁ¾~¶øx,$:!¾O=|Yãzx/ÝÜamCoµUñÝøÅð…¯ R€Y†êiÅe3o¬Ç ‡Ò9Ž„—¸™´Ïr3Û˜ëÞÚ˜c<_ª¬Ð‘ÛìtC‚\6íæ:tà²]h‚hL*0Çl8ašcÒ»f{ ˆÜby{5®Oïö#ÿûˆi ð‹¢"í‹g]˜•,t_Åù¬^˜EÙri¿= ¿íûã^[ÐiõØßGkÌ[ ¿Ð¼þyß…¾“IüíxÙHI®/ðHG·±–ÍMpšü2\^»µŒ'˜`Ð0,à^æD¸þÜãý Œ{Ç9ôdõu*¡b‹tê0㌆[zKc™¦£ô¬ÍºIñT‚çQ™g¬'+56YÌm[m7%VÏ,:Ë œMë,q‚Λv›Â}Fd€YtKñßá Až/†Vð÷¿êà¾JHõÛI*ùS”+öÿæøé¥É=£SXùКÒmE¤Nqi'rÿ­×Ó?Ñôv>¨lŸë#݉DãžøðO(šÏ„hpÑíL3¹Ó÷ÜvŒã˜hÈC?¬€¾!Ó÷Ž„@$V‚×…Š®¿§2µ¡vÝ•¡ùsøD4°ÍʆªŒ˜S“}ÁdÕ]Á«¹?¨ÞŸÚ“txŸê'øÇº¢¥¥Éi,gU&ée‘3u_´@~;ÜšØ!ÇÙH$‡? Øñ¨s®#h. ¾v{a¦\ÞvÄÿÛãÄæ­ÙE¶\Ò‡æD‘*xäæhÄ·P[e‡\·lïÓ»E|Î9“5Ò'J böM\­Î *Ñu~3yá9šÓxâÓ‹I@Zvo±®y—Üt9CÈù®í®ÎHêt:ΓÀ¨*dàYˆ0íq޲=çà{ø/Ú›dàˆxŠî=KÌ‘²-0p`ˆ`àd‹Ïß’ë—é$??™ä¯om¾?TbI4†0Æ1ŒcÆ1¤6«ê#Ë{ÿPXª£ë‰Â¢Ö ü4{šâ`±Æ1¨FÞd•èÄ ˆ]*0ƒn0¹ Ÿc¸)ç‰pEãÀ¨SC¤]ŒÄ.ˆuº}Kž@¥~XtÚÚòÆú^x9jȱNæŽo?¿·ô\ĨÊ{ÑŒ{Ká×"n¶ÓޏŒ,Y a2‡2¶ s =UÇkÑþ°E’2A€ $ 3†ðÇÁ‹Únïr*b*ÃX\‹ÚG…U–è<[ an-‡£oF_ùÒ½‹»ŠæÒ))k‘¨Ù™à&…Г·ô+Iˆ_¶'ëtSÊèñŒgÌð~¦Û‹…7‰Q¤Ç «´Ùž ¾PN-OÅÂÒ\UñMqŸ„fD~¢úíÈÐÌp9YöD^¶À­ž/Z òÝß~>~î&ÿ“EbæžA¼û޼d9 ÉË+¨ð¡ÛÀµL¬…è9Û›TÛpÎÐ6OψI"òã7U–cÅYÓ^wÄŒÃx2±åâvçÚ½Á$SûNI+‚gt¢)ÄP6Hš0ˆ]'„^¶n\Ÿ¢QÁÐÎ1ÆšpŒ£Þ™ âœãžS6FQÛvç4ëèÛ'è1ÆÐºx˜Gv¿’­Øìç‰Ý|ŽÝ뮩‡{ëï£/‘#ÄTlv÷V¼þá˜Cx!>æðO㫸œzDè.Yå€cw\!3/´StÁzÕuq¹BKg´Ð°sú„ñGš¤2‚ î}ßâ@v³Ç'§è‚ÿž¥©p) Æy°3”l(Ø3̲ —xÒæö§Ì29‡ŠŒÁ7¥ú9 Ä,L$Qøa—;ÃÄ{7îiTs0ˆR?–=+ šG%Š\JvÁ~,0v¤.³þüã‡7´·$ºeC±vdZZqŽXÕëîu,þ?ñÏïæÿ|©˜Ýà·DPï™h" d&KíHD‡EDJâ ç±DAýbäfÏóùžVh&™¬ŠèC†°Ëå‡Ì EÇFÒ¾{e Þó3½ÙÔò†© Ì›°"­l˜ víØnµÀÑ´‚"ðâˆèŒ¶…Hyfìb xܲét*ÑÿâÒœ>ŸPº]¿Õ’> C8Ý©œüoÉ|"ç(ìlâ6 ñ˜\|¢¸öF HóN±Té ±ûEÙUãáÁ 7ů£9­ ˆC(ƒ¹¼ªÒ«ˆ£bÜyœA•¬.J¨Ú#!iü%;äÖ%ÄÔ%Ùôù)T¶* ªVˆ—çÃ>±í}Oi¼ö‚ÊØ<£¤f3i’s0WÈzG—¨^ %ñrl¡øôàzv/êÀ~ ¾ï£Xç!,H›Ÿª.ˆHRÃtNÝãPc ‚.¨µ ÙˆãTí‘¢‹ðîFÀ>¿møn¶ÆÕRØèç}©] ã‹¶;U€è>ØØøcO‚%BP›X",VÊ›[’|'¶£t£ä}/ |?»ë<ϦÄñ4ÚcM &Óm‰±¦’BlI$˜ÒA'à››ØôÃäòN‚ƒÜKÍÂÐ]L3æLôhröÕµwxãˆQþRÍò†\_Æü0A4ÊjHuê³$ç~0zH¨*ìñD‡Æ!òQ¸‘Í\> –ä-JEéhvLÒª)þ©¹˜qŽ¡zXÙ,hÌ%6AàRô_ÇÓïÉ⊫(ÂH¥q"¹èÊzGõ@PmòÕ§õ0-ø•ýõƒo²Ÿô0Õ7„=y!"§Z"–€þlµRq,@rª/dÁŒˆ™E[À½©Ì2¥Âa[U,Q’ `Æ‘»Œê‹ÛzÞ­‡W)dÖ‡î<ãê©ì:P<¿Ô™K×g¼=¶m/›aëÊC¬(ðRuýÍÑÊ øõ{¦Mû[¸Ûùˆ¹õÃÁ²"Œ"Æß†Wø)N¿8²@×-}±±;¾´98çs]È?ðl„­=¶U×—ÓÝ/÷ýÓnnOíÌ“ ÅG†]O¬2ëÁÁ)—&^ÐD‚ÐB}nëÕùž¿¯í_./ÁkD8< ÛØr„Q¸¸Ž´QÞõOïO¨N¯Ñ&"‘\ŒÕ;Ý<<¯Òùº À¼¼‰¡óv¶|ügÙVø¹ú43É W2¡:×µ:¥Kùº‘³7g7Ç1ŒdRÐÉÚ™£>qÎ4ˆƹ~@ÿ‘’@Çソ©úŸ‹²ŒøÁär§r]™¶*æ ê¢ü³;”wGn\£ãqW‚.GÝÿ2-š•>â!f0;£0 >Au3¨þU°<ï/ ëA‡.`ÒPr‡–¢4Bbb|ªV¢xÏf;q²¤:‚·¶^‡Œ,¾f|@¿‰ 1 F†4rMszv¥á²F9¨%Ù< áŒ' ‘?^ˆ! ±Š"ŒÂ†`äóvIBÆØöÿF__ç‚+t41&ÑP·f6øG†ywçLÏ9%ú0MyKêó~;€a´&Œð–І!±×õüX¿Ÿ·.t}¢\û1bŒÍ›âðýÑ™ãœÖv†ð£]0ÃÀ‘é â‡Æ°† BQQG–|Öå}íýRÂ0ß ðÐZo1âë¬eœâ»\ŽwIWùÀ1ŽÆØÇ"4v?b²û+€’éP]ïBþýén_ŒÖÎze—Æüf±Ê0 Tùgo¼FØÈ‡H„Q‚$ DQê ql(Š<ônŠåÑnD!„1ˆ%'žsÌ.©°tT¾:»²åbèfÕ dJŸCî–ö²î¾Õvâ·g\²µºG¾D`mƆ41€ cÆ$8eáÛÞœbÌî3ÍÙq_;£žnÍ7\Ã-Î*º:e#užnÎà×+4L<ÃH¬‘Ž~çOÜýåçe-—W,´¶*+º‰4bœb±€w£/ŒM¬%\qQ|S;‚ Ù†^£ _ž$оHÀ!_ëØ -sblKšìh|Ê1€Å!bˆ¢XÜFHö®Àf¢nϾT8ÛXÓ4ó¡Šb”ʦ‘XèvO#Ð]{tlŽE<_ŸgUFT,¿œ“’|T[ôƒ‰ï ФŒªj¸(ÀªS0ÎVaxU6eR†Ešî|:yçÍÑ´GÅúÒ"v ¡LÄÿ'Òž&qþ‘-2™¦3õý†aèÄ6å3¦i¤½¡TÒᮑPõ†éwX{/Ÿ¥×¶4‹“Ÿt\œs|]¥S²^—c9èˆÍcä•épuÑÍG¹87KëÔ¶åê£-#4ð̳êòíï‘A¥ñ|qN".ËärËÓv1+ãëÆ·€n Ó,ÛŒànp‘ܘ†ä¬A c0 ]LOªHbX[ 1¤T?/£ãøâÿoʾí¸¾8Æ3Ç8ìð:cÚ3M¢1Ž€•Ã1ÍBsÃèc/)±¬d#àŒÇF}/'ø7ÿèØ6¤ÀÛ£<éë­·{T¦ù®oÍq˜‡vuŠÈîÆb¥¡††prD‰†uŠ¥3TfØñg½óü%ÜSXÈj®'⃉Úü•d{fV1†€cBLi$bŒgÖ1 \Zƨ1œD{ÄŒc´Ê+7¦ñ¡XâÑT¨Š‰ 1 ÄH“O»ûÇo·ëkPÂûÎÚ¤ ÖSHF}1.ôúãÐʧ$Ô1iˆW¢üÃ-hðƒi.Î5ËòôÀ.ˆ$L*ÿm½“ż÷ #¾©%…ñÓK ¬øqVíH½k÷¢Üÿûx·~¿Íë˜ãCCC1ÆŒ‚ðŒb‘5 3LØ17HÔÂ#±¢e#q²íL“\žoƒ(Ûd tzæâxžïyñ÷e5Þ¼B9!úra’'Ò¢½o»XÂ1ÂFaŠcî̃$’±lb¢UPߛӛ¿ a˜¥B±XÄ$±â®#­Ú{/ºõïXÇ¾ŠÆÇÿ,|w,fuÚòò¿Å™k% džßTÊôd"Œ“ªe˜æAÇÖ8™Í&e8&àåî'æoÌÃ0è7&ÜJAχ‰ò{žsâe6ŸÒŸ:œ;çcú²å5ÿiž‡ß‡âÿÏãŠétº&HÆŒƒãl åŸäÄþƒóíü'þÜ~ùN>ÉZ ôϬ@Ž~õPúÆÁóO–瞸Ê2’|CˆŽÊ;'tvMñþ:§jDÜpùªfwh›Àߟ €„éå ‰ßòÑÞx¿ǾòûÓÈ8^WOÀRG¾|è´Û;Ÿã|¥ÄGb[”%ˆˆN±ˆM"1Üg™0î,g`ìÆ\ɈÈ2 Q aü±XKI˜£;‚RiŒ‘Š1)ƒ&ž÷ÆýOÇòw¿o c˜ä1qaÿóýš ÿ2Ÿì2kcŸ!ã †4½q†zâ4Ô f!)Û•È÷øKªxÚGšˆŒ¨V&‰d2Å_ç¹³.Õ1žOÞ©R¤´ßØg÷ºtüÉû‡‚$Ê¥D3|Ц™Pc1 ¤†¡Xö¦4ýƒ}XžrŒC”oHä • R&(Æjh]ýƒq¼?R¯ùµýW…ðÑ&í¶8¦Øg¾6Äåñ _£@c.Íðá¢üÑÄ H™~iÉO7ÈéY\Uè y /Ÿõ4~ç±àߢø¿/Æ1›sp`Œx—¦Yåèá²´zN3 º0I—èÜ"¹÷7‡bðÅ:Wgù{(~ŸÍýN+á­ÿù•ù€3ÓtAl2(ds‹3Õ3Œ1šÆy¬T nŒa¤%„U3Ð% A• Š„öü=ާ˨{Ì/Á*ð9Jž„kšä¯f]³„T/‰ ìÀ.Ï€®Kâø€Èëâ)0˜JŒîå~÷Bп3¹<œ°z—øȽþø¾7$KÃÌ6æÜ¡†a&Ô5Ìp@½ a¨Œ# ¥—çüOGòÔ\ZfT…1oyÿ’)>coí·ö³º¸Þ¤ò 3€È(Ī¢CpHc£ªc™&ìfy Ø2ÆAcœ´b ŽDhÀ4hHdîŸÒûy;ß-¡ŠgÒ>7Æ÷ñKWWµ\š…C–@å‘*Ÿ*ÄÂ+njÂ"T8Eé~1˜$‰oÍùƒ)‘†&ǧ­eÞ_‰o7œ'Éõ½öù˜¾òÞ”ð„°D·eâ¾H @d1ŒcÆ1ŒÑ6 I (Ò14ˆÇ‰¦‘4Ã:…ñ™°€D "¡LÔ M%*wÿ[Gw¿í1-NüÄIM¡•*|¨I‰cùõ8³ ôʈÃÃ0Jƒ*%B¡œtHɄҪ%'QÄhÍ$0MÝÖ!†b VC&Õ?øôøKÆí«."W $‹2ìªj­]Ô}“Y¸]²ÃFª7I0ÊeBĨ.Ž1£gq©¿.̲I#XI¡&‘Tc¿$1tIÆ"'Dª•J%Q“I ;†wÒÖ¨‘¶hêrt=þ¦#÷¿ybUBUJ¥Q™ä £" z%%¹+O¨–"%2ð¢“)ᢙ ¨Dš2#*É L?wÊáAw9ù_͉|¢Ð‘}¬@ÜÙ.&ÕVIöh>Èû=&² ¯¯7]ƒ†2Ñ/ e6– PµÚ-Ï«Ú>¸>X>jyƒ Ð1Fb"Ȩ2mc@ÆEbz4 ƒ!¡‰@‚ Éæ9G¡B%R‘_“;Å(ŒÈ' HpŒ# Š3qË2(da ßbâ¬&ðœ^üq‡õ£yQæ×åCùÒÕW++5|¯xV$$—…ñ½–cTUcV±„¼jÕ-0«T Â° ˜wüÌp1“ÕhXp†Iç6ç”H´ÚYÓš®˜W¢¡zsŒƒ)"˜Ì´@JOÝ·+1H›Á-¸Ì²¨Ì’ÛÖ1~(–@Æ4†$Ñ!Æ]uÌëã`¢¡†È1¤4äC” Æ1‘6ì¦:‚L,oÊ%ÿ´_ª¯—ãC.ȈÛ#¹r†WHeê/IKÒŽq|n‹Ô3ìÀ&#sœ¾?ÛÚ­ÝÑx‚Þ½ûÑaý¸ fææêú‘º;ƒ Ïf*¨÷Lø˜pH˜DFUž‰ ¥TU3™ˆŒBA&‰„Hþ-5÷¾V¨„'˜†-èÏÄ0ß %L§‡áçjꢙ„aD—ÄŒŒ.ËñŒ¾$Ý#c0F`“I¥ê&"ùñ'3œA`rï¾G¹ù> ¹‘¥|$ÓðïGÕWɉíP™ ü ûýòÀwÅò/Æd dH*_"øÁÑ€eĉx`Œ½$$ P–ZXÛÌ‹µŸå5}Æþ’eÑ€†$7à»\‹Ý=ºù—Fà¿-ËôeŒ€Æ¿/FH2$F4^—ƒ.ïÈïsÑí{¼ _'q¨5x‹Ñ¢/íŒ ÏÔ=_;åŸé=ùXÃ0ðÖ)> ȯ÷’ÛíŒÒì¿Y}Õ—?l®w«Ùg¯âÂÒm¢/D˜—àg¢ïÓ¯ñ!÷TŸå~(/éC\ô¶eú†¡ õiýó¸9„±Ï0 $4¤ÂØñx^,§p•êa÷¹KÅþ—ú¾XÿŒûçt\»AÁõßñ3I.ø†ØÎ~Ïå~/iñø\?¤Øí•;Ĉ Ð~g™½§òAx7eÙr…ÚY¼åX®ïrº4H£»=|Mš3zFɶøwÜ ¢Ü;N^ëi|6›[Fb“çñŽ*ß÷?"bt` FÌ‚‹b#…r,Ȭu».)°`zƒ AYÒÙµr$ÁxFïW6E™æB÷´^ËÌúŸ€Áú†ë–¬ó¹s0 çË0 Ðívܳ©ã‘„|3„b ÿÖÛ<¡1Z¹zbÎlè3n÷Úûé'¯ ¯Äì}b*ãÍ:è®´‡ŸçõÿØŸ‘]ÕºÊ|+o÷…¿>ñô.´ïjBóÝÝ›dÇä ¸Ë¡ü ß»Ýô&wÜ>àü¿Î|ˆpÉÎ0Kdg+;"ÓfL"™ÜÓf!‚,Hg—ÂÈÒ˜n;×jýõÚÂf‹ø58>ö¯ü¼o‰TŸÖè•LH*«§Í«Yo8Õ1&®ž&Jô4<>ÃÓàc[³!U·QcAibÖ‹x/ô5ú²9SæåMö*rÝ0_´¿øýf"ø¦î äcì^ y^ÃÚ,ƒQ}>ÿ„Ë"Š{_ ‰]ü‹êé6H¬4‹Ûû¸+脾,óî-Û-öÞv†UÄMZ¿ ßudo-È$°:KÍR FÂTzøùpÓ‹Jïm ·Ýºw7¥îfÒäêqðl{©´½t6„Ðò<ˆ’ ùl€Âý&‘ŠÎ1±–66'¥KiÚ¨¥ì÷yÕ–&v|sôò·. #˜Á/]ÅÁºÄ­™•a¼(aõ*ĘØúF#Z}¬ Þþl …³¬•:ÍÖ à™Zµ›ÅIÃ}[{ï¸,<…Â{?¶ »×ßpaiÁa!¬Ä1¯%Ãçcðº2ä-ô tà—M“%ÿQÕ« Š¢UI“V6®ðýµ8øw>ÀæIf^Ùû¨ ¾|4+Ä3’ùQ®sKëþ¬.žË×nwRIº¹ºR96R;ÙYz•åé6V^²íq/T:[ŒÙ—™œ,~ÎòÅ$d¡¤°†Øi-ÀàÅÐð Äò(ù¼t=§µÚ;AaÚ¥*>ü…Ae{}åÜ„­oc·åwÿ¦ú÷ZH¥'S•»¥^ õ ½’äÞÁ4€[áŸm…ë+‚i$ošKÖêÁ@kG§¥åùr­tË¿6H}M1BDÛÎÓ šö«ŠùŒ+)¤jT…ê±þ4u´ÍIYßä<À·äÙú3{êÏ':Y9Ýþ†[ÊòY×ëÑ…Œ:‹QHUíQøÅë,*cR×ÊÅJª«áQÆ®q)©9§ÊìŽÞÚ6A k<ù3àîþ9:a2´ƒ]ª ˜fÓÙæ;t]¢Æjè×>*³…ò2"ÀÞ@!¬”€ŠèÌ`ÌO!ƒ#:DA½$:‡‚yò" @_ŽI‫À 7•9`C[,cÕÚE¹#’R±‹Ú¨ëöLpÇ B»¿f6—¹{X¶Õì%aAh¥¢–‹xãl%a…[*Ï U"A02˜ß"Õ2ÂØá&9cs&XÇ,°˜&0p³/2Ã¥³†&0¾5S 2•…ÏË’IÊóªÖðV¿yåo>°|4RV™…–‡˜9oÊ—Ì¢Ú´JݰRN<œ’NCDä· «ñÜ—Ö^\Ã+cŽ8ãï'/¡$’C¦NìÏ)¥.0ò¢Ñ÷3\‚Yߨԫq)„[E3y!ð|ê5i v;[=÷îœÞ‡6“HMR‚¨Ë‹cÅ“n*9@$¢j5ÙZ`YC;6$Õ|fš&P-† ã¡4E¼ˆŽ8†v31›Â6½ZÄʃ ½_*yøš/3-j¶ßOnëH”jÑŽHVtš¨)HâÇ y»_=¬C<ÂÅe¢ö†Ë8UÃxdeŒÊØ+¼4˜gpÝÍ´ÝE¾‰UÍ–Ì׎;Cˆ©†Õ¹Â[€[ †A~0á¦o]ý½ñw…p½´oæœvá¶%lª›xo½Â:öCjmŒª ˜é£M‚œÉ™ñ2›«ÛžeªÓ‘Ó=õòåŠ×<Ë@}¨ëÐ`e©šòÏHËŸx®Uþò!–š¦ >àü|¯–:ú)¬å©²³´Ãhv~Ü=8VŽÎ›±ÚÅn 6×í'[NNnn.–§Cv•¢^å vÔ`«É„â‰`R!H®=} Ú°\c•";KpǶv¹:w%¼$„š§Wk=Î@#ÁäiåbŽ <»ƒ=ع¹:šBœØÙ»Ž†›8¼®NLÍ™=záÓ³K^iÑ =¯Â÷>m†¸Z»,`Åce9¹'a-Sˆ_×déÄÑŒ€(!ŽÉ—›››»6ÎmÝ—Cè`Þãa¢šc2ÈÙMKm8¤1“Iç~1“ÈŒ‰Zj‰%$˜ÈM&” ‰åy@ì¢@cCE©j`×­üÒÎ`ýœL?Š\D³œ¾”:íÞAÙò/² wrµ„  Kɨ4¼­œD³°P$2ª‘s%0i@E‚ iC‘Î "‘dV"@²D(,6—\×~ooþÎBÖ è#Ó-Þ'”o[üÖõ„&Ÿš4_²Ÿ!I‡ Ð•&MÀÀ…«ôÁ%|Ò ,kñx‰<êêè¿ÌC R‹xËFÞ¶¾ËÁmŒvɲ| ìè]ðï Z/Ô2 ¼êⓃ®`Æ›ÄòbQyEiôjñ‘’H)"„•L"²22ÔUJ-+ÇR·k›ó2AH1¹ÌPLcJdwfëDŠ´á9ÍZPvFà³3 ªÍDý‡‘±uJ}knpÎ"Ë‚& ÆÂŸæñãG¥Æ`o Gbk*Ó=5 ú:msøB:±6'_Ìp¬6³»=sJ‚ €{u—ôñ‡¼72âÒ¼sñ?Ä([g®ÌðÀŒééîÂOKö³¡¼ ÖÌëDÓÖÛïu†³`{6ÌÄ·y>3è<½w⿸úiªiò„öbHCÞwíÊ›™ÏEN«uqÞ^¤›Þ_´ …÷}ªÑ«LÖu&ƒPD¶$­’e–‡HxBÛ\¶»B¶ìÕäÞÙ†³ ˜C·[¥°ª­À–Æ×ºŽP*xòñmÜáz^ä¬õ…ƒUÚ )eLl/UªJ£ÇŽ¢$^QqäU9\Ãѯ‡'%öî ­î¹<Þç§Ç±6¢:ŠÛ/ÎlæòvîÒ( X¶@V<ùš¦Ë<駱1”8Ö€æ| 9—{¯15سT†Ü° ¿m$%÷^òÒ³;ÁÃ{ddv;4W7V³BM¸ÍU Qk X¢Uê‹Jš´Q"¦è8C •«"hÊöµk¶›d¨Á,ÂÔ'-«¥¶¹bhP-9ãmªèØ-¢¹ÝäÜY·Ã£8cÅi`×ÅÄ4¨o ˜ƒ£éüÇM‰2*°uRåŸ~¿,¯326i× d’c•[ lh´|pçQÖã·—m´Um ³PkµK3‰ÑDݸËàñx½'ݾééfÀ*¤ÛI‹µ‚7ÝeÊÓć×5fëe¤ø-Au«Ò•8°úDöåÖÝyX{v«Ó1IÙê}ÿÅÜ‘N$& _€jigdo-0.7.3/gfx/jigdo32.ico0000644000175000017500000000627607701377567015206 0ustar richardrichard ¨ ( @€ ÚéÔçñãþþþýþüàíÛÐäÉÚéÔæñâõúôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúüùìôèáíÜàíÛñ÷ïÿÿÿìôéÌâÄÕæÎÒåËéòåûýûÌâĵԨØèÒæñãéòåìôéüýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþýîõìéòæåïàÐäȽرáíÜýþýÜêּرÐãÈìôéÊàÁ§Ì˜ÕçÏêóçëóèèñäèñäøû÷ÿÿÿüþü÷úöüýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøû÷öúôüþüøûøéòåêóçìôêèòåÌáêΜÞìÙüýüÝëØÔæÍÐäȨ̙ÃܸéòæëóèèñäáíÜâîÞöúõìôêØèÑàíÛõùóÿÿÿÿÿÿûýúøû÷ûüúïõìÝë×âîÞéòæÞìÙáíÜèñäëóèçñäº×®­ÏŸãïßøû÷Òå˲ҥ»Ø°ÕçÏèòåæðãßìÙÔæÍ×èÐ×çÐÆÞ½ÅݼÞìÙëóèêóçåðáÖçÐÏãÇÚéÔÞëÙÊàÂÎãÆÔæÍÅÝ»ÎâÆßìÚæðâèòåÓå˳ӥ·Õªìô鼨°¶ÕªÍâÅÖçÐßìÚÞëØÉàÀ·Õ«ÀÚµ´Ó§¯Ñ¡¶Õª³Ó¦¨Í™¦Ë–¡È‘¦Ì—©Íš¶Ô©¾Ù³¹Ö­°Ñ¢¼Ø±¼Ø±«ÎÁÛ·ÞìÙàíÛÖçÏÉàÀ±Ò£Ãܹ³Ó¦ÅÝ»ÖçÏÓæÌÎãÆ¸Ö¬™Äˆ®Ð¡­ÏŸŽ½z•Á‚«Îœ—Â…½yŒ¼x’À®ÐŸªÎœ©Íš­ÏŸ©Í™™Ä‡¢É’²Ò¥¡É‘Œ¼x·ÕªÐãÈÓåËÒåÊÁÛ·¯Ñ¡¾Ù²ÕæÎÔæÍÐäÈ·Õª‘¿~£Ê“©Í›‚·lw°_›Å‰•Á‚‹¼w}´g„¸oÆŒ®Ð ²Ò¥­ÏŸ£Ê”¨Íš È•Á‚¡È¬Î’À}´f®Ð¡ÎâÆÑåÊÐäɺ׮ËáÂÓæÌÐäÈÅÝ»˜Ã†“À€³Ó¦‘¿~|³d‘¿}“Áµiµi‡¹r¾{ È©Íš¹×®´Ô§™Äˆ¢É’­ÐŸ–„“À€£É“´Ó§•Á‚ˆºtÂÛ·ÑäÊÑäÊÇÞ½ÙéÒÜëÖØéÒÁÛ·¡È‘½Ù²­ÏŸ¡É‘»Ø¯·Õ«œÆ‹‰ºt’À¯Ñ¢¬Ïž È¨Í™Èß¿ÀÚµ§Ì˜³Ó¦ÌáìϞžÆŒ«ÎÐãÈÆÞ¼–ƒ¶ÕªÕçÎÛêÕ×èÐ÷úö÷úõôøòïõìôøòñ÷ïéòæôøóöùôïõìëóèêòæñ÷ïôùóîõìçñããïßàíÜÝë×çñãòøðõùóðöíæðáëóèôøòöùôðöîíôêôøòöúõ÷úõêóæçñãÜêÖÕçÎîõê×èÐÑäÈíôêçñäÎâÅÓæÌêóæåðàãïÞÕæÍÆÞ¼ÃܹÏãÆÅÝ»´Ô§ÑäÊâîÝÝëØ¼Ø°ÀÚµÝë×äïàéòåÒåÊÝë×çñãêóçéòæÜêּذÇÞ½ðöí»×¯×èÐêòæØèѵԨÖçÎýþýæñâ×èÐôøòúüùûýû÷úöãîÞÝë׿ٳÇÞ¾ÌáöժœÆ‹¿Ú´×çÐòøðÂÛ·¾Ù³àíÛìôèáîÜ×èШ͙ÍâÅóøñµÔ¨åðáøû÷ÜêÖÀÛ¶ñ÷ïÿÿÿýþýýþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðöíÚêÔÖçÏ×èÐÆ‹¡É‘ØèÑüýüÊàµԨæñâëóçßìÚ×èÑ«ÎÎãÆôùóÇÞ¾ò÷ðÿÿÿôùóéòåüýûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþùüø¯Ñ¢ÆŒéòåÿÿÿÞìÙÅÝ»êòçêóçÜêÖÕçϻدÐäÈèòåßìÚøû÷ýþýÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþýº×®©Í›öùôÿÿÿîõìÖçÐéòæìôéÙéÓÓæÌÆÞ¼ÐäÈÜêÖñ÷ï÷úöëóèíôêùüùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþüþü½Ù²½Ù²üýüÿÿÿøû÷áíÜêòæëóçÊàÂÅݼÇÞ½ÅÝ»Èß¿ýþüæðâÖçÏÙéÒâîÝüýüùüøþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýüÞìØèòäÇÞ½áîÜÿÿÿÿÿÿÿÿþòøñìóéëóçÏãÇÌáÃÎâÆÃܹ¾Ù³÷ûöÒåËÌáÃÕçÏÌáÃÜëÖÛêÕÙéÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúüùýþýÿÿÿîõì²Ò¥ËáÃÔåÌúüùÿÿÿÿÿÿÿÿÿþþþùûø÷û÷ÚéÔÐäÈÐäÈÍâĹ֭éòæÒåË·Õ«ÔæÍÍâěŊ¹Ö­½Ø±ÕæÎëóçÛêÕ×çÐÇÞ½µÔ¨äïàüþüÂÛ·›ÅŠÆÞ¼ÛêÕ÷úöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎâÆÇÞ¾ÌáÃÍâźׯÓåÌèñäšÄˆ²Ò¥¾Ù³”Á‚‰ºt»×¯±Ò¤¾|޽z—Ã…‘¿~˜Ã†ÃܹÀÛ¶œÆ‹“À€ÆÞ½ÛêÕçñãûüúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉßÀÂÛ·ÆÞ¼ÍâÄÅÞ¼¸Ö¬ïöìÀÚµ•Á‚šÄˆ¤Ê•½y‚¶lÆŒŽ½z”ÁŠ»vˆºs½y“À€n«T…¸p¢É’ÅÝ»ÞëØßìÚìôéýþýÿÿÿÿÿÿÿÿÿÿÿÿÈß¿Èß¿ÁÛ¶ÎâÆÍâÄÆŒÉàÀñ÷ïžÇo«U½z­ÏŸÆŒŒ¼x‡¹r“À€Æ‹šÄˆƒ·nn«Tn«U~´h¢É’ÃܹÞëØßìÚãîÞõùóÿÿÿÿÿÿÿÿÿÿÿÿÉàÀËá¾ٳÇÞ½Ïãǻׯ‰ºt¾Ù³ÑäÉ~´gr­Y—„ƌšÄˆŒ¼x‘À~¡È‘¿~x±`mªS|³e¾{ÇÅÝ»ÝëØâîÞêóæýþýÿÿÿÿÿÿÿÿÿÿÿÿÁÛ·ÅÞ¼Çß¾½Ù²Èß¿ÐãȯС¾{ÁÛ¶ÊàÁ”Ár­Y}´g‹¼wƒ·nˆºs•Â‚Š»u~´h{²d’À£É“©ÍšÎâÆâîÞéòæøû÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²Ò¥ÄÝ»ÊàÁÂܸÀÛ¶ÙéÓáîÜÄݺªÎ›ÍâÄÑäʰѢ¤Ê”¤Ê•šÄ‰‡¹rƒ·m޽z•Á‚‰ºtªÎ›·Õ«ÓåËäïàëôè÷úöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½Ø±ÍâÅËáÃËáÂÏãÆÔæÍáíÜâîÝÙéÓÏãÇÊàÁÒåÊÖçÏÎâÆ¶Õ©œÅŠ—Â…³Ó¦»×¯¾Ù³ÓåËáíÜãîÞêóçúüùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³Ó¦Èß¿ÞìÙÉß¿ÌâÄÑäÉÔæÌäïàöúõùûøèñäÐãÈÇÞ¾ÑäÊÖçÏÜêÖîõëúüùùûøäïàãïßéòåõùóýþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄݺ»×¯áîÝãîÞÆÞ¼ÌáÃÕæÍäïàóøñþþýÿÿÿýþüðöíêóçóøñûýûÿÿÿÿÿÿúüùò÷ð÷úõýþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜë×ÃܸÎãÆõùóâîÞÂÛ·ÒåÊèñäéòæíôêúüùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçñäÙéÓÂÛ¸äïàþþþëóèÒåÊÞëØçñäóøñýþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäïàãïßÓæÌÔæÍùûøÿÿÿùüùöúôûýúÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjigdo-0.7.3/gfx/pause.png0000644000175000017500000000132407701377625015054 0ustar richardrichard‰PNG  IHDR szzô›IDATxÚí—MŽÔ0…?;é$4 Ä‚ °àh6\€ 7˜KÏbÑpw;ñ_ظ,wÚIÿ€Ä,•º•¼T=¿*W*ðýëK-\›[kM•q%¾<§Á5.ÜÀk û+ð>ûOÀÔ7ôÆBxïñÞóôô„Ö¥]×qð¶Û-Î9¬µì÷{ñÞc$ÆÈÃÃÀ`€C&Ò$°J`çÎ9Æq<"×{`0Ž#Ã0°Ûí0Æ`Œ)Á½÷‚—EÀÃ0]×ÕÞÀápÀZ‹1†ççg¬µEÁJFïòîJúû…(rZkËîD¾/½kmÙ½©UÌëEVX ½t2BG–R"ÆÈ4M(Uj·‚QJ+Oñå‚^*׺%B ÆxzƦéÄê{K«_º!ÁEþJ)´nsž¦‰”)¥#)¥ÕsÙ¯)àœ+GIR4Jð¹#±H vc,•,×Z„åØ áV:®& µ »×Z7 ÔÁkÅ–_L@vï½'¥TŽbKÚj­¢=K@ª½n&ט¹Iq"'Bk½H &[¿êú¹Š@ý°sîè¶HàZ±º‰Ý¤€¼HĤõzÁ\­:7+ N¤¬5¢:¸XÆßR@Z±È¿–Á×}á)°F •ñqS {Ùý%7oá7½ êüÕf¯×“6„°Ú‚—Ö&O-Ó…öøzþð9ÇØ´˜ò´ú ø|^ ¸Þpžy"ú¼É3ßÞæÿIÆòo«1ª›—HvöC†Ò|-œÁo³ï°FÀçÑ™ Þ4&§”qVÆòŒ5gð‚ñç>Lú쨟Ïp³TÉÕsçðG&ýÓìoRonòüÙIEND®B`‚jigdo-0.7.3/gfx/progress-green.png0000644000175000017500000001650307701377567016713 0ustar richardrichard‰PNG  IHDR?ÜgΘèPLTEÓÓÓ>H;¢Ò™‚²zÆzrRzJ¶ztzªrÒÒÒ²²²²ztfLJÎþÁ®zuw¢oβ²¢bZþÞÞn~jy[Xržiþ¡šÂ†~Ý’‰ÚººÚþËÞÂÂpšgDPA–ZT¦zs÷–Šª‚n–e¶žžÁö³ÂÂÂäþÓÊ®®fubp’i²~w Ê—ÞÞÞ~~~²šš’®‹P^NrŽl·ìªb]þºÂh’`òœ“—Æþ´¸zJEž¿–ÊÊÊ–if®––îþÞˆb_ÏúÁr‚ndŽ]¨Ú¢nh’ˆJjF¾¢¢î›’ÚÚÚ¾¾¾vˆq¶rjR>:ZS[‚Súþæb_ƒNHɆ}ˆ¶YkV^|Y–njà„‚¢|¯nf’’’®®®jvfvžnnŒg¶‰ZXþª¦ŽŽŽ¶Þ®þþþRvL¦¦¶¶¶Ò¶¶ÎÎÎï–d„^¢vo‚‚‚xVS‚TMΊ‚všpÂ{rˆfbX@@Žº…±æ£VNù¢™fbVtOr–kzŠvzžr®â£n†iꔌoRO†²|Ú‹ƒ°vo–b]LXH‡ZVcŠ\·~y׆~b|\šrnÁñµ†­ar]bJFÇöºÖþÈ“ºŒþÂ֦ͮ¦i`SbO|NGŠŠŠ‚fb¾€x~žx~¤v‡RJv–px’s™ËªÎ¢¨ngœf`‘kgr†mÖ„²…þ®­KnF…¦~ÉúºÞ–‹Â‚y¸å¯Ò‚z¦vqhzcú¦Ÿœnjþ¦šîþÚˆVO€®wŠjfBJ>bZæ’‡}šwÆ€w_FE^ŠWYzS¢f\y¦p¤Ö™ÎŽƒÒ†|j„di~e_m[ªÞ¡n‚i¨rl‡^Zºò­œjeÞþΞƖWfSžrnlNNŠº•¾^X‚ž} Î—qVS¾vn\†U®…«jbNsI°rjj–b{–uêþØvŽr¼zqav\˜^VÌxªvªzvþ¦¡ÒþÄjŠb~nnôþáÊýÂÒŽ†–ÂŽŠ¦†jŽc¸vmþ¾È†¢ªvq€^Z޾…rŠm}RN¢rmX~P¢jcøèMtRNS@æØfñIDATxÚÕOhUišÆ+ Áw!Ù\R C*ìfÚ@tCHç’†q™1 3%Ãzá".t*‚‹"Treƒ¦L@ÈBB Ü@â HZ0$‘¨H$%ó¾ï÷ïý¾óÿ‹9ÞEQ›Ó¹4¿ßyžç;÷Þúî÷¹^?|÷ûËñúáG¸þ³¿~‡×ÿgö]ÿ/Ù_týÙ_týŸ²¿èúÿÊþ¢ëÿ!û‹®ÿçì/¼þ‡ßåxýøÝ?æz}÷×ÍÇÿ¿ïÉÅÿ_ß/æã¿¿”ÿÇùøÿ‰|üŸø”ÿ?žÌÇÿ?äããc>þOýümó_ínËÅçÔv>þäã¿w(ÿsñÿ·7£ùø?¶œÿëùø?x‘‹ÿÿ¾ýýÿ/ÿÿ›óþß?Ó“‹ÿ¾Æ\ü÷nå⿾s˜‹ÿ¿•.åâÿ§³¹ø¿xo4ÿÇÞ-çâÿÝê™\ü}õý7͹ûõ@þ×öú‡Ÿåá­ûÈPþ÷¶]ÊÃÿÿ\ìy›‡ÿ¿¿¹ö<ÿÇÞ­äâãÃJ.þÎäáÿÏ·ÿðý7ÌK¹©i ;ÿå½÷ãµ­ìüϯõÏ´æá©©çRþ{§'ÞæàÿÄé Wóð?{®k2ÿ¿½Ë*€àÿÃêñW9ø?µpæç_¾eþË7{<Ë̵oÞÊÌÿThû0ÿõÁÚÀÛüoŒ>ÊÁÿú­Ÿ>]ÍÃÿܽ““Ùùÿõ×{Vrðÿà`ùã«ü}ñ*«…àmªþµö,3ÿïOwv¿nÊÌumª²Øs˜ÿñÍ×­[o³ó߸¾Ø6ô(;ÿû³;—G¯fçÿâ±¹§Ùüo<}p°ƒÿS×Wn¿ÊÁÿí3wnøfù¯®õßl.=ËÈÿnçÞ^K¥i{(+ÿ»k»Ý½µæìü/ 6µe@òß;<Ýuø(3ÿ¼xkúZ&ˆÿ;ßÌ»a23ÿsç Ã\_ÈÎÿ‡ƒÕ…Ûw3óÿçã/Îüáû†o—ÿòÔøLSëVvþ;_Îìd€øï/—§êc¥,Hþ+3¯áÏ¿ÍÌ?4˜Ò—,HþïÌ² øŸ}slîÞýO“Yùÿí·§ Àdþ®.¼¸›™ÿ…Ûg^e üï–!–ö3 ù?ý¾e|°Ö3”™ÿþî™áÖgÍYù¯7×zßfã¿Ò¸¹?¼32ðüQVþ×/ÞšûéB$ÿ³÷,Ofç?£Šÿ×W>f@ò¿p÷ç,€ÿÏÀuj|s}§m+ÿ}§÷Ö:–^׆²ñ_-W;ꃋméüC|], ¤@ñß{çÆtÛDz4ÿ³;Ó—¿œ½š™ÿcsçžv¥@ñøp°:™ÿ‡«+ÏÜÍÈÿÑó™(ÿåþ›•™±#© þ×Þïí½o;x)µÄÿü.@#T˜­æŒücì´¦@ñbx±tm´ùQFþïÌÞš+]þ”ZÕÞ`<=9:™ÿs–'³ò µŠÿ…Û/ e üW×Öú§7ö¾®moeãÿtßÚT}°©5­øÿßMä·c|føHj4ÿK3_oÔÚ&šßfâ`lvg¤}ôÒ£lüC›þéZjÿ¿ž»÷.µ’ÿ €wPüÃ8µº’^ÅÿÊñÛpmŠÀK• ¾ %:¥ÈKÀ^gKwãØbëÄPþ¡Íw/ 6ÕzR  ø¯l¾^,Aƒz”ÿ™õáÅÚå®­”hþ! ]}~5Óù ¹§÷/¤@ó/àCZ ÿ§ €â”(þE@ú¥á›åÿ1ÞCKéü÷í~RžŸy ~(=ÿ0Êýxy©g¨9ÿõFxó;ðîÓ  ù?±?—·õ ¥@ðÿwàÿ"\ºÜ•RÃ?.€{O\=›…*@(ÀÃt˜þ ÐñÛ¯îfãÿ¸l@)(ÿ0Öªó7ë½cMéü?¡xYžNÉ? €JïðNJ ÿK3µÖžthþ7{ׇoM´I'€æ ÀO÷S  ø^Ãÿ9 ùúÊdþ1`/||‘JÍÿ ñ7µàžøÇ¨œ†¡”üW;aì½/Ï_ÙüÚt$•Ä? àjǕƯM5˜Íø¯@À†më “–ÿ%#í°¡eáŸ&À…T0þE¼{ðiålzþE‚«œL%㟠­š(@J€††oŽ*@S7DZ§À𺠦¶BÉ?4°òîTw/O%€:ÿ—Ð$+Lþ1àò®‰4pþ).§Àð¯à~×r ÿ*R Àúχl@i`üc`J'@aø_«blö~M%€à@)`ç€ñ_yO1Km)`üWñ½×ZÛR`øßìÝ›]œiëI#€á Àå®/)ÐüË@N.OžMÍ¿(@$ÀA  ÿ:R `ø_9¯ •ÅáŸn¢P’ ùǼ÷d­¥c|óë `k(=ÿð÷»ë3_ñ>‘XÃ? Á×7vj­Û)`üŸØ§iO#ãŸ`'À§äHþg%ÿ¢¥€ñÿ (6ÿ²ᳬÔü«À”B€ñÐ]o¤Ñ–TÅ? `XÀÕϰa±„ô$@ò¸¼ °Ô h$¹Œÿú†5 Ã¤hþ©a”ÚÚážTÉÿ¾äÿ5 0þ!>D¤@ó/ ~šmåcþO\_]N%€Í?@JŠÂ? j@‰ü—i콇€ ózñHb$ÿø0¨Öíg‡Í©ùǃ«KXa Àø‡´> ù1ÒÖ5ü%-ÿ:`|:{õjzþ)6R `ø×€à`9¡¬ÿ›€ œTÆÿÊy)(ÿ»e*@ó"¢#  þw‘(@}u<†©%€óOPÁcüä0þÇ+è.5 öÄˆÏ þ)°]ëI,€Ë?L`œ& `ø§$là“£É°ø7X› 1 Àø§”Z€Bð¯ L`¨Ñ‹µdhþûTtC…¹AñC)ø§GpãT Ú¶·  øÜÅð*ÁN(€Íÿþ€éÒeð'¡ÿЀ(`'ÀæŸÐý“’ `øH7 „Øü‹XN!€Ã?€à—dˆâ.šPÉ? `XÀ/Ç¡5%€øï˜ÇGX€Dˆ“H‹(@Â]Ð'¡†`fŸPû—‰­DpþeÌÉ HÅ¿*@¦%Àâ_ )ÀÃDðóO*@×EJ(çÅ@r ÿ €Çº‹6íÔÚ ù‚h¯s­e¾ †Ë øŸ’ vç)Æ0?’ `ó¿¤Þ:\=°•D‹€±á0a|™J"€Ë?@‰&@"þe$€ñÎ €דàð/ ….ÿ¼% püSàØ©mÇ ø¯®Ñ€D°„úH©u ^âÿ¦âþ>ž@%€ó¯€ ð<^—ÿõ±a¨ð0º à包ŒQ€dzz?™>þÕH"@Õ€@€Wñpþ.œ—B€"ñ ÞK(ŠÀ`³ PßÄ HÁ‡ÀU €¥¡Ïvþ—61°áN €Å?-`¸šlà¡«±(þyо֕H‹ÿYÍ?N€®$0þ±©¬Ä Àù— ˜@ÜËã°øgX€¢ð/>IÚÿJÇ `øÇ @ܱ†Ç  ùÇ'`"º+´   ù×HN€$0þíhà @~  p˜”q €ý‰&@¬þ1èÀ-€—ÿª?‡Ñø¿8û@5 ày¤~þÕ@V"àü«ÀÐÁj´ÿ<¨Å àßm@qŒZÀØ€°GÄ ø_S¨¯“€náM„p¤’ÿù *@Ô€ä=‹€óhF5 š`(Z—‰pø7° (^‹7P€‡ÑùW°œH›S€Œ¿D P þÕ€ŠÁ¨5J€ÿ°€!`cG  ù— À•ʦxÀðï€øÓ¸#pø·@làH4ÿ CÐX,þÝ  -€—Ö€è4B‡]€D‚ @î&åß 9¢( ÿºQPZ@ð/€d¢ $jH”Œ7ô1N”.ÿt*ÆûN|ü› ,&@”þEÈt6p´^þ)äˆ ”ÿ„Øü³Ð :üóH"@ÑøÇ*€è0á0þŸþU`ªµ=\ÿ"ðÛŒM4 "pø·@M€(üϨ (F/ÿ*¨Áÿ@„.ÿ,TŠÀâ€bGÆ?o@Q àðMí#@QøgH@¯.0CãøÇ *@&Ä€{x¸þi¿TWË{øÀP˜>þÅ—#€Å¿.@*ĈÀåŸ Ó€Â0ü; PŒ6ÿÁ  !€Ë¿\À¦Åàò¯ j@bGPLþo>Æ Œ@r&€â€dêÃø¬:LŒÿô ¸ €¾ˆ3¼'@€‰¿Ëù§˜½%е‚ü³(Å ÿÜâ_€àB¸þYÈ!€ šÀ òž€l@‰Q€à ð¸¾D H†„ àá ÐZµe^ŒXÐG Ñ,þMz‰g ½¬Å‡ àòÏ@6 ¼8\€ ÿ*`×ĈÀÇ? š(Àdˆû¿'p„ `óo Ý€¢pøH€l@âk-w“ðÈ€n@á‡ÿ@¨%€æß `(@ª)„ýþûuÚ§À¯"‹øÀ"ãß-@ò³µ] ðöQ,ÿ²±x+LÿU€0v¦Åˆ pÿ÷@„ÿº™ † àŸ-Ó€Âð/  )@CˆÅã )*öÔ+ç_ ¢Ã4E `ø7@k@¡ùˆ€h@~|üc\ÔXm`¿þyÈô%T‡]€Ô6À/€‡+Ô~¸ê ”L€W^<üË”P€ò/ ›¾OÕ;¸>&K¼WÆçÀ?5 yµ_Å¿²€½¤°W€ÿVÄ àá_Š`À+@ÿ¢©C ¯Aþy‚0Œúå?Ѐü¸ü›¤™>üˤ@NyÔP`þÕ dN€08ÿ¦ÁÞm™§ ,&@¨6ÿÁc-Þ'€(™‡‚ü[ P¸AþER '@ˆŒ»Ù (T‡ÿÿ1ù÷@¨AþurÀ/@ùçÀ«´OÁ?}Œøw@6 P,þí$ÀÄ}¡9žc²A‡ Àá_ < HnàçA|ü[@ H p6(@ÿzÐgá¸üŸ‹ïå“ñÊÓ€¼øø?ï@”á¿Åå_NàýuRkPÅÕæ_ýœŠ.â8ÿ¦™P (Lο)@,T ÀÇ¿XÀ²Ñð Ê?h@â4(€Ë¿'ôö €ÿHBø`5 ¿AþMJ$@ø—@ €Š}'mmíq°ø—X-àÏì.0  øg@vl²íÀË¿ ù‡Õ€ö àáß Ù€hû`ü;ÜÒ Ð¯Ñüë$&€G/ÿ¾ ¯¶ðï€P<üËü›"@±øw`<°%]4ÿtÔ§øÇ#PâÛôj`üÛ`ŠÀ±'(€Q€øÛ–éáÀËÿ €š™[AÂùg\ Œ·½ €Ë¿VòÆ? &À™€^þýà ¨üóØgU€óo ø{Bz¸ù·@Ù³#'@@ÿ¢©P ˆxÀå_ €;4Å   Œ¢ÉC W€ÿ¾`Ø Šÿ`z ”Ël»xø· Pœ…àÿ³Å? rãP÷pKäÿ³Ë¿ò.¶°ùçà À i1xd à矀i@´]|ü«$€Ð5¹]äÿÈâ_/`Ö€ÄÀC [ο[€dPÒøË0Bò|ü›¬žŠÃÿ®áŸ€u˜ ”8õç1ƒXü› €Ù£ZŒ-€Å?+@b›Ü2ØÀÏ¿XÀ*jj gm"ø‡`ø¾_€PþYl¨ àÀÏ¿?h;„ó¯ Z?ÿ:‚ È Hü«L ˜Ž@ó¤fØü?Ñ0€œÀƒŒCKÍ¿<_Ã7PW¿h"ÊW@ÿªQ(mGüxù·À4 qd àå] 7D‡@–^þ$W€ÿþ`¬ZÄñÏWÿô ´Ñƒà†¢òÏ`ŠP°ÎǤÀÏ¿ «Q²ðð/  €A“=®!üã/:ëPÚŠkmüë¤@M`1\Bøç ÚÀ\€ ÿa &€-@ÿ<ìäà០Ӏ覮Œ!üŸ€3ŠÏ? €²é{¸Àæß)@rˤ˜Ppþû ÿ<ìúÔn àçßÓ€ä! „'jlØpþíä@ˆñüë l5$åŸ °ÌˆàŸ€j@BøÈ ¯…ä_/`ûL€V#€Å?¸€uˆÄnâZοÀ²ÉX¢ï3ê ` àåŸ-`ÕÛŽXjüü» ˜[\?ÿV8ÀÀâß.@¡ ˆ à០_²ˆàß Ó€l¼ü»à Åâ¿Åæ_/àÇã¼™{¸@òßR­®©'`ÁP ˆì±ðo-`úb¬ñt ªç_À˜n@j”‘üŸrÀ#€Ÿÿü[—ÿ]'Ô°ˆàßÀKú9 ýy.€Å?ã_.`¬é Œ„ñO@ŧ tÒ "ùgÀXü{ \¼Yøø v ŠDðÏ Û€´^þu:Å€W€8þ½À(ÿþ°¸“ÿ¤Àj@êàü›Lþé¨Ë54²¶µ¡üóàÞ‰ ¬òo ø«*tR§ (@(ÿ¾QX¡ÿa`5 u$ðñï € «B€xþµËfüü]Àä Ý€¤¹ùÿ?¼bîÝ‘K‚IEND®B`‚jigdo-0.7.3/gfx/restart.png0000644000175000017500000000463707701377625015435 0ustar richardrichard‰PNG  IHDR szzô fIDATxÚ—mŒUÕ¹ÇÏÚkÏyã sF®DœIÃZÄ;ÑÛv”•CÛ´m,_4|ibc¼&¶˜˜ÔÆ/÷VJso/ -i?¨¥/¦MGš1 Â 0¬Š‚…vàÌyÙûœý²ÖºØçdô¶ê“ûöíã駟ؼ—^Î@g³ûÐÌöŽ=zô‹×]wCCC(¥¸þúëyå•W>ü˜5ƤZkÛÓÓƒ·lÙ2033£ëõú϶mÛ¶rtt”J¥B<üðÃÄqü#à ðp1»’D2—Ì™œœüüöíÛñ<ÞÞ^šÍ&§OŸ¾AD8çbk­éííµÊ9G­V“ÙÙÙeÀ¿oÞ¼ß÷‰ã˜}ûöÑh4f€w3«/ç³µcý\¶ÎÎ{þz£ÑxÿÅ_ìÖ–­[·|ÖZ»ÚSŒ¢H7›MQõz]cVÜ~ûíø¾ˆ¦)øŸÌÒŽõW2ß·€H²µ•í_ÉHüÇÁƒ»©Ý××ÇÆqέ²Ö–’$ñ›Í¦RÎ9Úí¶rÎÝyã7bíÕša­å­·Þ"­e®eGpóÔdûAöÞ;§OŸž_Æ9·Ñ9W²Öæâ8Vª^¯KÇžsîkK—.ÅZ‹Ö¡Ùl2ÏÇÌÊøPe›'6{Þ.5 Œ1]]¾|9À‚µ¶'IOµZ-1ÆxÀòr¹Üµ^)Ev¯²àŠ>ü£$t¹\&Žc’$!Iúúú–ç\Îã©$IÄZë(¥pÎaŒ`Íš5dEÆfÚ—ê¨| ‰%«V­¢ÕjÑjµh·Ûh­;e8ôXk¯pÎ)€(ŠpÎÑjµ°ÖrÇw|'«v˜É¬»iÞÞ?#’þë–[n!Â0¤Ýn†aç¹ïœÓÎ9¥2F\¨V«¤iJÇw÷Ýwåry<úè£ÓÀÿfûùŒˆ7ïdÖ•J¥¡Í›7Óh4h6›¤iÊìì,Y:K‡¸ê4'ùå… H’„z½N³Ù¤§§‡Ç{ àÇÀ:€]»vqòäIvìØñ%`RD~¦µ^´hQ~ppÐÏÈèíÛ·“$ Fƒ$I0ÆpîÜ9€‰,k,àäÚk¯í©Õj¥(о022òó{ï½—B¡À’%KX±bÅb‘={ö°{÷n._¾LÇÄqÌÙ³gyá…Ø»w/"ò+Ïó¾ŸÏçßÃð³ÖÚßý‹øøAgpÑZ_ô*•Šj·Ûž1&?33ó­ 6àyišv§¤ 6°téR¦¦¦Ø±cGן"ºuëÃ974==½#I’•R¯)¥þSD^‘?dú{yQDÞó<ïŠÖº*"·xñb†¡2Æ$ÖÚM¾ï/À9G†(¥V®\ÉÖ­[éíí%šÍ&FƒF£çy¬_¿ž{î¹Y{òäÉo׋È;Yu ;SD.+¥®h­kZë–700 AH𦾵öÌÙ³g¿¼zõjr¹\7 çWÇr¹L£Ñ V«177× °4Méééáæ›ofll YuêÔ©û€E"2%"SJ]QJUµÖµ|>j­oÉ’%ªÝnKÇÊ9×vÎE—.]º}íڵݠ ‚€$I¨T*4 êõ:µZv»M’$]"###Œ‘ÏçËÓÓÓ_WJíö<ï¾ï×óù|ëšk®‰EÄjçÅbÑA¤i(¥~=33óÕ—_~ùß¶lÙ‚ïû$IBE\¾|™J¥‚1†8މ¢ˆ8Ž1ÆtÍ ­5ív›z½ÞÉ4 ­u¼páÂxppМ;w휣P(¸\.—¦i:ç.ß~÷Ýw²ÿþkïºë.úûû»!ÜÉë0 ‚€8Ž»#¶s­5q355ÅÄÄÀ_Eä§@âœóœsxžç’$¹:’-^¼¸3’¹8ޱÖb­×šÍfpâĉõ•J¥•J…(Šˆ¢ˆv»Mš¦ÝY?>ÌK/½Ä|0)"?‘?eÃKS)Õô<¯],Ób±èšÍ&ÚZ‹ˆP,]lj1&L’¤š_¿¦:ôÍC‡}榛nbÕªUcºGmŒ! CNœ8ÁÑ£G¦€ßŠÈL6¦Í‰Èœˆ4D$RJ­µK’äª_:ó¹sŽ… Ú(ŠâV«ÕȪUšuÁ÷§NzÎÓ ºz½ÎñãÇ™œœœ|1› ê@-ŸSJÕ=Ï s¹\šÏçÿ?‰R©dªÕj{^[mgyܺÀ>|øã€kÙÚTJ…Zë¨P($ù|Þ¦iŠºÚ†ÑR;û:k6 €ÅÀÔèè(ããã“Àï> °ïûi©T2…BÁxžG¿›˜˜p:;ꊛ·šL`||ùÞ¼“zv]‘îˆ1†(Šº¹ùIÓËâ 6)¥8çú²ýN}*àY˜¹þ“0¯uÆÀY”‘I¦ˆ4”RÁ§þ¨ügä8*$ˆ:´IEND®B`‚jigdo-0.7.3/gfx/start.png0000644000175000017500000000407007701377637015100 0ustar richardrichard‰PNG  IHDR szzôÿIDATxÚ­—[Œ]UÇßÚûœ¹tfèLo‰€€±jH" ÒEI0ãCðÁwy)¤šŒ$ "(VLÃ5\Ë¥”Bg:½Ph!v:çœ9gïµÖ·>ö>Ói­ˆ‘ïdíËÉÉþÿ×ÿÿ_ß^G:ݶ”¡ÄGOÔ@ЀגžïÒ)Ú´ŠÃÌváÕb1š®I3 ‘5É]ƒFÖ¤áš4ó&ͬI3` d 1À`sˆ¡a›äYNÔHáKze—Â÷ÈÅ9²,£! ²ÌqòÒS `rÿ»‚3’S"‘R{5IOîäYNæ22ÉÈœÃ9AÁ0ŒD²DÔˆ%ÎRJøX5UÉEç2"X–Ó¯¡æ0F¬ÄEÈ4#D‡‘»œ\'‚¸ œ^“5"âÀƒªâœ#¥>)OˆžüεwŠ™†ˆôñO½îêë÷ª)cãcvÒiŸ³¨5E“ 8Ááê£C¤:;©®³úìÄñ³ŸßfPŒª„èñÑ#…ïIõPaÝë䎵wEd}žçë&–OìÒS¾p²ž|ú‰vÝ­ß·”Ž gbgYuOV}$#“'Õµ“ A¨ìI)b¨èOÙ0¶mÛ&P}·eû7ÝõëßÜtß=÷?˜åÙºÖ\kÇ®©Ýq÷Ž=úùÓO²o»ÞÄ’@2Ìf‚˜AÂp(±VŠúh@JJT%j@zeO4E’%®øÖ•ÙóÏ=?´?jH{ìþ`'¿ÿí}üñw½íÄÝ~ÂÄèSC#Cqùªeú•óV§›n½ÁH@rI‚Xe‹˜€ ˜ÙQ ,d¤[v%j@“rñ…kò·Þ|k 0»÷ð.|*(¬GÉ<îßÏú»ÿĆ{ÙêÄÝ>ºtä©%£KÂòUúåsV§z½™R)b‚$0H`ÉHÉ~ùb^B, )rÁ¹6&ß›î<´o¥„¬ÀrE%°oï~¸ëÏ<|ï_ˆ ‡å+'tõ×¾”n¸ù5Hɰ¦†j^3ìZ%šéôÚRÆ’ žoœ÷ÍÆÔö©Q`æÝ[Và]IÌJ\ÃȆE¯Àö`ÃÝòÈý·:q·çü•‰•ã3ËVLèYg1]ûãk,©-€kLɪ¡ªH»Û’"ô(cÁšó/iLmß1 ̼±ÿe"%AJBV"ÍD–e¨*eˆ¥¢^ùxï Þ·‘ÇÖ?8ùIžçOœ01v`ùªåñ̯ž‘¾ûëL£¢Q±E $«l¹ù9éùyz¡Ç¥^ÖØ19= ̼ôþÓ””é\AÞhàœ’CÀû€úD ‰ä3ûòØú'ùÇCÿDDnÉùãcKGL¬§ŸuZºôšK̰ž?,¢EÏw¹ü¢+ÓS;G™M;6RZŠG³H–eˆ,U’_‘HÁÐИÐ8¸ÿOnØÄ¦GžEDnÉòìñÑF éÄŠñ´òĶæ{Y²DJ‰«|ѤôøˆDbôdÎ!"u€1DB_‰H5‰Á±®üÑe|ýòóxú/ÏÝûÂß_böàáµs³­ í¹ÎôÌÇã¾½ûÓÕ7_•RJÈáά´‹¢Í•k¾ÓØ9µk˜Ù¸õaÊT¬Ä[‰[D ¥„F%ÄHð¡Z+¡h¨BCdöã9^|òU^ßô&k‡~58ð;À!`^6ÐB˜‘Ã.w­Æ@£NµÕjq â"ð²ô‹wKGÚiJt[]¶¸#H«n‰H[œt\æºF¹ìÄ¥alŘåGb‹-Ð*Í>à½9²¡ÀŒ^»`êõ¼÷âÔ>àéø`=ãV%5s5ð¼Ë\/of~pÉ`Nç_{nZÈÀñ*Æj)yð¥¯ö3E§dú­ÝL¾<½ø[íqwAj¡U·ÄÉ|–»^Þløá¦ŽŒ§±c¶úâ3í¨¯R²ÊÒã{=[ö²có®>ð;5p§žq¤^^²t8®-ð'0€{×þá)„DøÈ ÜÂIÛ9×ÍrW4ahtHÿàãh4­ÚͱÆ9gf¶´úBaN„6"Ïø¸††‡ ˆÀÆAz`‘®q®—å®ü,€û%ÇÜ;  ƒ5Á”õ€Ö6ý_Àÿ‰€Ô$²zHm‰~ÖÀýú[–àôŨIEND®B`‚jigdo-0.7.3/gfx/stop.png0000644000175000017500000000164207701377654014731 0ustar richardrichard‰PNG  IHDR szzôiIDATxÚí—MnãF…¿nÒ$›AYäYä(9SN”ENä‰ã‘%‘ìúË¢‹2íHöÈÎ ‹LJ”ÐõøÞ«b5|Yÿ÷U.Ü{ÿÆŠ3ñÍ PýLü ¹¿1ž0üñÛï ··Ôa ÔJ w\m‚Î3m>2ŽÌ‡=Ó~Ïa÷‘ýnÇþã=K[˜ÛÂÒ{~þåW€ÀpL gܸ•cPj…pU%T 1P#Ôp5œP¥”B)…Z*‹Ó¼îÿm^W¸È@4é_= ÖþË @…H¾†.B˜QJ¥”Ê,Âý4m…þO¿[å/x_PG'†B— Tñ&xkØÒðÖpéa­…L­ñ×áðÜ_%Ãõ€“1u^¨á˜ÝµR(D8¦ºI¾`KCç†.ˆà°,Üí÷çL8l“_@0QÌ`ÔnDH†´†.32ÍÈ4!ó„›D8>ìˆð(" îüº@G¸U|©µRJÉ*0Dm™ióDËäîÞ#"¬q=€ù¸GÛ-ã80Ô![‰£j¨" ÁÌP3Ì sÇÜp£_W&®°ØÐÐaè PwÏ¤Š¨"¦h†™bfxއááxøõšæÎàµÔÓ}ÀÌSDUíÌä}3ÇÍñ ×3 Ѫ ÔR6÷žLMQí Ää‘ W4Rë’¼†Ùp2`)åd,O­ÕìÀæªÚ«¥›²_¯g 5†¡RK@)°Ç,A¨¦Öäó•x›Yl8õôÞº"âDïÖªJSIcöªp··•ak-éO Ö—x6›­ fЍ¡™Ü4µ7;õ…7H°<ÑjÂÝþá±ÁxôR3G]SÝTB–¢{ï†W7¢d –Â,rz±<±k»2±0wüYòàMU°Pò}~?M›Áª·×ÈãÙý¬.5ÿ”ä¯VASe7ÍO· +!Òþ„“æŸüE»ã‡y¡W`Ù°2ÐAI»Û_6Ýkë&§–øLñðcæ¸9Ç@ä´úð=ððõúçw,öÀ”Ÿ}ËÏМÛî6cÔðN–Éïro} €äèLþyáÞ³<÷=æÞòÚÁdÌÄãóî§"Ï'r0ùÏf?IΑ¾¨AIEND®B`‚jigdo-0.7.3/install-sh0000755000175000017500000001273607701377526014462 0ustar richardrichard#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 jigdo-0.7.3/jigdo.glade0000644000175000017500000042205710261546437014543 0ustar richardrichard True Jigsaw Download GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False 640 480 True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True False 2 True GTK_SHADOW_OUT GTK_POS_LEFT GTK_POS_TOP True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH True True 1 True False Open new .jigdo file or URL True gtk-new True True False False True 1 False Scan local filesystems for data needed by jigsaw downloads - useful when "upgrading" e.g. a CD image to a newer version True Reuse files True True True False False True True True True True False False 1 False Change preferences True gtk-properties True True False False True 1 True Exit from Jigsaw Download True gtk-quit True True False False True 0 False False True True 240 True True True True False GTK_POS_BOTTOM False False 6 True False 4 16 True False 0 0 False False True True False 4 True False 0 True True # False False GTK_JUSTIFY_CENTER False True 0.5 0.5 0 0 0 True False True GTK_RELIEF_NONE True True 0.5 0.5 1 1 0 0 0 0 True # True True GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 False False GTK_PACK_END 0 True True 0 True False True False 4 True False 0 True True True True True GTK_RELIEF_NORMAL True True 0.5 0.5 0 0 0 0 0 0 5 True False 2 True gtk-ok 4 0.5 0.5 0 0 0 False False True <b>_Start</b> True True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False GTK_PACK_END 0 False False GTK_PACK_END True 2 3 False 2 4 True URL False False GTK_JUSTIFY_RIGHT False False 1 0.5 2 0 0 1 0 1 fill True Save to False False GTK_JUSTIFY_RIGHT False False 1 0.5 2 0 0 1 1 2 fill True If the string you enter does not point to a .jigdo file, jigdo behaves just like a normal "download manager" and saves the file in the destination directory, nothing else. True True True 0 True * False 1 2 0 1 True If you entered a .jigdo URL above, this must be a directory name. For single-file downloads, it can also be a filename. True True True 0 True * False 1 2 1 2 True True Browse... True GTK_RELIEF_NORMAL True 2 3 0 1 fill True True Browse... True GTK_RELIEF_NORMAL True 2 3 1 2 fill 4 True True GTK_PACK_END 0 False False GTK_PACK_END True Enter location of <tt>.jigdo</tt> file, and destination directory to save files to. False True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False GTK_PACK_END False True True #Open False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 tab 6 True False 4 True True GTK_POLICY_NEVER GTK_POLICY_ALWAYS GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE True Please specify files/directories on the local filesystem to scan. Any files needed by jigdo templates that are found this way will not have to be downloaded again. Note that any directory to be scanned must contain single files which would otherwise be downloaded - in the case of CD images, you must supply the directory where the CD is mounted, NOT the name of an .iso file. False False GTK_JUSTIFY_CENTER True False 0.5 0.5 0 0 0 True True True False 4 True True True True 0 True * False 0 True True True True Browse... True GTK_RELIEF_NORMAL True 0 False False 0 False False True False 8 True True True GTK_RELIEF_NORMAL True 2 True False 4 True Start scan False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 False False 0 False False True True True Clear cache True GTK_RELIEF_NORMAL True 0 False False 0 False False False True True #Reuse False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 tab 6 True False 0 True False 0 True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False True False True GTK_JUSTIFY_RIGHT True False 1 0.5 12 0 0 True True True False 0 True False 6 True True GTK_RELIEF_NORMAL True True close.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True restart.png 0.5 0.5 0 0 0 False False GTK_PACK_END True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 3 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True stop.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True pause.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True start.png 0.5 0.5 0 0 0 False False GTK_PACK_END 0 False False GTK_PACK_END 0 False False GTK_PACK_END 0 False False GTK_PACK_END True True GTK_POLICY_AUTOMATIC GTK_POLICY_NEVER GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE 6 True 4 2 False 4 4 True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 1 2 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 2 3 fill True <b>Status </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 3 4 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 3 4 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 0 1 fill True <b>URL </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 0 1 fill True <b>Saving to </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 1 2 fill True <b>Progress </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 2 3 fill 0 False False GTK_PACK_END False True True #Download False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 tab True True True True GTK_POS_TOP False False 4 True False 4 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE True False 8 True <b>Note: Even though some data is already being downloaded, you need to complete step 2 before the actual file download can start!</b> False True GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 False False True 0 False True True True #<b>ShortInfo</b> False True GTK_JUSTIFY_LEFT False True 7.45058015283e-09 0.5 0 0 0 False False True True #Info x x x False True GTK_JUSTIFY_LEFT True True 0.5 0.5 0 0 0 False True 0 True True True GTK_BUTTONBOX_END 6 True True True GTK_RELIEF_NORMAL True True 0.5 0.5 0 0 0 0 0 0 True False 2 True gtk-go-forward 4 0.5 0.5 0 0 0 True True GTK_PACK_END True _Next True True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False True False True True <b>_1</b>: Information True True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 tab 4 False 12 True False 0 True <b>Reuse local files for generation of %1:</b> False True GTK_JUSTIFY_LEFT False False 7.45058015283e-09 0.5 0 0 0 False False True UNIMPLEMENTED just click Next... False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 True True 0 True True True False 4 True False True True True 0 True * False 0 True True True 0.5 0.5 1 0 0 0 0 0 True False True ... True GTK_RELIEF_NORMAL True 0 False False True False True True GTK_RELIEF_NORMAL True True 0.5 0.5 0 0 0 0 0 0 True False 2 True gtk-ok 4 0.5 0.5 0 0 0 True True True _Start scan True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False False 0 False True True GTK_BUTTONBOX_END 6 True True True gtk-go-back True GTK_RELIEF_NORMAL True True True True GTK_RELIEF_NORMAL True True 0.5 0.5 0 0 0 0 0 0 True False 2 True gtk-go-forward 4 0.5 0.5 0 0 0 True True GTK_PACK_END True _Next True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False True False True True <b>?</b>: Reuse files False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 tab 4 True False 12 True False 4 True <b>Select servers to download data from:</b> False True GTK_JUSTIFY_LEFT False False 7.45058015283e-09 0.5 0 0 0 False False True _Automatic selection: Use "netselect" to find the best servers True GTK_RELIEF_NORMAL True False False True 0 False False True False 0 True Select the country you live in. jigdo will then try out all available servers on your continent. Sooner or later, it should end up using a server that is fast for you. True _Simple selection, by country name: True GTK_RELIEF_NORMAL True False False True radiobutton1 0 False False True False True False True False True True False True 0 Select the country you live in → True * False True GTK_SELECTION_BROWSE 0 True True 0 False False True False 0 True 0.5 0 1 0 0 0 0 0 True Select or enter server URLs for the different types of servers that the .jigdo file references. You can enter several URLs for each type of server - in this case, jigdo will try to find the fastest. True Ad_vanced: True GTK_RELIEF_NORMAL True False False True radiobutton1 0 False False True True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE True 3 2 False 2 2 True <b>Type of server: </b> False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 0 1 fill True <b>URL(s) for that server:</b> (space-separated list, preferred URLs first) False True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 1 2 0 1 True Debian False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 1 2 fill True Non-US False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 0 1 2 3 fill True False True False True False True True True True 0 True * False True GTK_SELECTION_BROWSE 1 2 1 2 True False True False True False True True True True 0 True * False True GTK_SELECTION_BROWSE 1 2 2 3 0 True True 0 True True 0 True True True GTK_BUTTONBOX_END 6 True True True gtk-go-back True GTK_RELIEF_NORMAL True True True True GTK_RELIEF_NORMAL True True 0.5 0.5 0 0 0 0 0 0 True False 2 True gtk-go-forward 4 0.5 0.5 0 0 0 True True GTK_PACK_END True <b>_OK, start download!</b> True True GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False True False True True <b>_2</b>: Select servers True True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 tab 4 True False 0 True False 0 True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False True False True GTK_JUSTIFY_RIGHT True False 1 0.5 12 0 0 True True True False 0 True False 6 True True GTK_RELIEF_NORMAL True True close.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True restart.png 0.5 0.5 0 0 0 False False GTK_PACK_END True False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 3 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True stop.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True pause.png 0.5 0.5 0 0 0 False False GTK_PACK_END True True GTK_RELIEF_NORMAL True True start.png 0.5 0.5 0 0 0 False False GTK_PACK_END 0 False False GTK_PACK_END 0 False False GTK_PACK_END 0 False False GTK_PACK_END True True GTK_POLICY_AUTOMATIC GTK_POLICY_NEVER GTK_SHADOW_NONE GTK_CORNER_TOP_LEFT True GTK_SHADOW_NONE 6 True 4 2 False 4 4 True <b>Saving to </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 1 2 fill True <b>Progress </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 2 3 fill True <b>Status </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 3 4 fill True <b>URL </b> False True GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 0 1 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 3 4 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 2 3 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 1 2 fill True True # False False GTK_JUSTIFY_CENTER False True 0 0.5 0 0 1 2 0 1 0 False False GTK_PACK_END False True True <b>_3</b>: Download True True GTK_JUSTIFY_LEFT False False 0 0.5 0 0 tab False True True #Jigdo False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 tab True False True GTK_POLICY_ALWAYS GTK_POLICY_ALWAYS GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True True True False True True True 0 True True 10 True Select File GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_NORTH_WEST True True True True GTK_RELIEF_NORMAL True True True True GTK_RELIEF_NORMAL True True Settings - Jigsaw Download GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True False 0 10 True True 5 True GTK_BUTTONBOX_END 8 True True True True OK True GTK_RELIEF_NORMAL True True True True Apply True GTK_RELIEF_NORMAL True True True True Cancel True GTK_RELIEF_NORMAL True 0 True True 0 False True GTK_PACK_END 6 True False 0 True Settings False False GTK_JUSTIFY_CENTER False False 0.5 0.5 0 0 0 True False True False 4 True Maximum number of simultaneous downloads False False GTK_JUSTIFY_RIGHT False False 0.5 0.5 2 0 0 False False True True 1 0 False GTK_UPDATE_ALWAYS False False 1 1 5 1 10 10 0 False False 0 True True True True Display tooltips True GTK_RELIEF_NORMAL True True False True 0 False False True True Switch to download screen once download of a .jigdo file is complete True GTK_RELIEF_NORMAL True True False True 0 False False True False 0 True True Save .jigdo files in destination directory True GTK_RELIEF_NORMAL True False False True 0 True False True True Save .template files in destination directory True GTK_RELIEF_NORMAL True False False True 0 True False 0 False False True False 4 True Remove finished download jobs from list after (seconds) False False GTK_JUSTIFY_RIGHT False False 0.5 0.5 2 0 0 False False True True 1 0 False GTK_UPDATE_ALWAYS False False 1 1 100000000 1 10 10 0 False False 0 True True True 4 2 False 2 4 True HTTP proxy False False GTK_JUSTIFY_RIGHT False False 7.45058015283e-09 0.5 2 0 0 1 0 1 fill True FTP proxy False False GTK_JUSTIFY_CENTER False False 7.45058015283e-09 0.5 2 0 0 1 1 2 fill True no proxy False False GTK_JUSTIFY_LEFT False False 7.45058015283e-09 0.5 2 0 0 1 2 3 fill True True True True 0 True * False 1 2 0 1 True True True True 0 True * False 1 2 1 2 True Default servers False False GTK_JUSTIFY_CENTER False False 0 0.5 0 0 0 1 3 4 fill True Enter a list of domains (separated with spaces) for which you do not want to use the proxies you set up above. Example: "mycompany.com local.network.lan" True True True 0 True * False 1 2 2 3 True Enter a list of domains (separated with spaces) for which you do not want to use the proxies you set up above. Example: "mycompany.com local.network.lan" True True True 0 True * False 1 2 3 4 0 True True 0 True True True 0 False False True License for Jigsaw Download GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False 516 300 True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST 4 True GTK_POLICY_NEVER GTK_POLICY_ALWAYS GTK_SHADOW_IN GTK_CORNER_TOP_LEFT True True False False True GTK_JUSTIFY_LEFT GTK_WRAP_WORD False 0 0 0 4 4 0 jigdo-0.7.3/jigdo.gladep0000644000175000017500000000156207701377527014724 0ustar richardrichard Jigdo jigdo gfx FALSE FALSE FALSE FALSE FALSE gtk/interface.cc.tmp gtk/interface.hh.tmp gtk/gui.cc.tmp gtk/gui.hh.tmp gtk/support.cc.tmp gtk/support.hh.tmp jigdo-0.7.3/jigdo.spec0000644000175000017500000002117510433370355014410 0ustar richardrichard%define version 0.7.3 %define release 0 %define buildfordefault Red Hat Linux # My rather hacky distribution selector: not very sophisticated but it SEEMS to WORK! # use 'rpmbuild --define '_vendor redhat' to build for Red Hat Linux # use 'rpmbuild --define '_vendor mandrake' to build for Mandrake (untested, sorry!) # use 'rpmbuild --define '_vendor whatever' to build for Whatever (but please implement it yourself!) # If no command line _vendor macro is entered, the _vendor macro of the buildsystem is used # (see: 'rpmbuild --showrc | grep _vendor'). If the buildsystem doesn't have a _vendor macro # we'll use Red Hat Linux ... and just see how far we'll get! %if %{!?_vendor:1}%{?_vendor:0} %{expand: %{warn:No _vendor available: default settings (%{buildfordefault}) will be used!}} %{expand: %%define _vendor redhat} %endif # Gotcha! In bash true = 0 but in rpm-specfile-macros true = 1! %if %([[ %{_vendor} == "redhat" ]] && echo 1 || echo 0) %{expand: %%define buildforredhat 1} %{expand: %{echo:Red Hat Linux settings will be used!}} %else %{expand: %%define buildforredhat 0} %endif %if %([[ %{_vendor} == "mandrake" ]] && echo 1 || echo 0) %{expand: %%define buildformandrake 1} %{expand: %{warn:Mandrake settings in this specfile are untested}} %else %{expand: %%define buildformandrake 0} %endif %if %([[ %{_vendor} == "suse" ]] && echo 1 || echo 0) %{expand: %%define buildforsuse 0} %{expand: %%define buildforredhat 1} %{expand: %{error:SuSE settings in this specfile are not implemented}} %{expand: %{echo:Red Hat Linux settings will be used!}} %else %{expand: %%define buildforsuse 0} %endif %if %([[ %{_vendor} != "redhat" && %{_vendor} != "mandrake" && %{_vendor} != "suse" ]] && echo 1 || echo 0) %{expand: %{warn:%{_vendor} unknown: Default settings (%{buildfordefault}) will be used}} %{expand: %%define buildforredhat 1} %endif %define name jigdo %define title Jigdo %define summary Jigsaw Download %define icon jigdo.png %if %{buildforredhat} %{expand: %%define jigdoprefix %{_prefix}} %{expand: %%define _menudir %{_prefix}/share/applications} %{expand: %%define menufile %{name}.desktop} %{expand: %%define _bindir %{jigdoprefix}/bin} %{expand: %%define _datadir %{jigdoprefix}/share} %{expand: %%define _liconsdir %{_prefix}/share/icons/hicolor/48x48/apps} %{expand: %%define _iconsdir %{_prefix}/share/icons/hicolor/32x32/apps} %{expand: %%define _miconsdir %{_prefix}/share/icons/hicolor/16x16/apps} %{expand: %%define _mandir %{jigdoprefix}/man} %{expand: %%define group Applications/Internet} %{expand: %%define configure ./configure --prefix=%{jigdoprefix}} %{expand: %%define make make} %{expand: %%define makeinstall_std make DESTDIR=%{buildroot} install} BuildRequires: compat-db, w3c-libwww-devel, gawk, zlib-devel, gtk2-devel, ImageMagick Requires: compat-db, w3c-libwww, wget %endif %if %{buildformandrake} %{expand: %%define _menudir %{_libdir}/menu} %{expand: %%define menufile %{name}} %{expand: %%define _iconsdir %{_datadir}/icons} %{expand: %%define _miconsdir %{_datadir}/icons/mini} %{expand: %%define _liconsdir %{_datadir}/icons/large} %{expand: %%define longtitle %{summary}} %{expand: %%define group Networking/File transfer} %{expand: %%define section %{group}} # Update Menu %{expand: %%define _update_menus_bin %{_bindir}/update-menus} %{expand: %%define update_menus if [ -x %{_update_menus_bin} ]; then %{_update_menus_bin} || true ; fi} # Clean Menu %{expand: %%define clean_menus if [ "$1" = "0" -a -x %{_update_menus_bin} ]; then %{_update_menus_bin} || true ; fi} BuildRequires: libdb3.3-devel, w3c-libwww-devel, mawk, libopenssl0-devel Requires: libdb3.3, w3c-libwww, libopenssl0, common-licenses %endif %if %{buildforsuse} %{expand: %%define jigdoprefix fake} %{expand: %%define _menudir fake} %{expand: %%define menufile fake} %{expand: %%define _iconsdir fake} %{expand: %%define _miconsdir fake} %{expand: %%define _liconsdir fake} %{expand: %%define _mandir fake} %{expand: %%define group fake} %{expand: %%define configure fake} %{expand: %%define make fake} %{expand: %%define makeinstall_std fake} BuildRequires: fake Requires: fake %endif Summary: %{summary} Name: %{name} Version: %{version} Release: %{release} Group: %{group} URL: http://atterer.net/jigdo/ Source: http://atterer.net/jigdo/%{name}-%{version}.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root License: GPL %description Jigsaw Download, or short jigdo, is an intelligent tool that can be used on the pieces of any chopped-up big file to create a special "template" file which makes reassembly of the file very easy for users who only have the pieces. What makes jigdo special is that there are no restrictions on what offsets/sizes the individual pieces have in the original big image. This makes the program very well suited for distributing CD/DVD images (or large zip/tar archives) because you can put the files of the CD on an FTP server - when jigdo is presented the files along with the template you generated, it is able to recreate the CD image. %prep %setup -q %build %configure %make %install rm -rf %{buildroot} %makeinstall_std mkdir -p %{buildroot}{%{_liconsdir},%{_iconsdir},%{_miconsdir}} convert gfx/jigdo-icon.png -geometry 48 %{buildroot}%{_liconsdir}/%{icon} convert gfx/jigdo-icon.png -geometry 32 %{buildroot}%{_iconsdir}/%{icon} convert gfx/jigdo-icon.png -geometry 16 %{buildroot}%{_miconsdir}/%{icon} # Menu stuff mkdir -p %{buildroot}%{_menudir} %if %{buildforredhat} cat > %{buildroot}%{_menudir}/%{menufile} < %buildroot%{_menudir}/%{menufile} << EOF ?package(%{name}): \ command="%{_bindir}/%{name}" \ title="%{title}" \ longtitle="%{longtitle}" \ section="%{section}" \ icon="%{icon}" \ needs="x11" EOF %post # This will only execute, if %{_update_menus_bin} (see above) # is executable %{update_menus} %postun # This will only execute, if %{_update_menus_bin} (see above) # is executable %{clean_menus} %endif %{find_lang} %{name} %clean rm -rf %{buildroot} %files -f %{name}.lang %defattr(-,root,root) %doc README doc/TechDetails.txt doc/*.html %{_mandir}/man1/%{name}* %{_bindir} %{_datadir}/%{name} %{_menudir}/%{menufile} %{_liconsdir}/%{icon} %{_iconsdir}/%{icon} %{_miconsdir}/%{icon} %changelog * Sun Jun 1 2003 Paul Bolle 0.7.0-5 - Some optimizations (too lazy to specifiy) - Small typo in %%description - Added dependency: ImageMagick (Red Hat Linux) - added --prefix =/usr to configure (Red Hat Linux) - preliminary work for SuSE (mostly fake!) * Fri May 16 2003 Paul Bolle 0.7.0-4 - Minimization of use of buildforredhat and buildformandrake - Minor optimizations * Tue May 13 2003 Paul Bolle 0.7.0-3 - Use jigdo-icon.png instead of 3 png.bz2 of mandrake.src.rpm - Deleted double listing of pixmaps in %%files * Sun May 11 2003 Paul Bolle 0.7.0-2 - Richard Atterer reminded me that I had suggested to use %%{_vendor} - Deleted VERSION from %%doc and added all html's - Added build-dependencies: zlib-devel, gtk2-devel (Red Hat Linux) - Added dependencie: wget (Red Hat Linux) * Sat May 10 2003 Paul Bolle 0.7.0-1 - First Red Hat Linux 9 enabled rewrite * Sat Dec 28 2002 Alexander Skwar 0.6.8-3mdk - Rebuild for new glibc * Thu Sep 05 2002 Lenny Cartier 0.6.8-2mdk - rebuild * Sat Jul 20 2002 Alexander Skwar 0.6.8-1mdk - 0.6.8 * Thu Jun 6 2002 Alexander Skwar 0.6.7-1mdk - 0.6.7 - Remove gcc 3.1 patch - merged upstream - Fix download source * Fri May 31 2002 Alexander Skwar 0.6.6-1mdk - 0.6.6 - Add patch to make it compile with gcc 3.1.1 * Fri Apr 26 2002 Alexander Skwar 0.6.5-1mdk - After constant reminders by the author, here's the (not that much anymore) brand new version of the academy award winning jigdo - Version 0.6.5! ;) * Tue Mar 5 2002 Alexander Skwar 0.6.4-1mdk - 0.6.4 * Sun Feb 8 2002 Alexander Skwar 0.6.2-3mdk - Jigdo compiles with gcc 2.96 now * Sat Jan 24 2002 Alexander Skwar 0.6.2-2mdk - Make the SPEC be generic, so that it can be built on non-Mandrake machines * Sat Jan 24 2002 Alexander Skwar 0.6.2-1mdk - 0.6.2 - Remove patch1 - merged upstream * Tue Jan 22 2002 Alexander Skwar 0.6.1-1mdk - First Mandrake release jigdo-0.7.3/po/.cvsignore0000644000175000017500000000004407701403045015043 0ustar richardrichard.cvsignore *.gmo *.pox Makefile cvs jigdo-0.7.3/po/Makefile.in0000644000175000017500000000434607725437721015137 0ustar richardrichard# Project: Jigdo (Jigsaw download) # __ _ # |_) /| Copyright (C) 2001-2002 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ srcdir = @srcdir@ VPATH = @srcdir@ prefix = @prefix@ datadir = @datadir@ PACKAGE = jigdo INSTALL = @INSTALL@ GMSGFMT = msgfmt MSGFMT = msgfmt XGETTEXT = xgettext MSGMERGE = msgmerge catalogs = @CATALOGS@ .SUFFIXES: .SUFFIXES: .po .pox .gmo .mo .po.pox: $(MAKE) $(PACKAGE).pot $(MSGMERGE) $< $(srcdir)/$(PACKAGE).pot -o $*.pox .po.mo: $(MSGFMT) -o $@ $< .po.gmo: rm -f "$*.gmo" && $(GMSGFMT) --statistics -o "$*.gmo" $< #______________________________________________________________________ .PHONY: all clean distclean mostlyclean maintainer-clean doc \ dep depend check update-po update-gmo update-pot all: Makefile $(catalogs) clean mostlyclean: rm -f *.pox $(PACKAGE).po *.new.po distclean: rm -f *.pox $(PACKAGE).po *.new.po *.old rm -f *~ \#*\# *.bak Makefile config.h maintainer-clean: rm -f *.pox $(PACKAGE).po *.new.po *.gmo *.mo rm -f *~ \#*\# *.bak Makefile config.h check doc dep: ; #______________________________________________________________________ #$(srcdir)/$(PACKAGE).pot: $(srcdir)/../src/Makefile.in Makefile update-pot: cd ../src && $(MAKE) gtk/interface.cc gtk/interface.hh (cd "$(srcdir)/../src/" && find . -type f) \ | egrep '\.(cc|hh|ih|fh|h)$$' \ | xargs $(XGETTEXT) --default-domain=$(PACKAGE) \ --keyword=_ --keyword=N_ --directory="$(srcdir)/../src" \ --c++ --sort-output --foreign-user --from-code=UTF-8 \ && test ! -f "$(PACKAGE).po" \ || ( rm -f "$(srcdir)/$(PACKAGE).pot" \ && mv "$(PACKAGE).po" "$(srcdir)/$(PACKAGE).pot" ) update-po: Makefile cd "$(srcdir)"; \ $(MAKE) $(PACKAGE).pot; \ for file in $(catalogs); do \ lang=`echo $$file | sed -e 's/\.gmo$$//'`; \ echo "$$lang:"; \ if $(MSGMERGE) "$$lang.po" "$(PACKAGE).pot" \ -o "$$lang.new.po"; then \ mv -f "$$lang.new.po" "$$lang.po"; \ else \ echo "msgmerge for $$file failed!"; \ rm -f "$$lang.new.po"; \ fi; \ done $(MAKE) update-gmo update-gmo: $(catalogs) Makefile: Makefile.in cd .. && sh config.status # update Makefile from Makefile.in jigdo-0.7.3/po/de.gmo0000644000175000017500000013430110433432676014153 0ustar richardrichardÞ•-„ ‘ì01s3§)·– á‚x$û({)6‘*QÈ+&,gA,–©,‚@-Ã-Ë-Ð-Ò- ä- î-ú-....+.=.P.e.}.,’.:¿.dú.*_/+Š/o¶/ &010(F0 o02}0`°01%1&>1%e1I‹1#Õ1,ù1&2F2f2>ƒ2Â28ß2738P35‰3¿3Â3 Ë3Õ3Ù3Ü3 á3ï3ÿ3 4 4!,4N4b4|44 ¨4 ²4½4Ð4Tæ4<;5]x5GÖ5†6!¥6GÇ6:7J7A\7ž7. 8O8,b88;Ÿ8.Û8 9 $9K19"}9 9·9Ñ9å9 :c:˜s:- ; :;F;L; N;X;v<…<,Œ<B¹<@ü<==P= b=8n=)§=\Ñ=.>)C>4m>!¢>"Ä>)ç>#?$5?&Z??  ?Á?$à?5@;@X@#v@š@%¸@Þ@å@ù@% A/A'@A,hA•A¨A#ÇAëA8 B™BBSÜB0C'6C,^C‹C¥C$ÁCæCSDVDsD>ŽD%ÍD"óD&E0=EnE&‹E ²EÓE4ðE %F4FF{F •F&ŸFÆF&ÎFõF" G1.G `G«kGwH*H5ºH$ðHI 5IVIvI(•I$¾IãI'öI J)J~9J½¸L*vN!¡NÃN\ßNÇ_ `` `#` )`4`=`$E` j`v`(’`»`-Í`2û`.a1a:aCa1Ra„a ”a¢a¼bw¾b6c6MdÕ „d`Zp»u±Øu‡Šwcy*vyq¡yÂz¬Özƒ{‹{‘{“{ ¥{ ¯{»{Â{Ë{Ü{ à{||)|@|Z|4o|5¤|}Ú|0X}4‰}‰¾} H~S~5h~ ž~5¬~yâ~\2c2–-ÉO÷+G€Bs€$¶€ Û€#ü€V #w?›=ÛR‚<l‚©‚¬‚ ´‚Á‚łȂ͂à‚÷‚ƒ$ƒ7ƒ'Tƒ|ƒ—ƒ¯ƒ ̃ Öƒáƒ!ôƒh„@„^À„Q…‘q….†>2†>q†°†GÁ†† ‡=‡·:䇈6/ˆ6fˆˆ ¸ˆRň0‰I‰`‰z‰"“‰ ¶‰[ĉ˜ Š7¹ŠñŠ ‹‹‹z!‹œŒ ­Œ4¸ŒVíŒRD—­À;Ï2 Žp>ޝŽ8ÍŽI.P.B®0ñ0"2S.†0µ,æ;‘>O‘/Ž‘(¾‘4ç‘0’2M’€’‡’ ¢’E¯’õ’2“(:“c“0|“,­“Ú“A÷“¦9”gà”H•1O•3•µ•$Ó•1ø•$*–‡O– ×–$ø–;—,Y—.†—0µ—/æ—!˜-8˜)f˜-˜3¾˜$ò˜H™`™ x™9‚™¼™2Ì™ÿ™$š3@š tš°š 0›1Ñ›1œ=5œ$sœ&˜œ%¿œ$åœ2 +=i2€³ÂÜÒů 3u¢,©¢Ö¢dñ¢!V£x£"{£(ž£Ç£Ö£7ö£,.¤[¤b¤=e¤"£¤OƤO¥df¥ Ë¥­ئ#†¨ª¨6Ũü¨5©F©1c©D•©>Ú©ª2ªGª_ª+gª.“ª ªϪ_«2x«««Õ¼«&’¬°¹¬'j­ ’­ ­+À­ì­{ ®}ˆ® ¯¯ ¯T;¯8¯7ɯÞ°à°!ú°3±P±bT±§·±_´w´}´d†´2ë´¼µeÛµ>A¶€¶™¶O°¶ · · · ·(· 8·B·T· s··+¡·Í·,à·: ¸H¸ K¸V¸ ]¸5j¸ ¸²¸¯¦¾,Y H‡“âÜpÇö¿»Ïò5ˆ ”\§ŸËß÷$ãRÔ¡%ÈMNo‰nT¥²é1OÂX(j¹æõÙeÓ–›# .(c>Z& -åz?][Þ@Ûq6D¸·Ýh†{kÀµÌЗ¢fÕ #ÚÆsÉ' mÒœ:°B½×y8!3‘Ö ìÁÿ)UÃá2dG%’¤"PÄ…J_w+«Š4 ų,0ƒ‚¼`Ê®ç/êÑ;CúŒW„ibèí~¨*þ^žñ˜¬Îº!rItý­  7àAQF=Žx glš•Í<±*ø&u9 £Øa™Vôð$¶ü´ó}-ûLä|ëvùï©E+")K'S€ Copyright © 2001-%1 Richard Atterer Jigsaw Download homepage: http://atterer.net/jigdo Copyright (C) 2001-%1 Richard Atterer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING or for details. Downloading .jigdo data - please wait... Further options: (can append 'k', 'M', 'G' to any BYTES argument) --merge=FILE [make-template] Add FILE contents to output jigdo --no-force Do not delete existent output files [default] --min-length=BYTES [default %1] [make-template] Minimum length of files to search for in image data --md5-block-size=BYTES [default %2] Uninteresting internal parameter - jigdo-file enforces: min-length < md5-block-size --readbuffer=BYTES [default %3k] Amount of data to read at a time --check-files [default] [make-template,md5sum] Check if files exist and get or verify checksums, date and size [make-image] Verify checksum of files written to image --no-check-files [make-template,md5sum] when used with --cache, [make-image] Do not verify checksums of files --scan-whole-file [scan] Scan whole file instead of only first block --no-scan-whole-file [scan] Scan only first block [default] --greedy-matching [make-template] Prefer immediate matches of small files now over possible (but uncertain) matches of larger files later [default] --no-greedy-matching [make-template] Skip a smaller match and prefer a pending larger one, with the risk of missing both --image-section [default] --no-image-section --servers-section [default] --no-servers-section [make-template] When creating the jigdo file, do or do not add the sections `[Image]' or `[Servers]' --debug[=all|=UNIT1,UNIT2...|=help] Print debugging information for all units, or for specified units, or print list of units. Can use `~', e.g. `all,~libwww' --no-debug No debugging info [default] --match-exec=CMD [make-template] Execute command when files match CMD is passed to a shell, with environment set up: LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE e.g. 'mkdir -p "${LABEL:-.}/$MATCHPATH" && ln -f "$FILE" "${LABEL:-.}/$MATCHPATH$LEAF"' --no-hex [default] --hex [md5sum, list-template] Output checksums in hexadecimal, not Base64 --gzip [default] Use gzip compression, not --bzip2 Important options: -i --image=FILE Output/input filename for image file -j --jigdo=FILE Input/output filename for jigdo file -t --template=FILE Input/output filename for template file -T --files-from=FILE Read further filenames from FILE (`-' for stdin) -r --report=default|noprogress|quiet|grep Control format of status reports to stderr (or stdout in case of `grep') -f --force Silently delete existent output files --label Label=%1%2path [make-template] Replace name of input file `%1%2path%3a%2file%4txt' (note the `%3') with `Label:a/file%4txt' in output jigdo --uri Label=http://www.site.com [make-template] Add mapping from Label to given URI instead of default `file:' URI [print-missing] Override mapping in input jigdo -0 to -9 Set amount of compression in output template --bzip2 Use bzip2 compression instead of default --gzip --cache=FILE Store/reload information about any files scanned Jigsaw Download License Usage: %1 COMMAND [OPTIONS] [FILES...] Commands: make-template mt Create template and jigdo from image and files make-image mi Recreate image from template and files (can merge files in >1 steps, uses `IMG%2tmp' for --image=IMG) print-missing pm After make-image, print files still missing for the image to be completely recreated --no-cache Do not cache information about scanned files --cache-expiry=SECONDS[h|d|w|m|y] Remove cache entries if last access was longer ago than given amount of time [default 30 days] -h --help Output short help -H --help-all Output this help -h --help Output this help -H --help-all Output more detailed help -v --version Output version info list-template ls Print low-level listing of contents of template data or tmp file print-missing-all pma Print all URIs for each missing file scan sc Update cache with information about supplied files verify ver Check whether image matches checksum from template md5sum md5 Print MD5 checksums similar to md5sum(1) bytes) of ##ShortInfo#Download#Info x x x#Jigdo#Open#Reuse%, %02d:%02d:%02d remaining%1 (at end of %3)%1 (line %2 in %3)%1 days and %2 hours%1 hours and %2 minutes%1 list-template: %2%1 list-template: --template not specified. %1 list-template: Sorry, cannot read from standard input. %1 make-image: Not both --image and --template specified. (Attempt to deduce missing names failed.) %1 make-template: Could not read `%2' (%3)%1 make-template: Could not write `%2' (%3)%1 make-template: Not all of --image, --jigdo, --template specified. (Attempt to deduce missing names failed.) %1 minutes%1 print-missing: %2%1 scan: Please specify a --cache file. %1 verify: %2%1 verify: Invalid template data - corrupted file?%1 verify: Not both --image and --template specified. (Attempt to deduce missing names failed.) %1: %2%1: Could not open `%2' for input: %3%1: Could not open `%2' for output: %3%1: Invalid argument to --label: `%2'%1: Invalid argument to --report (allowed: default noprogress quiet grep)%1: Invalid argument to --uri: `%2'%1: Invalid command `%2' (Must be one of:%3)%1: Invalid size specifier `%2'%1: Invalid time specifier `%2'%1: Out of memory - aborted.%1: Output file `%2' already exists - delete it or use --force%1: Please specify a command%1: Try `%1 -h' or `man jigdo-file' for more information%1: Unit `%2' not found while scanning --debug argument%L1: Please specify `on', `off' or `guess' after --proxy%L1: Try `%L1 -h' or `man jigdo' for more information, , paused, stalled.../s/sec305 Use Proxy400 Bad Request401 Unauthorized403 Forbidden404 Not Found407 Proxy Authentication Required408 Request Timeout500 Internal Server Error501 Not Implemented503 Service Unavailable%1%E1%EF1 (%EF2)?: Reuse filesClose this download and all its children, and remove them from the list belowClose this download and remove it from the list belowContinue all child downloads (after Pause) or Resume them (after Stop or error)Continue download (after Pause) or Resume it (after Stop)Note: Even though some data is already being downloaded, you need to complete step 2 before the actual file download can start!Open of output file failedPause all child downloads, but leave connections to servers openPause download, but leave connection to server openProgress Restart download - the data downloaded so far is discardedRestart download and processing of .jigdo - the .jigdo data downloaded so far (and only it) is discardedReuse local files for generation of %1:Saving to Select servers to download data from:Status Stop download by closing connections of all childrenStop download by closing the connectionType of server: URL URL(s) for that server: (space-separated list, preferred URLs first)Write to output file failed_1: Information_2: Select servers_3: Download_OK, start download!_StartThis is Free Software, distributable under the terms of the GNU GPL v2.Jigsaw Download %F1 Copyright 2001-%2 Richard Atterer http://atterer.net/jigdoA new section must be started after [Include]Ad_vanced: ApplyBBrowse...By default, debug output is disabled except for `assert'. Argument to --debug is a comma-separated list of unit names, or `all' for all units. Just `--debug' is equivalent to`--debug=all'. Output for the listed units is enabled, precede a name with `~' to disable it. Registered units:Cache entry %1CancelCannot create image because of missing filesCannot save to `%LE1' because that already exists as a device/linkCannot save to `%LE1' because that already exists as a directoryChange preferencesChecksum is wrongClear cacheClick on lines below to display corresponding info aboveCommand supplied with --match-exec failedCopied input files to temporary file `%1' - repeat command and supply more files to continueCorrupted input dataCould not create temporary directory: %L1Could not move finished image from `%1' to `%2' (%3)Could not open `%1' for input: %2Could not open `%1' for output: %2Could not open `%L1' for input - excludedCould not open `%L1' for input: %L2Could not open `%L1' for output: %L2Could not open `%LE1' for output: %LE2Could not open cache file: %L1Could not open output file: %LE1Could not read from `%L1': %L2Could not rename `%L1' to `%L2': %L3Could not set up environment for --match-exec commandCould not truncate `%1' (%2)Could not write template dataCould not write to output file: %L1Couldn't find pixmap file: %sData stream active - really abort it?DebianDecompression errorDefault serversDelete/rename the file or use --forceDisplay tooltipsDownload in progress - really abort it?Download is complete - fetched %1 (%2 bytes)Download is pausedDownload of .jigdo file failedDownload was restarted - waiting...Download was stopped manuallyERROR: Checksums do not match, image might be corrupted!Enter a list of domains (separated with spaces) for which you do not want to use the proxies you set up above. Example: "mycompany.com local.network.lan"Enter location of .jigdo file, and destination directory to save files to.ErrorError - could not access temporary fileError - template data's DESC section invalidError accessing cache: %1Error accessing destinationError accessing directory to save toError accessing output fileError during cache expiry: %1. The cache file may be corrupt, consider deleting it.Error during compression: %1Error loading `%LE1': %LE2Error opening file `-' (using standard input not allowed here)Error processing .jigdo file contentsError reading compressed data - %1Error reading filenames from `%1' (%2)Error reading filenames from standard input (%1)Error reading from `%1' (%2)Error reading from directory `%1' (%2)Error reading template data (%1)Error scanning image - abortError while reading `%1' - file will be ignored (%2)Error while writing to `%1' (%2)Error: `%1' does not match checksum in template dataExit from Jigsaw DownloadFTP proxyFailed – see error of child downloadFailed:Field for source URL/filename is emptyFinished - fetched %1Finished - image size is %1 bytes.Found %1 of the %2 files required by the templateHTTP proxyIf the string you enter does not point to a .jigdo file, jigdo behaves just like a normal "download manager" and saves the file in the destination directory, nothing else.If you entered a .jigdo URL above, this must be a directory name. For single-file downloads, it can also be a filename.Ignoring existing temporary file `%1' - %2Input .jigdo data contains invalid control charactersInput .jigdo data is not valid UTF-8Invalid MD5Sum in Parts sectionInvalid Template-MD5Sum argumentInvalid argument to --try-firstInvalid argument to --try-lastInvalid cache entry: `%L1' is not a fileInvalid characters after closing `]'Invalid image nameInvalid template data - corrupted file?Jigdo logoJigsaw DownloadJigsaw Download - half-finished download This directory contains the data for a half-finished download of a .jigdo file. Do not change or delete any of the files in this directory! (Of course you can delete the entire directory if you do not want to continue with the download.) If the jigdo application was stopped and you want it to resume this download, simply enter again the same values you used the first time. In the "URL" field, enter: %1 In the "Save to" field, enter the parent directory of the directory containing this file. Unless you have moved it around, the correct value is: %2 [Download] URL=%1 Destination=%2 Jigsaw Download 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. In addition, as a special exception, the author gives permission to link the jigdo code with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and to distribute the linked executables. Label name contains invalid character `%1'Label name is not followed by `='License for Jigsaw DownloadLine contains more than one URI, ignoring part after `%1' (maybe you need to use "" quotes?)Loop of [Include] directivesMBMatch of `%1' at offset %2Maximum number of simultaneous downloadsMissing argumentNo `=' after first wordNo `[Image]' section found in .jigdo dataNo closing `]' for section nameNon-USOKOK: Checksums match, image is good!Open new .jigdo file or URLOutput file exists - overwrite it or resume download?Please answer the pop-up: Overwrite, resume or cancel?Please enter an "http" or "ftp" URL to download, or the name/URL of a .jigdo file to process.Please note: The copyright notice below only applies to the text of the GNU General Public License; the copyright of the program is as specified above. Also note that jigdo is licensed under GPL version _2_ and no other version. Please specify files/directories on the local filesystem to scan. Any files needed by jigdo templates that are found this way will not have to be downloaded again. Note that any directory to be scanned must contain single files which would otherwise be downloaded - in the case of CD images, you must supply the directory where the CD is mounted, NOT the name of an .iso file.Premature end of template dataRecursive label definitionRemove finished download jobs from list after (seconds)Restarted - waitingRestarting will discard already downloaded dataResume failedResuming `%LE1' is not possible: %LE2Resuming `%LE1' is not possible: It is not a regular fileResuming download - overlap is %1kBResuming... %1kBRetrieving .jigdoRetrieving .templateReuse filesSave .jigdo files in destination directorySave .template files in destination directorySave toScan local filesystems for data needed by jigsaw downloads - useful when "upgrading" e.g. a CD image to a newer versionSection name invalidSection name invalid at character `%1'Select FileSelect or enter server URLs for the different types of servers that the .jigdo file references. You can enter several URLs for each type of server - in this case, jigdo will try to find the fastest.Select the country you live in →Select the country you live in. jigdo will then try out all available servers on your continent. Sooner or later, it should end up using a server that is fast for you.Server sent more data than expectedSettingsSettings - Jigsaw DownloadSize of output file changedSkipping object `%1' (%2)Some data has already been downloaded. Are you sure you want to delete this data and restart the download?Sorry, at the moment the Windows port of jigdo cannot create files bigger than 2 GB. Use the Linux version.Start scanStatusSuccessfully created `%1'Switch to download screen once download of a .jigdo file is completeThe destination `%LE1' cannot be accessed: %LE2The destination `%LE1' is present, but cannot be accessed: %LE2The output file `%LE1' already exists with a size of %2 bytes, it is %3 old. Overwrite deletes the data in this file, whereas Resume can be used to continue an earlier, interrupted download of the same file.Transfer interruptedTry %1 of %2 after %E3UNIMPLEMENTED just click Next...URLUpgrade required - this .jigdo file needs a newer version of the jigdo programUsage: %L1 [OPTIONS] [URL] Options: -h --help Output help -Y --proxy=on/off/guess [guess] Turn proxy on (i.e. use env vars http_proxy, ftp_proxy, all_proxy) or off, or guess (from Mozilla/KDE/wget/lynx settings) -v --version Output version info --debug[=all|=UNIT1,UNIT2...|=help] Print debugging information for all units, or for specified units, or print list of units. Can use `~', e.g. `all,~libwww' --no-debug No debugging info [default] Value redefinedWaitingWaiting...Warning - no files specified. The template will contain the complete image contents!Warning: This does not seem to be a template fileWhen the download was stopped, the length of the output file `%LE1' was %2 bytes, but now it is %3 bytes. Do you really want to resume the download (from byte %3)?Will not create image or temporary file - try again with different input filesWill not reuse existing temporary file `%1' - %2_Abort data stream_Abort download_Automatic selection: Use "netselect" to find the best servers_Awesome!_Cool!_Fantastic!_Next_Overwrite_Restart_Resume_Simple selection, by country name: _Start scan`%1' is not a template file`%1=...' line missing in [Image] sectionfile is too shortit corresponds to a different image/template.it was not created by jigdo-file, or is corrupted.kBno proxyscanningscanning imagesince its creation, the template was regenerated.verifying imagewriting imageProject-Id-Version: jigdo Report-Msgid-Bugs-To: POT-Creation-Date: 2006-05-19 18:21+0200 PO-Revision-Date: 2005-07-05 14:48+0200 Last-Translator: RA -*- coding: utf-8; -*- Language-Team: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copyright © 2001-%1 Richard Atterer Homepage von Jigsaw Download: http://atterer.net/jigdo Copyright © 2001-%1 Richard Atterer Dieses Programm ist Freie Software; Weitergabe und/oder Änderungen sind erlaubt unter den Bedingungen der GNU General Public License, Version 2. Details siehe Datei COPYING oder . .jigdo-Daten werden heruntergeladen - bitte warten... Weitere Optionen (bei BYTES kann man »k«, »M«, »G« anhängen) --merge=DATEI [make-template] Inhalt von DATEI in Ausgabe-jigdo aufnehmen --no-force Bereits existierende Ausgabedateien nicht löschen [Voreinstellung] --min-length=BYTES [Voreinstellung %1] [make-template] Minimale Länge von Dateien, die in der Image-Datei gesucht werden sollen --md5-block-size=BYTES [Voreinstellung %2] Uninteressanter interner Parameter - jigdo-file erzwingt: min-length < md5-block-size --readbuffer=BYTES [Voreinstellung %3k] Datenmenge, die jeweils am Stück eingelesen wird --check-files [Voreinstellung] [make-template,md5sum] Überprüfen, ob Eingabedateien existieren, und Prüfsummen, Datum und Größe überprüfen [make-image] Prüfsummen von Dateien überprüfen, die in die Image-Datei geschrieben werden --no-check-files [make-template,md5sum] Bei Benutzung von --cache, [make-image] Prüfsummen nicht überprüfen --scan-whole-file [scan] Ganze Datei einlesen, nicht nur den ersten Block --no-scan-whole-file [scan] Nur ersten Block einlesen [Voreinstellung] --greedy-matching [make-template] Sofortiges Erkennen kleinerer Dateien anstelle von möglichem (aber unsicheren) Erkennen größerer Dateien später [Voreinstellung] --no-greedy-matching [make-template] Kleineren Match überspringen und dafür größeren bevorzugen, jedoch besteht das Risiko, dass der größere sich als Nicht-Match herausstellt --image-section [Voreinstellung] --no-image-section --servers-section [Voreinstellung] --no-servers-section [make-template] Beim Erzeugen der jigdo-Datei die Abschnitte »[Image]« oder »[Servers]« erzeugen bzw. nicht erzeugen. --debug[=all|=SUBSYSTEM1,SUBSYSTEM2...|=help] [make-template] Debug-Info ausgeben für alle Subsysteme, oder nur für die angegebenen, oder Liste aller Subsysteme ausgeben. »~« zum Abschalten, z.B. »all,~make-template« --no-debug [make-template] Keine Debug-Info [Voreinstellung] --match-exec=CMD [make-template] Kommando ausführen, wenn Dateien gefunden werden. CMD wird an eine Shell übergeben, nachdem diese Umgebungsvariablen gesetzt wurden: LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE\n". Beispielkommando: 'mkdir -p "${LABEL:-.}/$MATCHPATH" && ln -f "$FILE" "${LABEL:-.}/$MATCHPATH$LEAF"' --no-hex [Voreinstellung] --hex [md5sum, list-template] Prüfsummen hexadezimal statt als Base64 ausgeben --gzip [Voreinstellung] Gzip-Kompression verwenden, nicht --bzip Wichtige Optionen: -i --image=DATEI Ausgabe-/Eingabe-Name für Image-Datei -j --jigdo=DATEI Ausgabe-/Eingabe-Name für jigdo-Datei -t --template=DATEI Eingabe-/Ausgabe-Name für template-Datei -T --files-from=DATEI Weitere Dateinamen von DATEI lesen (»-« für stdin) -r --report=default|noprogress|quiet|grep Ausgabeformat für Statusmeldungen an stderr oder stdout bei »grep«) -f --force Bereits existierende Ausgabedateien überschreiben --label Label=%1%2path [make-template] Namen der Eingabedatei »%1%2path%3a%2file%4txt« (man beachte »%3«) durch »Label:a/file%4txt« in Ausgabe-jigdo ersetzen --uri Label=http://www.site.com [make-template] Definition eines URI für »Label« angeben, die anstelle des voreingestellten »file:«-URI verwendet wird [print-missing] Diese Definition von »Label« derjenigen in der Eingabe-jigdo-Datei vorziehen -0 bis -9 Komprimierungsgrad der Ausgabe-template-Datei --bzip2 Bzip2-Kompression verwenden, nicht --gzip --cache=DATEI Puffer für Informationen über gescannte Dateien Lizenz von Jigsaw Download Benutzung: %1 KOMMANDO [OPTIONEN] [DATEIEN...] Kommandos: make-template mt Erzeugt template und jigdo aus Image und Dateien make-image mi Erzeugt Image aus template und Dateien (in mehreren Schritten möglich, benutzt »IMAGE%2tmp« für --image=IMAGE) print-missing pm Gibt nach make-image die Dateien aus, die zur Wiederherstellung des Image noch benötigt werden --no-cache Keine Informationen über gescannte Dateien speichern --cache-expiry=SEKUNDEN[h|d|w|m|y] Puffer-Einträge entfernen, wenn letzter Zugriff länger als diese Zeitspanne her ist [Voreinstellung 30 Tage] -h --help Kurzen Hilfetext ausgeben -H --help-all Diesen Hilfetext ausgeben -h --help Diesen Hilfetext ausgeben -H --help-all Detaillierteren Hilfetext ausgeben -v --version Versionsnummer ausgeben list-template ls Low-level-Informationen über Inhalt von template- oder tmp-Datei ausgeben print-missing-all pma Alle URIs für jede noch fehlende Datei ausgeben scan sc Dateien scannen und Informationen über sie in Puffer aufnehmen verify ver Überprüfen, ob Image-Datei die Prüfsumme aus der template-Datei hat md5sum md5 MD5-Prüfsummen ähnlich md5sum(1) erzeugen Bytes) von ##ShortInfo#Download#Info x x x#Jigdo#Öffnen#Wiederverwenden%, %02d:%02d:%02d verbleibende Zeit%1 (am Ende von %3)%1 (Zeile %2 in %3)%1 Tage und %2 Stunden%1 Stunden und %2 Minuten%1 list-template: %2%1 list-template: --template muss angegeben werden. %1 list-template: Sorry, kann nicht von stdin lesen. %1 make-image: Es wurde nicht sowohl --image als auch --template angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.) %1 make-template: Konnte »%2« nicht lesen (%3)%1 make-template: Konnte »%2« nicht schreiben (%3)%1 make-template: Es wurden nicht alle Optionen --image, --jigdo, --template angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.) %1 Minuten%1 print-missing: %2%1 scan: Bitte geben Sie eine Datei für --cache an. %1 verify: %2%1 verify: Ungültige template-Daten - kaputte Datei?%1 verify: Es wurde nicht sowohl --image als auch --template angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.) %1: %2%1: Fehler beim Öffnen von »%2« zur Eingabe: %3%1: Fehler beim Öffnen von »%2« zur Ausgabe: %3%1: Ungültiges Argument für --label: »%2«%1: Ungültiges Argument für --report (Erlaubt: default noprogress quiet grep)%1: Ungültiges Argument für --uri: »%2«%1: Ungültiges Kommando »%2« (Muss eines der Folgenden sein:%3)%1: Ungültige Größenangabe »%2«%1: Ungültige Zeitspanne »%2«%1: Nicht genug Speicher - Abbruch.%1: Ausgabedatei »%2« existiert bereits - löschen Sie sie oder benutzen Sie --force%1: Bitte geben Sie ein Kommando an%1: Weitere Informationen mit »%1 -h« oder »man jigdo-file«%1: Argument »%2« von --debug: Kein Subsystem dieses Namens%L1: Bitte nach --proxy einen der Parameter »on«, »off« oder »guess« angeben%L1: Weitere Informationen mit »%L1 -h« oder »man jigdo«, , Pause, Stillstand.../s/Sek305 Proxy benutzen400 Ungültige Anfrage401 Keine Berechtigung403 Zugriff untersagt404 Nicht gefunden407 Proxy benötigt Passwort408 Zeitüberschreitung bei der Anfrage500 Interner Server-Fehler501 Nicht implementiert503 Service nicht verfügbar%1%E1%EF1 (%EF2)?: Dateien wiederverwendenDownload Schließen und ihn zusammen mit ihm zugeordneten Downloads aus der Liste unten entfernenDownload Schließen und ihn aus der Liste unten entfernenDownloads Fortsetzen (nach Pause) oder Wieder aufnehmen (nach Stop oder Fehler)Download Fortsetzen (nach Pause) oder Wieder aufnehmen (nach Stop)Hinweis: Obwohl bereits Daten heruntergeladen werden, müssen Sie mit Schritt 2 fertig sein, bevor der eigentliche Download beginnen kann!Öffnen der Ausgabedatei fehlgeschlagenPause der Downloads, Verbindung zum Server bleibt offenPause des Downloads, Verbindung zum Server bleibt offenVerlauf Neustart des Downloads - bisher Heruntergeladenes wird verworfenNeustart des Downloads der .jigdo-Datei - bisher heruntergeladene .jigdo-Daten (und nur sie) werden verworfenLokale Dateien zur Generierung von %1 wiederverwenden:Speichern in Auswahl der Server, von denen heruntergeladen wird:Status Stop der Downloads, Verbindung wird geschlossenStop des Downloads, Verbindung wird geschlossenArt des Servers: URL URL(s) für den Server: (durch Leerzeichen getrennt, bessere Server zuerst)Schreiben in die Ausgabedatei schlug fehl_1: Information_2: Server-Auswahl_3: Herunterladen_OK, Herunterladen starten!_StartDies ist Freie Software, Verbreitung erlaubt gemäß GNU GPL v2Jigsaw Download %F1 Copyright 2001-%2 Richard Atterer http://atterer.net/jigdoNach [Include] muss ein neuer Abschnitt begonnen werden_Fortgeschritten: ÜbernehmenBDurchsuchen...In der Grundeinstellung ist die Debug-Ausgabe abgeschaltet, mit Ausnahme von »assert«. Argument ist eine durch Kommas getrennte Liste von Subsystem-Namen, oder »all« für alle. Einfach »--debug« ist gleichbedeutend mit »--debug=all«. Die Ausgabe wird angeschaltet, außer wenn ein »~« vor dem Subsystem-Namen steht, dann wird sie abgeschaltet. Registrierte Subsysteme:Cache-Eintrag %1SchließenEs fehlen noch Dateien zur Erzeugung der Image-DateiKann nicht in »%L1« speichern - dieses Objekt existiert bereits als Verzeichnis/LinkKann nicht in »%LE1« speichern - dieses Objekt existiert bereits als VerzeichnisEinstellungen ändernFalsche PrüfsummeCache löschenAuf Zeile unten klicken, um Infos über sie oben anzuzeigenKommando von --match-exec gab Fehlerstatus zurückEingabedateien wurden in temporäre Datei »%1« geschrieben - wiederholen Sie das Kommando mit weiteren DateienEingabedaten sind verfälschtTemporäres Verzeichnis konnte nicht erzeugt werden: %L1Fertiges Image konnte nicht von »%1« nach »%2« verschoben werden (%3)Fehler beim Öffnen von »%1« zur Eingabe: %2Fehler beim Öffnen von »%1« zur Ausgabe: %2Fehler beim Öffnen von »%L1« zur Eingabe - Datei wird ignoriertFehler beim Öffnen von »%L1« zur Eingabe: %L2Fehler beim Öffnen von »%L1« zur Ausgabe: %L2Fehler beim Öffnen von »%LE1« zur Ausgabe: %LE2Cache-Datei konnte nicht geöffnet werden: %L1Ausgabedatei konnte nicht geöffnet werden: %LE1Von »%L1« konnte nicht gelesen werden: %L2Datei »%L1« konnte nicht in »%L2« umbenannt werden: %L3Setzen der Umgebungsvariablen für --match-exec fehlgeschlagenDatei »%1« konnte nicht verkürzt werden (%2)Schreiben der template-Daten schlug fehlIn Ausgabedatei konnte nicht geschrieben werden: %L1Pixmap-Datei »%s« konnte nicht gefunden werdenEs wird gerade heruntergeladen - wirklich stoppen?DebianFehler beim DekomprimierenHaupt-ServerLöschen Sie die Datei, benennen Sie sie um oder benutzen Sie --forceTooltips anzeigenEs wird gerade heruntergeladen - wirklich stoppen?Komplett heruntergeladen - %1 (%2 Bytes)Pause beim Herunterladen.jigdo-Datei konnte nicht heruntergeladen werdenHerunterladen wurde neu gestartet - warte...Herunterladen wurde gestopptFEHLER: Prüfsummen nicht gleich, Image könnte Fehler enthalten!Geben Sie eine Liste von Domains an (durch Leerzeichen getrennt), für die die obigen Proxies nicht benutzt werden sollen. Beispiel: "mycompany.com local.network.lan"Geben Sie den Link für die .jigdo-Datei und ein Zielverzeichnis zum Speichern von Dateien an.FehlerFehler - Zugriff auf temporäre Datei schlug fehlFehler - DESC-Abschnitt in template-Daten ungültigFehler beim Cache-Zugriff: %1Fehler beim Zugriff auf AusgabedateiFehler beim Zugriff auf Verzeichnis zum SpeichernFehler beim Zugriff auf AusgabedateiFehler beim Entfernen alter Cache-Einträge: %1. Die Cache-Datei könnte unbrauchbar geworden sein, eventuell sollten Sie sie löschen.Fehler bei der Komprimierung: %1Fehler beim Laden von »%LE1«: %LE2Fehler bei Öffnen von »-« (stdin ist hier nicht erlaubt)Fehler bei der Verarbeitung der .jigdo-DateiFehler beim Lesen der komprimierten Daten - %1Fehler beim Lesen der Dateinamen von »%1« (%2)Fehler beim Lesen der Dateinamen von stdin (%2)Fehler beim Lesen von »%1« (%2)Fehler beim Lesen aus Verzeichnis »%1« (%2)Fehler beim Lesen der Template-Daten - %1Fehler beim Scannen der Image-Datei - AbbruchFehler beim Lesen von »%1« - ignoriere Datei (%2)Fehler beim Schreiben in »%1« (%2)Fehler: »%1« hat nicht die Prüfsumme, die in der Template-Datei stehtJigsaw Download beendenFTP-ProxyFehlgeschlagen – siehe Fehlermeldung des Teil-DownloadsFehlgeschlagen:Eingabefeld für Quell-URL bzw -Dateiname ist leerFertig - %1 heruntergeladenFertig - Image-Größe ist %1 Bytes.%1 der %2 vom Template benötigten Dateien gefundenHTTP-ProxyWenn das Eingegebene keine .jigdo-Datei bezeichnet, verhält sich jigdo wie ein normaler »Download-Manager« und speichert die Datei im Zielverzeichnis, sonst passiert nichts.Wenn Sie oben einen .jigdo-URL eingegeben haben, muss hier ein Verzeichnisname stehen. Für das Herunterladen einzelner Dateien kann es auch ein Dateiname sein.Ignoriere bestehende temporäre Datei »%1« - %2Name des Labels enthält ungültige SteuerzeichenBei den .jigdo-Daten handelt es sich nicht um gültiges UTF-8Ungültige MD5Sum in Parts-AbschnittArgument von Template-MD5Sum ungültigUngültiges Argument für --try-firstUngültiges Argument für --try-lastUngültiger Cache-Eintrag: »%L1« ist keine DateiUngültige Zeichen nach schließendem »]«Ungültiger Image-NameUngültige Template-Daten - Dateiinhalt zerstört?Logo von jigdoJigsaw DownloadJigsaw Download - halbfertiger Download Dieses Verzeichnis enthält die Daten für den halbfertigen Download einer .jigdo-Datei. Verändern oder löschen Sie keine der Dateien in diesem Verzeichnis! (Natürlich können Sie das ganze Verzeichnis löschen, falls Sie mit dem Download nicht fortfahren wollen.) Falls jigdo beendet wurde und Sie jetzt den Download wieder aufnehmen wollen, geben Sie einfach dieselben Werte ein wie beim ersten Mal. Im Feld »URL«, geben Sie ein: %1 Im Feld »Speichern in«, geben Sie das Verzeichnis ein, das dem Verzeichnis übergeordnet ist, in dem sich diese Datei befindet. Wenn Sie dieses Verzeichnis nicht verschoben haben, ist der korrekte Wert: %2 [Download] URL=%1 Destination=%2 Jigsaw Download ist freie Software: Weitergabe und/oder Änderungen sind erlaubt unter den Bedingungen der GNU General Public License, Version 2, herausgegeben von der Free Software Foundation. Als Ausnahme gestattet der Autor zusätzlich das Linken des jigdo-Code mit der »OpenSSL«-Bibliothek des OpenSSL-Projekts (oder mit modifizierten Versionen dieser Bibliothek, die dieselbe Lizenz benutzen) sowie die Verbreitung der resultierenden Programme. Name des Labels enthält ungültiges Zeichen »%1«Es folgt kein »=« auf den Namen des LabelsLizenz von Jigsaw DownloadZeile enthält mehr als einen URI, Teil nach »%1« wird ignoriert (vielleicht ""-Quotes verwenden?)Schleife von [Include]-DirectivenMBDatei »%1« an Offset %2 gefundenMaximum für Anzahl paralleler DownloadsArgument fehltKein »=« nach dem ersten WortKein »[Image]«-Abschnitt in den .jigdo-Daten gefundenKeine schließende »]« für Abschnitt-NameNon-USOKOK: Prüfsummen stimmen überein, Image-Datei ist in Ordnung!Neue .jigdo-Datei oder URL öffnenAusgabedatei existiert bereits - überschreiben oder Download wieder aufnehmen?Bitte beantworten Sie im Dialog: Überschreiben, Wieder aufnehmen oder Abbruch?Bitte geben Sie einen »http«- oder »ftp«-URL oder den Namen/URL einer .jigdo-Datei ein.Hinweis: Der Copyright-Vermerk unten bezieht sich nur auf den Text der GNU General Public License; das Copyright des Programms ist wie oben angegeben. Zusätzlich ist noch zu beachten, dass jigdo als Lizenz nur Version _2_ der GPL und keine andere Version zulässt. Bitte lokale Dateien/Verzeichnisse zum Durchsuchen angeben. Von jigdo-Templates benötigte Dateien, die auf diesem Weg gefunden werden, brauchen nicht erneut heruntergeladen werden. Hinweis: Durchsuchte Verzeichnisse müssen Dateien enthalten, die andernfalls heruntergeladen werden müssten - bei CD-Images müssen Sie also das Verzeichnis angeben, unter dem die CD eingebunden (gemountet) ist, NICHT den Namen einer .iso-Datei.Vorzeitiges Ende der Template-DatenRekursive Label-DefinitionBeendete Downloads aus Liste entfernen nach (Sekunden)Neustart - warte...Beim Neustart geht bereits Heruntergeladenes verlorenWieder aufnehmen gescheitertWiederaufnehmen von »%LE1« nicht möglich: %LE2Wiederaufnehmen von »%LE1« nicht möglich: Ist keine normale DateiHerunterladen wieder aufgenommen - überlappender Bereich %1kBWieder aufnehmen... %1kBLade .jigdo herunterLade .template herunterDateien.jigdo-Dateien im Zielverzeichnis speichern.template-Dateien im Zielverzeichnis speichernSpeichern inAuf Platte nach Dateien suchen, die von Downloads benötigt werden - nützlich, wenn z.B. CD-Images auf eine neue Version "aufgefrischt" werdenAbschnitt-Name ungültigAbschnitt-Name enthält ungültiges Zeichen »%1«Datei auswählenWählen Sie für die verschiedenen Arten von Servern jeweils Server-URLs aus oder geben Sie sie ein. Pro Art können mehrere URLs eingegeben werden - in diesem Fall wird jigdo versuchen, den schnellsten zu finden.Land auswählen, in dem Sie wohnen →Wählen Sie das Land aus, in dem Sie wohnen. jigdo wird dann alle verfügbaren Server auf ihrem Kontinent ausprobieren. Früher oder später sollte ein schneller gefunden sein.Server schickte mehr Daten als erwartetEinstellungenEinstellungen - Jigsaw DownloadGröße der Ausgabedatei hat sich geändertÜberspringe Objekt »%1« (%2)Es wurden bereits Daten heruntergeladen. Sind Sie sicher, dass sie diese löschen und das Herunterladen neu starten wollen?Leider kann die Windows-Version von jigdo keine Dateien erzeugen, die größer als 2 GB sind. Benutzen Sie die Linux-Version.Scan startenStatus»%1« wurde erfolgreich erzeugtZum Download-Dialog schalten, sobald die .jigdo-Datei komplett heruntergeladen wurdeAuf Ausgabe »%LE1« kann nicht zugegriffen werden: %LE2Ausgabe »%LE1« vorhanden, aber nicht zugreifbar: %LE2Die Ausgabedatei »%LE1« existiert bereits, sie ist %2 Bytes lang und %3 alt. Überschreiben löscht die Daten in der Datei, während Wieder aufnehmen einen unterbrochenen Download derselben Datei fortsetzt.Übertragung unterbrochenVersuch %1 von %2 nach %E3NICHT IMPLEMENTIERT Bitte einfach Weiter klicken...URLUpgrade nötig - diese .jigdo-Datei funktioniert nur mit einer neueren Version des jigdo-ProgrammsBenutzung: %L1 [OPTIONEN] [URL] Optionen: -h --help Hilfetext ausgeben -Y --proxy=on/off/guess [guess] Proxy an-/ausschalten (d.h., Umgebungsvariablen http_proxy/ftp_proxy/all_proxy benutzen) oder Einstellungen von Mozilla/KDE/wget/lynx übernehmen -v --version Versionsnummer ausgeben --debug[=all|SUBSYSTEM1,SUBSYSTEM2...|=help] Debug-Info ausgeben für alle Subsysteme, oder nur für die angegebenen, oder Liste aller Subsysteme ausgeben. »~« zum Abschalten, z.B. »all,~make-template« --no-debug Keine Debug-Info [Voreinstellung] Wert mehrmals definiertWarteWarte...Achtung - es wurden keine Dateien angegeben. Das Template wird die kompletten Image-Daten enthalten!Achtung: Dies scheint keine Template-Datei zu seinAls das Herunterladen gestoppt wurde, betrug die Länge der Ausgabedatei »%LE1« %2 Bytes, aber jetzt sind es %3 Bytes. Wollen Sie den Download wirklich wieder (von Byte %3 an) aufnehmen?Es wird keine Image-Datei oder temporäre Datei erzeugt - versuchen Sie es mit anderen EingabedateienExistierende temporäre Datei »%1« wird nicht verwendet - %2_Abbruch des DatenstromsHerunterladen _stoppen_Automatische Auswahl: Benutze "netselect", um die schnellsten Server zu finden_Wahnsinn!_Cool!_Fantastisch!_Weiter_Ueberschreiben_Neustart_Wieder aufnehmen_Einfache Auswahl, nach Land: _Scan starten»%1« ist keine Template-DateiZeile »%1=...« fehlt im [Image]-AbschnittDatei ist zu kleinsie gehört zu einem anderen Image/Template.sie wurde nicht von jigdo-file erzeugt oder ist zerstört.kBkein ProxyScanneScanne Imageseit seiner Erzeugung wurde das Template neu erzeugt.Verifiziere ImageSchreibe Imagejigdo-0.7.3/po/de.po0000644000175000017500000017530510433370613014010 0ustar richardrichard# -*-coding: utf-8;-*- # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: jigdo\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-05-19 18:21+0200\n" "PO-Revision-Date: 2005-07-05 14:48+0200\n" "Last-Translator: RA -*- coding: utf-8; -*-\n" "Language-Team: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: gtk/interface.cc:427 gtk/interface.cc:943 msgid "\n" msgstr "\n" #: gtk/gui.cc:201 msgid "" "\n" "\tCopyright © 2001-%1 Richard Atterer \n" "\tJigsaw Download homepage: http://atterer.net/jigdo\n" "\n" msgstr "" "\n" "\tCopyright © 2001-%1 Richard Atterer \n" "\tHomepage von Jigsaw Download: http://atterer.net/jigdo\n" "\n" #: jigdo-file.cc:288 msgid "" "\n" "Copyright (C) 2001-%1 Richard Atterer \n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License, version 2. See\n" "the file COPYING or for details.\n" "\n" msgstr "" "\n" "Copyright © 2001-%1 Richard Atterer \n" "Dieses Programm ist Freie Software; Weitergabe und/oder Änderungen sind\n" "erlaubt unter den Bedingungen der GNU General Public License, Version 2.\n" "Details siehe Datei COPYING oder .\n" #: gtk/gtk-makeimage.cc:25 msgid "" "\n" "Downloading .jigdo data - please wait..." msgstr "" "\n" ".jigdo-Daten werden heruntergeladen - bitte warten..." #: jigdo-file.cc:357 msgid "" "\n" "Further options: (can append 'k', 'M', 'G' to any BYTES argument)\n" " --merge=FILE [make-template] Add FILE contents to output jigdo\n" " --no-force Do not delete existent output files [default]\n" " --min-length=BYTES [default %1]\n" " [make-template] Minimum length of files to search\n" " for in image data\n" " --md5-block-size=BYTES [default %2]\n" " Uninteresting internal parameter -\n" " jigdo-file enforces: min-length < md5-block-size\n" " --readbuffer=BYTES [default %3k]\n" " Amount of data to read at a time\n" " --check-files [default]\n" " [make-template,md5sum] Check if files exist and\n" " get or verify checksums, date and size\n" " [make-image] Verify checksum of files written to\n" " image\n" " --no-check-files [make-template,md5sum] when used with --cache,\n" " [make-image] Do not verify checksums of files\n" " --scan-whole-file [scan] Scan whole file instead of only first block\n" " --no-scan-whole-file [scan] Scan only first block [default]\n" " --greedy-matching [make-template] Prefer immediate matches of small\n" " files now over possible (but uncertain) matches of \n" " larger files later [default]\n" " --no-greedy-matching\n" " [make-template] Skip a smaller match and prefer a\n" " pending larger one, with the risk of missing both\n" " --image-section [default]\n" " --no-image-section\n" " --servers-section [default]\n" " --no-servers-section\n" " [make-template] When creating the jigdo file, do\n" " or do not add the sections `[Image]' or `[Servers]'\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n" " --match-exec=CMD [make-template] Execute command when files match\n" " CMD is passed to a shell, with environment set up:\n" " LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE\n" " e.g. 'mkdir -p \"${LABEL:-.}/$MATCHPATH\" && ln -f \"$FILE" "\" \"${LABEL:-.}/$MATCHPATH$LEAF\"'\n" " --no-hex [default]\n" " --hex [md5sum, list-template] Output checksums in\n" " hexadecimal, not Base64\n" " --gzip [default] Use gzip compression, not --bzip2\n" msgstr "" "\n" "Weitere Optionen (bei BYTES kann man »k«, »M«, »G« anhängen)\n" " --merge=DATEI [make-template] Inhalt von DATEI in Ausgabe-jigdo\n" " aufnehmen\n" " --no-force Bereits existierende Ausgabedateien nicht löschen\n" " [Voreinstellung]\n" " --min-length=BYTES [Voreinstellung %1]\n" " [make-template] Minimale Länge von Dateien, die in\n" " der Image-Datei gesucht werden sollen\n" " --md5-block-size=BYTES [Voreinstellung %2]\n" " Uninteressanter interner Parameter - jigdo-file\n" " erzwingt: min-length < md5-block-size\n" " --readbuffer=BYTES [Voreinstellung %3k]\n" " Datenmenge, die jeweils am Stück eingelesen wird\n" " --check-files [Voreinstellung]\n" " [make-template,md5sum] Überprüfen, ob Eingabedateien\n" " existieren, und Prüfsummen, Datum und Größe überprüfen\n" " [make-image] Prüfsummen von Dateien überprüfen,\n" " die in die Image-Datei geschrieben werden\n" " --no-check-files [make-template,md5sum] Bei Benutzung von --cache,\n" " [make-image] Prüfsummen nicht überprüfen\n" " --scan-whole-file [scan] Ganze Datei einlesen, nicht nur den ersten Block\n" " --no-scan-whole-file [scan] Nur ersten Block einlesen [Voreinstellung]\n" " --greedy-matching [make-template] Sofortiges Erkennen kleinerer Dateien\n" " anstelle von möglichem (aber unsicheren) Erkennen\n" " größerer Dateien später [Voreinstellung]\n" " --no-greedy-matching\n" " [make-template] Kleineren Match überspringen und dafür\n" " größeren bevorzugen, jedoch besteht das Risiko, dass der\n" " größere sich als Nicht-Match herausstellt\n" " --image-section [Voreinstellung]\n" " --no-image-section\n" " --servers-section [Voreinstellung]\n" " --no-servers-section\n" " [make-template] Beim Erzeugen der jigdo-Datei die\n" " Abschnitte »[Image]« oder »[Servers]« erzeugen bzw.\n" " nicht erzeugen.\n" " --debug[=all|=SUBSYSTEM1,SUBSYSTEM2...|=help]\n" " [make-template] Debug-Info ausgeben für alle Subsysteme,\n" " oder nur für die angegebenen, oder Liste aller " "Subsysteme\n" " ausgeben. »~« zum Abschalten, z.B. »all,~make-template«\n" " --no-debug [make-template] Keine Debug-Info [Voreinstellung]\n" " --match-exec=CMD [make-template] Kommando ausführen, wenn Dateien " "gefunden\n" " werden. CMD wird an eine Shell übergeben, nachdem diese\n" " Umgebungsvariablen gesetzt wurden: LABEL, LABELPATH,\n" " MATCHPATH, LEAF, MD5SUM, FILE\\n\". Beispielkommando:\n" " 'mkdir -p \"${LABEL:-.}/$MATCHPATH\" && ln -f \"$FILE\" " "\"${LABEL:-.}/$MATCHPATH$LEAF\"'\n" " --no-hex [Voreinstellung]\n" " --hex [md5sum, list-template] Prüfsummen hexadezimal statt\n" " als Base64 ausgeben\n" " --gzip [Voreinstellung] Gzip-Kompression verwenden, nicht --" "bzip\n" #: jigdo-file.cc:318 msgid "" "\n" "Important options:\n" " -i --image=FILE Output/input filename for image file\n" " -j --jigdo=FILE Input/output filename for jigdo file\n" " -t --template=FILE\n" " Input/output filename for template file\n" " -T --files-from=FILE\n" " Read further filenames from FILE (`-' for stdin)\n" " -r --report=default|noprogress|quiet|grep\n" " Control format of status reports to stderr (or\n" " stdout in case of `grep')\n" " -f --force Silently delete existent output files\n" " --label Label=%1%2path\n" " [make-template] Replace name of input file\n" " `%1%2path%3a%2file%4txt' (note the `%3') with\n" " `Label:a/file%4txt' in output jigdo\n" " --uri Label=http://www.site.com\n" " [make-template] Add mapping from Label to given\n" " URI instead of default `file:' URI\n" " [print-missing] Override mapping in input jigdo\n" " -0 to -9 Set amount of compression in output template\n" " --bzip2 Use bzip2 compression instead of default --gzip\n" " --cache=FILE Store/reload information about any files scanned\n" msgstr "" "\n" "Wichtige Optionen:\n" " -i --image=DATEI\n" " Ausgabe-/Eingabe-Name für Image-Datei\n" " -j --jigdo=DATEI\n" " Ausgabe-/Eingabe-Name für jigdo-Datei\n" " -t --template=DATEI\n" " Eingabe-/Ausgabe-Name für template-Datei\n" " -T --files-from=DATEI\n" " Weitere Dateinamen von DATEI lesen (»-« für stdin)\n" " -r --report=default|noprogress|quiet|grep\n" " Ausgabeformat für Statusmeldungen an stderr\n" " oder stdout bei »grep«)\n" " -f --force Bereits existierende Ausgabedateien überschreiben\n" " --label Label=%1%2path\n" " [make-template] Namen der Eingabedatei\n" " »%1%2path%3a%2file%4txt« (man beachte »%3«) durch\n" " »Label:a/file%4txt« in Ausgabe-jigdo ersetzen\n" " --uri Label=http://www.site.com\n" " [make-template] Definition eines URI für »Label«\n" " angeben, die anstelle des voreingestellten »file:«-URI\n" " verwendet wird\n" " [print-missing] Diese Definition von »Label«\n" " derjenigen in der Eingabe-jigdo-Datei vorziehen\n" " -0 bis -9 Komprimierungsgrad der Ausgabe-template-Datei\n" " --bzip2 Bzip2-Kompression verwenden, nicht --gzip\n" " --cache=DATEI\n" " Puffer für Informationen über gescannte Dateien\n" #: gtk/gui.cc:200 msgid "" "\n" "Jigsaw Download License\n" msgstr "" "\n" "Lizenz von Jigsaw Download\n" #: jigdo-file.cc:296 msgid "" "\n" "Usage: %1 COMMAND [OPTIONS] [FILES...]\n" "Commands:\n" " make-template mt Create template and jigdo from image and files\n" " make-image mi Recreate image from template and files (can merge\n" " files in >1 steps, uses `IMG%2tmp' for --image=IMG)\n" " print-missing pm After make-image, print files still missing for\n" " the image to be completely recreated\n" msgstr "" "\n" "Benutzung: %1 KOMMANDO [OPTIONEN] [DATEIEN...]\n" "Kommandos:\n" " make-template mt Erzeugt template und jigdo aus Image und Dateien\n" " make-image mi Erzeugt Image aus template und Dateien (in\n" " mehreren Schritten möglich, benutzt »IMAGE%2tmp«\n" " für --image=IMAGE)\n" " print-missing pm Gibt nach make-image die Dateien aus, die zur\n" " Wiederherstellung des Image noch benötigt werden\n" #: jigdo-file.cc:344 msgid "" " --no-cache Do not cache information about scanned files\n" " --cache-expiry=SECONDS[h|d|w|m|y]\n" " Remove cache entries if last access was longer\n" " ago than given amount of time [default 30 days]\n" " -h --help Output short help\n" " -H --help-all Output this help\n" msgstr "" " --no-cache Keine Informationen über gescannte Dateien\n" " speichern\n" " --cache-expiry=SEKUNDEN[h|d|w|m|y]\n" " Puffer-Einträge entfernen, wenn letzter Zugriff\n" " länger als diese Zeitspanne her ist\n" " [Voreinstellung 30 Tage]\n" " -h --help Kurzen Hilfetext ausgeben\n" " -H --help-all Diesen Hilfetext ausgeben\n" #: jigdo-file.cc:351 msgid "" " -h --help Output this help\n" " -H --help-all Output more detailed help\n" msgstr "" " -h --help Diesen Hilfetext ausgeben\n" " -H --help-all Detaillierteren Hilfetext ausgeben\n" #: jigdo-file.cc:354 msgid " -v --version Output version info" msgstr " -v --version Versionsnummer ausgeben" #: jigdo-file.cc:314 msgid "" " list-template ls Print low-level listing of contents of template\n" " data or tmp file\n" msgstr "" " list-template ls Low-level-Informationen über Inhalt von\n" " template- oder tmp-Datei ausgeben\n" #: jigdo-file.cc:306 msgid "" " print-missing-all pma\n" " Print all URIs for each missing file\n" " scan sc Update cache with information about supplied files\n" msgstr "" " print-missing-all pma\n" " Alle URIs für jede noch fehlende Datei ausgeben\n" " scan sc Dateien scannen und Informationen über sie in\n" " Puffer aufnehmen\n" #: jigdo-file.cc:310 msgid "" " verify ver Check whether image matches checksum from template\n" " md5sum md5 Print MD5 checksums similar to md5sum(1)\n" msgstr "" " verify ver Überprüfen, ob Image-Datei die Prüfsumme aus der\n" " template-Datei hat\n" " md5sum md5 MD5-Prüfsummen ähnlich md5sum(1) erzeugen\n" #: util/progress.cc:86 msgid " bytes)" msgstr " Bytes)" #: util/progress.cc:65 util/progress.cc:77 util/progress.cc:83 msgid " of " msgstr " von " #: gtk/interface.cc:242 gtk/interface.cc:259 gtk/interface.cc:509 #: gtk/interface.cc:519 gtk/interface.cc:538 gtk/interface.cc:548 #: gtk/interface.cc:1061 gtk/interface.cc:1071 gtk/interface.cc:1081 #: gtk/interface.cc:1091 msgid "#" msgstr "#" #: gtk/interface.cc:623 msgid "#ShortInfo" msgstr "#ShortInfo" #: gtk/interface.cc:585 msgid "#Download" msgstr "#Download" #: gtk/interface.cc:631 msgid "" "#Info\n" "x\n" "x\n" "x" msgstr "" "#Info\n" "x\n" "x\n" "x" #: gtk/interface.cc:1107 msgid "#Jigdo" msgstr "#Jigdo" #: gtk/interface.cc:351 msgid "#Open" msgstr "#Öffnen" #: gtk/interface.cc:413 msgid "#Reuse" msgstr "#Wiederverwenden" #: util/progress.cc:75 msgid "%, " msgstr "%, " #: util/progress.cc:95 #, c-format msgid "%02d:%02d:%02d remaining" msgstr "%02d:%02d:%02d verbleibende Zeit" #: job/jigdo-io.cc:219 job/jigdo-io.cc:228 msgid "%1 (at end of %3)" msgstr "%1 (am Ende von %3)" #: job/jigdo-io.cc:219 job/jigdo-io.cc:228 msgid "%1 (line %2 in %3)" msgstr "%1 (Zeile %2 in %3)" #: gtk/gtk-single-url.cc:330 msgid "%1 days and %2 hours" msgstr "%1 Tage und %2 Stunden" #: gtk/gtk-single-url.cc:332 msgid "%1 hours and %2 minutes" msgstr "%1 Stunden und %2 Minuten" #: jigdo-file-cmd.cc:347 msgid "%1 list-template: %2" msgstr "%1 list-template: %2" #: jigdo-file-cmd.cc:321 msgid "%1 list-template: --template not specified.\n" msgstr "%1 list-template: --template muss angegeben werden.\n" #: jigdo-file-cmd.cc:326 msgid "%1 list-template: Sorry, cannot read from standard input.\n" msgstr "%1 list-template: Sorry, kann nicht von stdin lesen.\n" #: jigdo-file-cmd.cc:285 msgid "" "%1 make-image: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" "%1 make-image: Es wurde nicht sowohl --image als auch --template\n" "angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.)\n" #: jigdo-file-cmd.cc:231 msgid "%1 make-template: Could not read `%2' (%3)" msgstr "%1 make-template: Konnte »%2« nicht lesen (%3)" #: jigdo-file-cmd.cc:272 msgid "%1 make-template: Could not write `%2' (%3)" msgstr "%1 make-template: Konnte »%2« nicht schreiben (%3)" #: jigdo-file-cmd.cc:204 jigdo-file-cmd.cc:444 msgid "" "%1 make-template: Not all of --image, --jigdo, --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" "%1 make-template: Es wurden nicht alle Optionen --image, --jigdo,\n" "--template angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.)\n" #: gtk/gtk-single-url.cc:334 msgid "%1 minutes" msgstr "%1 Minuten" #: jigdo-file-cmd.cc:481 msgid "%1 print-missing: %2" msgstr "%1 print-missing: %2" #: jigdo-file-cmd.cc:559 msgid "%1 scan: Please specify a --cache file.\n" msgstr "%1 scan: Bitte geben Sie eine Datei für --cache an.\n" #: jigdo-file-cmd.cc:385 msgid "%1 verify: %2" msgstr "%1 verify: %2" #: jigdo-file-cmd.cc:391 msgid "%1 verify: Invalid template data - corrupted file?" msgstr "%1 verify: Ungültige template-Daten - kaputte Datei?" #: jigdo-file-cmd.cc:364 msgid "" "%1 verify: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" "%1 verify: Es wurde nicht sowohl --image als auch --template\n" "angegeben. (Versuch, die Dateinamen zu raten, schlug fehl.)\n" #: jigdo-file-cmd.cc:353 jigdo-file-cmd.cc:397 msgid "%1: %2" msgstr "%1: %2" #: jigdo-file-cmd.cc:45 jigdo-file-cmd.cc:67 msgid "%1: Could not open `%2' for input: %3" msgstr "%1: Fehler beim Öffnen von »%2« zur Eingabe: %3" #: jigdo-file-cmd.cc:98 jigdo-file-cmd.cc:113 msgid "%1: Could not open `%2' for output: %3" msgstr "%1: Fehler beim Öffnen von »%2« zur Ausgabe: %3" #: jigdo-file-cmd.cc:156 msgid "%1: Invalid argument to --label: `%2'" msgstr "%1: Ungültiges Argument für --label: »%2«" #: jigdo-file.cc:583 msgid "" "%1: Invalid argument to --report (allowed: default noprogress quiet grep)" msgstr "" "%1: Ungültiges Argument für --report (Erlaubt: default noprogress quiet grep)" #: jigdo-file-cmd.cc:138 msgid "%1: Invalid argument to --uri: `%2'" msgstr "%1: Ungültiges Argument für --uri: »%2«" #: jigdo-file.cc:689 msgid "" "%1: Invalid command `%2'\n" "(Must be one of:%3)" msgstr "" "%1: Ungültiges Kommando »%2«\n" "(Muss eines der Folgenden sein:%3)" #: jigdo-file.cc:427 msgid "%1: Invalid size specifier `%2'" msgstr "%1: Ungültige Größenangabe »%2«" #: jigdo-file.cc:453 msgid "%1: Invalid time specifier `%2'" msgstr "%1: Ungültige Zeitspanne »%2«" #: jigdo-file.cc:481 msgid "%1: Out of memory - aborted." msgstr "%1: Nicht genug Speicher - Abbruch." #: jigdo-file-cmd.cc:84 msgid "%1: Output file `%2' already exists - delete it or use --force" msgstr "" "%1: Ausgabedatei »%2« existiert bereits - löschen Sie sie oder benutzen Sie " "--force" #: jigdo-file.cc:646 msgid "%1: Please specify a command" msgstr "%1: Bitte geben Sie ein Kommando an" #: jigdo-file.cc:736 msgid "%1: Try `%1 -h' or `man jigdo-file' for more information" msgstr "%1: Weitere Informationen mit »%1 -h« oder »man jigdo-file«" #: util/log.cc:94 msgid "%1: Unit `%2' not found while scanning --debug argument" msgstr "%1: Argument »%2« von --debug: Kein Subsystem dieses Namens" #: gtk/jigdo.cc:89 msgid "%L1: Please specify `on', `off' or `guess' after --proxy" msgstr "" "%L1: Bitte nach --proxy einen der Parameter »on«, »off« oder »guess« angeben" #: gtk/jigdo.cc:56 msgid "%L1: Try `%L1 -h' or `man jigdo' for more information" msgstr "%L1: Weitere Informationen mit »%L1 -h« oder »man jigdo«" #: gtk/gtk-single-url.cc:543 util/progress.cc:101 msgid ", " msgstr ", " #: gtk/gtk-single-url.cc:537 msgid ", paused" msgstr ", Pause" #: gtk/gtk-single-url.cc:541 msgid ", stalled" msgstr ", Stillstand" #: gtk/interface.cc:705 msgid "..." msgstr "..." #: gtk/gtk-single-url.cc:545 msgid "/s" msgstr "/s" #: util/progress.cc:103 msgid "/sec" msgstr "/Sek" #: net/download.cc:375 msgid "305 Use Proxy" msgstr "305 Proxy benutzen" #: net/download.cc:376 msgid "400 Bad Request" msgstr "400 Ungültige Anfrage" #: net/download.cc:377 msgid "401 Unauthorized" msgstr "401 Keine Berechtigung" #: net/download.cc:378 msgid "403 Forbidden" msgstr "403 Zugriff untersagt" #: net/download.cc:379 msgid "404 Not Found" msgstr "404 Nicht gefunden" #: net/download.cc:380 msgid "407 Proxy Authentication Required" msgstr "407 Proxy benötigt Passwort" #: net/download.cc:381 msgid "408 Request Timeout" msgstr "408 Zeitüberschreitung bei der Anfrage" #: net/download.cc:382 msgid "500 Internal Server Error" msgstr "500 Interner Server-Fehler" #: net/download.cc:383 msgid "501 Not Implemented" msgstr "501 Nicht implementiert" #: net/download.cc:384 msgid "503 Service Unavailable" msgstr "503 Service nicht verfügbar" #: gtk/gtk-single-url.cc:420 msgid "%1" msgstr "%1" #: gtk/gtk-makeimage.cc:164 gtk/gtk-single-url.cc:453 #, c-format msgid "%E1" msgstr "%E1" #: gtk/gtk-makeimage.cc:227 #, c-format msgid "%EF1 (%EF2)" msgstr "%EF1 (%EF2)" #: gtk/interface.cc:764 msgid "?: Reuse files" msgstr "?: Dateien wiederverwenden" #: gtk/gui.cc:370 msgid "" "Close this download and all its children, and remove them from the " "list below" msgstr "" "Download Schließen und ihn zusammen mit ihm zugeordneten Downloads " "aus der Liste unten entfernen" #: gtk/gui.cc:318 msgid "Close this download and remove it from the list below" msgstr "Download Schließen und ihn aus der Liste unten entfernen" #: gtk/gui.cc:349 msgid "" "Continue all child downloads (after Pause) or Resume them " "(after Stop or error)" msgstr "" "Downloads Fortsetzen (nach Pause) oder Wieder aufnehmen (nach " "Stop oder Fehler)" #: gtk/gui.cc:299 msgid "Continue download (after Pause) or Resume it (after Stop)" msgstr "" "Download Fortsetzen (nach Pause) oder Wieder aufnehmen (nach " "Stop)" #: gtk/interface.cc:613 msgid "" "Note: Even though some data is already being downloaded, you\n" "need to complete step 2 before the actual file download can start!" msgstr "" "Hinweis: Obwohl bereits Daten heruntergeladen werden, müssen Sie mit\n" "Schritt 2 fertig sein, bevor der eigentliche Download beginnen kann!" #: gtk/gtk-single-url.cc:169 gtk/gtk-single-url.cc:197 msgid "Open of output file failed" msgstr "Öffnen der Ausgabedatei fehlgeschlagen" #: gtk/gui.cc:354 msgid "Pause all child downloads, but leave connections to servers open" msgstr "Pause der Downloads, Verbindung zum Server bleibt offen" #: gtk/gui.cc:304 msgid "Pause download, but leave connection to server open" msgstr "Pause des Downloads, Verbindung zum Server bleibt offen" #: gtk/interface.cc:576 gtk/interface.cc:1034 msgid "Progress " msgstr "Verlauf " #: gtk/gui.cc:313 msgid "Restart download - the data downloaded so far is discarded" msgstr "" "Neustart des Downloads - bisher Heruntergeladenes wird verworfen" #: gtk/gui.cc:364 msgid "" "Restart download and processing of .jigdo - the .jigdo data downloaded so far (and only it) is discarded" msgstr "" "Neustart des Downloads der .jigdo-Datei - bisher " "heruntergeladene .jigdo-Daten (und nur sie) werden verworfen" #: gtk/interface.cc:681 msgid "Reuse local files for generation of %1:" msgstr "Lokale Dateien zur Generierung von %1 wiederverwenden:" #: gtk/interface.cc:567 gtk/interface.cc:1025 msgid "Saving to " msgstr "Speichern in " #: gtk/interface.cc:779 msgid "Select servers to download data from:" msgstr "Auswahl der Server, von denen heruntergeladen wird:" #: gtk/interface.cc:529 gtk/interface.cc:1043 msgid "Status " msgstr "Status " #: gtk/gui.cc:359 msgid "Stop download by closing connections of all children" msgstr "Stop der Downloads, Verbindung wird geschlossen" #: gtk/gui.cc:309 msgid "Stop download by closing the connection" msgstr "Stop des Downloads, Verbindung wird geschlossen" #: gtk/interface.cc:843 msgid "Type of server: " msgstr "Art des Servers: " #: gtk/interface.cc:558 gtk/interface.cc:1052 msgid "URL " msgstr "URL " #: gtk/interface.cc:851 msgid "" "URL(s) for that server: (space-separated list, preferred URLs first)" msgstr "" "URL(s) für den Server: (durch Leerzeichen getrennt, bessere Server " "zuerst)" #: gtk/gtk-single-url.cc:493 msgid "Write to output file failed" msgstr "Schreiben in die Ausgabedatei schlug fehl" #: gtk/interface.cc:667 msgid "_1: Information" msgstr "_1: Information" #: gtk/interface.cc:928 msgid "_2: Select servers" msgstr "_2: Server-Auswahl" #: gtk/interface.cc:1101 msgid "_3: Download" msgstr "_3: Herunterladen" #: gtk/interface.cc:923 msgid "_OK, start download!" msgstr "_OK, Herunterladen starten!" #: gtk/interface.cc:291 msgid "_Start" msgstr "_Start" #: gtk/gui.cc:108 msgid "" "This is Free Software, distributable under the terms " "of the GNU GPL v2." msgstr "" "Dies ist Freie Software, Verbreitung erlaubt gemäß " "GNU GPL v2" #: gtk/gui.cc:99 msgid "" "Jigsaw " "Download %F1\n" "Copyright 2001-%2 Richard Atterer\n" "http://atterer.net/jigdo" msgstr "" "Jigsaw " "Download %F1\n" "Copyright 2001-%2 Richard Atterer\n" "http://atterer.net/jigdo" #: job/jigdo-io.cc:534 msgid "A new section must be started after [Include]" msgstr "Nach [Include] muss ein neuer Abschnitt begonnen werden" #: gtk/interface.cc:820 msgid "Ad_vanced: " msgstr "_Fortgeschritten: " #: gtk/interface.cc:1518 msgid "Apply" msgstr "Übernehmen" #: util/progress.cc:38 msgid "B" msgstr "B" #: gtk/interface.cc:334 gtk/interface.cc:340 gtk/interface.cc:385 msgid "Browse..." msgstr "Durchsuchen..." #: util/log.cc:101 msgid "" "By default, debug output is disabled except for `assert'. Argument\n" "to --debug is a comma-separated list of unit names, or `all' for\n" "all units. Just `--debug' is equivalent to`--debug=all'. Output for\n" "the listed units is enabled, precede a name with `~' to disable it.\n" "Registered units:" msgstr "" "In der Grundeinstellung ist die Debug-Ausgabe abgeschaltet, mit\n" "Ausnahme von »assert«. Argument ist eine durch Kommas getrennte Liste\n" "von Subsystem-Namen, oder »all« für alle. Einfach »--debug« ist\n" "gleichbedeutend mit »--debug=all«. Die Ausgabe wird angeschaltet, außer\n" "wenn ein »~« vor dem Subsystem-Namen steht, dann wird sie abgeschaltet.\n" "Registrierte Subsysteme:" #: job/makeimagedl.cc:229 job/makeimagedl.cc:241 msgid "Cache entry %1" msgstr "Cache-Eintrag %1" #: gtk/interface.cc:1523 msgid "Cancel" msgstr "Schließen" #: mkimage.cc:868 msgid "Cannot create image because of missing files" msgstr "Es fehlen noch Dateien zur Erzeugung der Image-Datei" #: gtk/gtk-single-url.cc:98 #, c-format msgid "Cannot save to `%LE1' because that already exists as a device/link" msgstr "" "Kann nicht in »%L1« speichern - dieses Objekt existiert bereits als " "Verzeichnis/Link" #: gtk/gtk-single-url.cc:84 #, c-format msgid "Cannot save to `%LE1' because that already exists as a directory" msgstr "" "Kann nicht in »%LE1« speichern - dieses Objekt existiert bereits als " "Verzeichnis" #: gtk/interface.cc:198 msgid "Change preferences" msgstr "Einstellungen ändern" #: util/gunzip.cc:240 util/gunzip.cc:251 util/gunzip.cc:262 util/gunzip.cc:273 msgid "Checksum is wrong" msgstr "Falsche Prüfsumme" #: gtk/interface.cc:408 msgid "Clear cache" msgstr "Cache löschen" #: gtk/joblist.cc:116 msgid "Click on lines below to display corresponding info above" msgstr "Auf Zeile unten klicken, um Infos über sie oben anzuzeigen" #: mktemplate.cc:435 msgid "Command supplied with --match-exec failed" msgstr "Kommando von --match-exec gab Fehlerstatus zurück" #: mkimage.cc:664 msgid "" "Copied input files to temporary file `%1' - repeat command and supply more " "files to continue" msgstr "" "Eingabedateien wurden in temporäre Datei »%1« geschrieben - wiederholen Sie " "das Kommando mit weiteren Dateien" #: zstream.cc:202 zstream.cc:232 msgid "Corrupted input data" msgstr "Eingabedaten sind verfälscht" #: job/makeimagedl.cc:127 msgid "Could not create temporary directory: %L1" msgstr "Temporäres Verzeichnis konnte nicht erzeugt werden: %L1" #: mkimage.cc:673 msgid "Could not move finished image from `%1' to `%2' (%3)" msgstr "Fertiges Image konnte nicht von »%1« nach »%2« verschoben werden (%3)" #: jigdoconfig.cc:61 msgid "Could not open `%1' for input: %2" msgstr "Fehler beim Öffnen von »%1« zur Eingabe: %2" #: mkimage.cc:911 msgid "Could not open `%1' for output: %2" msgstr "Fehler beim Öffnen von »%1« zur Ausgabe: %2" #: scan.cc:284 msgid "Could not open `%L1' for input - excluded" msgstr "Fehler beim Öffnen von »%L1« zur Eingabe - Datei wird ignoriert" #: job/cached-url.cc:134 msgid "Could not open `%L1' for input: %L2" msgstr "Fehler beim Öffnen von »%L1« zur Eingabe: %L2" #: job/makeimagedl.cc:267 msgid "Could not open `%L1' for output: %L2" msgstr "Fehler beim Öffnen von »%L1« zur Ausgabe: %L2" #: gtk/gtk-single-url.cc:120 #, c-format msgid "Could not open `%LE1' for output: %LE2" msgstr "Fehler beim Öffnen von »%LE1« zur Ausgabe: %LE2" #: scan.cc:166 msgid "Could not open cache file: %L1" msgstr "Cache-Datei konnte nicht geöffnet werden: %L1" #: gtk/gtk-single-url.cc:170 gtk/gtk-single-url.cc:198 #, c-format msgid "Could not open output file: %LE1" msgstr "Ausgabedatei konnte nicht geöffnet werden: %LE1" #: job/cached-url.cc:162 msgid "Could not read from `%L1': %L2" msgstr "Von »%L1« konnte nicht gelesen werden: %L2" #: job/makeimagedl.cc:406 msgid "Could not rename `%L1' to `%L2': %L3" msgstr "Datei »%L1« konnte nicht in »%L2« umbenannt werden: %L3" #: mktemplate.cc:427 msgid "Could not set up environment for --match-exec command" msgstr "Setzen der Umgebungsvariablen für --match-exec fehlgeschlagen" #: mkimage.cc:645 msgid "Could not truncate `%1' (%2)" msgstr "Datei »%1« konnte nicht verkürzt werden (%2)" #: mktemplate.cc:934 zstream.cc:85 zstream.cc:96 msgid "Could not write template data" msgstr "Schreiben der template-Daten schlug fehl" #: gtk/gtk-single-url.cc:494 msgid "Could not write to output file: %L1" msgstr "In Ausgabedatei konnte nicht geschrieben werden: %L1" #: gtk/support.cc:92 gtk/support.cc:116 #, c-format msgid "Couldn't find pixmap file: %s" msgstr "Pixmap-Datei »%s« konnte nicht gefunden werden" #: gtk/gtk-single-url.cc:658 msgid "Data stream active - really abort it?" msgstr "Es wird gerade heruntergeladen - wirklich stoppen?" #: gtk/interface.cc:859 msgid "Debian" msgstr "Debian" #: util/gunzip.cc:28 msgid "Decompression error" msgstr "Fehler beim Dekomprimieren" #: gtk/interface.cc:1634 msgid "Default servers" msgstr "Haupt-Server" #: mkimage.cc:771 msgid "Delete/rename the file or use --force" msgstr "Löschen Sie die Datei, benennen Sie sie um oder benutzen Sie --force" #: gtk/interface.cc:1553 msgid "Display tooltips" msgstr "Tooltips anzeigen" #: gtk/gtk-single-url.cc:655 msgid "Download in progress - really abort it?" msgstr "Es wird gerade heruntergeladen - wirklich stoppen?" #: gtk/gtk-single-url.cc:404 msgid "Download is complete - fetched %1 (%2 bytes)" msgstr "Komplett heruntergeladen - %1 (%2 Bytes)" #: gtk/gtk-single-url.cc:236 msgid "Download is paused" msgstr "Pause beim Herunterladen" #: job/jigdo-io.cc:113 msgid "Download of .jigdo file failed" msgstr ".jigdo-Datei konnte nicht heruntergeladen werden" #: gtk/gtk-single-url.cc:741 msgid "Download was restarted - waiting..." msgstr "Herunterladen wurde neu gestartet - warte..." #: gtk/gtk-single-url.cc:265 msgid "Download was stopped manually" msgstr "Herunterladen wurde gestoppt" #: jigdo-file-cmd.cc:413 msgid "ERROR: Checksums do not match, image might be corrupted!" msgstr "FEHLER: Prüfsummen nicht gleich, Image könnte Fehler enthalten!" #: gtk/interface.cc:1647 gtk/interface.cc:1654 msgid "" "Enter a list of domains (separated with spaces) for which you do not want to " "use the proxies you set up above. Example: \"mycompany.com local.network.lan" "\"" msgstr "" "Geben Sie eine Liste von Domains an (durch Leerzeichen getrennt), für die " "die obigen Proxies nicht benutzt werden sollen. Beispiel: \"mycompany.com " "local.network.lan\"" #: gtk/interface.cc:346 msgid "" "Enter location of .jigdo file, and destination directory to save " "files to." msgstr "" "Geben Sie den Link für die .jigdo-Datei und ein Zielverzeichnis zum " "Speichern von Dateien an." #: net/download.cc:355 msgid "Error" msgstr "Fehler" #: mkimage.cc:621 msgid "Error - could not access temporary file" msgstr "Fehler - Zugriff auf temporäre Datei schlug fehl" #: mkimage.cc:549 msgid "Error - template data's DESC section invalid" msgstr "Fehler - DESC-Abschnitt in template-Daten ungültig" #: scan.cc:265 scan.hh:433 msgid "Error accessing cache: %1" msgstr "Fehler beim Cache-Zugriff: %1" #: gtk/gtk-single-url.cc:83 gtk/gtk-single-url.cc:97 gtk/gtk-single-url.cc:119 #: gtk/jobline.cc:70 msgid "Error accessing destination" msgstr "Fehler beim Zugriff auf Ausgabedatei" #: gtk/jobline.cc:84 msgid "Error accessing directory to save to" msgstr "Fehler beim Zugriff auf Verzeichnis zum Speichern" #: gtk/gtk-single-url.cc:601 msgid "Error accessing output file" msgstr "Fehler beim Zugriff auf Ausgabedatei" #: scan.cc:204 msgid "" "Error during cache expiry: %1. The cache file may be corrupt, consider " "deleting it." msgstr "" "Fehler beim Entfernen alter Cache-Einträge: %1. Die Cache-Datei könnte " "unbrauchbar geworden sein, eventuell sollten Sie sie löschen." #: mktemplate.cc:924 msgid "Error during compression: %1" msgstr "Fehler bei der Komprimierung: %1" #: gtk/gui.cc:247 #, c-format msgid "Error loading `%LE1': %LE2" msgstr "Fehler beim Laden von »%LE1«: %LE2" #: scan.cc:281 msgid "Error opening file `-' (using standard input not allowed here)" msgstr "Fehler bei Öffnen von »-« (stdin ist hier nicht erlaubt)" #: job/jigdo-io.cc:239 msgid "Error processing .jigdo file contents" msgstr "Fehler bei der Verarbeitung der .jigdo-Datei" #: zstream.cc:286 msgid "Error reading compressed data - %1" msgstr "Fehler beim Lesen der komprimierten Daten - %1" #: recursedir.cc:94 msgid "Error reading filenames from `%1' (%2)" msgstr "Fehler beim Lesen der Dateinamen von »%1« (%2)" #: recursedir.cc:91 msgid "Error reading filenames from standard input (%1)" msgstr "Fehler beim Lesen der Dateinamen von stdin (%2)" #: mkimage.cc:395 mktemplate.cc:323 msgid "Error reading from `%1' (%2)" msgstr "Fehler beim Lesen von »%1« (%2)" #: recursedir.cc:52 msgid "Error reading from directory `%1' (%2)" msgstr "Fehler beim Lesen aus Verzeichnis »%1« (%2)" #: mkimage.cc:224 msgid "Error reading template data (%1)" msgstr "Fehler beim Lesen der Template-Daten - %1" #: jigdo-file.cc:172 msgid "Error scanning image - abort" msgstr "Fehler beim Scannen der Image-Datei - Abbruch" #: scan.cc:409 msgid "Error while reading `%1' - file will be ignored (%2)" msgstr "Fehler beim Lesen von »%1« - ignoriere Datei (%2)" #: mkimage.cc:558 msgid "Error while writing to `%1' (%2)" msgstr "Fehler beim Schreiben in »%1« (%2)" #: mkimage.cc:407 msgid "Error: `%1' does not match checksum in template data" msgstr "Fehler: »%1« hat nicht die Prüfsumme, die in der Template-Datei steht" #: gtk/interface.cc:205 msgid "Exit from Jigsaw Download" msgstr "Jigsaw Download beenden" #: gtk/interface.cc:1605 msgid "FTP proxy" msgstr "FTP-Proxy" #: job/makeimagedl.cc:455 msgid "Failed – see error of child download" msgstr "Fehlgeschlagen – siehe Fehlermeldung des Teil-Downloads" #: gtk/gtk-makeimage.cc:166 gtk/gtk-single-url.cc:457 msgid "Failed:" msgstr "Fehlgeschlagen:" #: gtk/jobline.cc:49 msgid "Field for source URL/filename is empty" msgstr "Eingabefeld für Quell-URL bzw -Dateiname ist leer" #: gtk/gtk-single-url.cc:408 msgid "Finished - fetched %1" msgstr "Fertig - %1 heruntergeladen" #: jigdo-file.cc:182 msgid "Finished - image size is %1 bytes." msgstr "Fertig - Image-Größe ist %1 Bytes." #: mkimage.cc:839 msgid "Found %1 of the %2 files required by the template" msgstr "%1 der %2 vom Template benötigten Dateien gefunden" #: gtk/interface.cc:1596 msgid "HTTP proxy" msgstr "HTTP-Proxy" #: gtk/interface.cc:325 msgid "" "If the string you enter does not point to a .jigdo file, jigdo behaves just " "like a normal \"download manager\" and saves the file in the destination " "directory, nothing else." msgstr "" "Wenn das Eingegebene keine .jigdo-Datei bezeichnet, verhält sich jigdo wie " "ein normaler »Download-Manager« und speichert die Datei im Zielverzeichnis, " "sonst passiert nichts." #: gtk/interface.cc:332 msgid "" "If you entered a .jigdo URL above, this must be a directory name. For single-" "file downloads, it can also be a filename." msgstr "" "Wenn Sie oben einen .jigdo-URL eingegeben haben, muss hier ein " "Verzeichnisname stehen. Für das Herunterladen einzelner Dateien kann es auch " "ein Dateiname sein." #: mkimage.cc:963 msgid "Ignoring existing temporary file `%1' - %2" msgstr "Ignoriere bestehende temporäre Datei »%1« - %2" #: job/jigdodownload.cc:136 job/jigdo-io.cc:182 msgid "Input .jigdo data contains invalid control characters" msgstr "Name des Labels enthält ungültige Steuerzeichen" #: job/jigdodownload.cc:124 job/jigdodownload.cc:146 job/jigdo-io.cc:97 #: job/jigdo-io.cc:171 job/jigdo-io.cc:192 msgid "Input .jigdo data is not valid UTF-8" msgstr "Bei den .jigdo-Daten handelt es sich nicht um gültiges UTF-8" #: job/jigdo-io.cc:610 job/jigdo-io.cc:617 msgid "Invalid MD5Sum in Parts section" msgstr "Ungültige MD5Sum in Parts-Abschnitt" #: job/jigdo-io.cc:581 job/jigdo-io.cc:588 msgid "Invalid Template-MD5Sum argument" msgstr "Argument von Template-MD5Sum ungültig" #: job/url-mapping.cc:239 msgid "Invalid argument to --try-first" msgstr "Ungültiges Argument für --try-first" #: job/url-mapping.cc:248 msgid "Invalid argument to --try-last" msgstr "Ungültiges Argument für --try-last" #: job/makeimagedl.cc:291 job/makeimagedl.cc:329 job/makeimagedl.cc:352 msgid "Invalid cache entry: `%L1' is not a file" msgstr "Ungültiger Cache-Eintrag: »%L1« ist keine Datei" #: job/jigdo-io.cc:445 util/configfile.cc:119 msgid "Invalid characters after closing `]'" msgstr "Ungültige Zeichen nach schließendem »]«" #: job/jigdo-io.cc:566 msgid "Invalid image name" msgstr "Ungültiger Image-Name" #: mkimage.cc:85 mkimage.cc:108 mkimage.cc:126 mkimage.cc:218 mkimage.cc:230 #: mkimage.cc:234 msgid "Invalid template data - corrupted file?" msgstr "Ungültige Template-Daten - Dateiinhalt zerstört?" #: gtk/interface.cc:1241 msgid "Jigdo logo" msgstr "Logo von jigdo" #: gtk/interface.cc:157 msgid "Jigsaw Download" msgstr "Jigsaw Download" #: job/makeimagedl.cc:144 msgid "" "Jigsaw Download - half-finished download\n" "\n" "This directory contains the data for a half-finished download of a\n" ".jigdo file. Do not change or delete any of the files in this\n" "directory! (Of course you can delete the entire directory if you do\n" "not want to continue with the download.)\n" "\n" "If the jigdo application was stopped and you want it to resume this\n" "download, simply enter again the same values you used the first time.\n" "\n" "In the \"URL\" field, enter:\n" " %1\n" "\n" "In the \"Save to\" field, enter the parent directory of the directory\n" "containing this file. Unless you have moved it around, the correct\n" "value is:\n" " %2\n" "\n" "\n" "[Download]\n" "URL=%1\n" "Destination=%2\n" msgstr "" "Jigsaw Download - halbfertiger Download\n" "\n" "Dieses Verzeichnis enthält die Daten für den halbfertigen Download\n" "einer .jigdo-Datei. Verändern oder löschen Sie keine der Dateien in\n" "diesem Verzeichnis! (Natürlich können Sie das ganze Verzeichnis\n" "löschen, falls Sie mit dem Download nicht fortfahren wollen.)\n" "\n" "Falls jigdo beendet wurde und Sie jetzt den Download wieder aufnehmen\n" "wollen, geben Sie einfach dieselben Werte ein wie beim ersten Mal.\n" "\n" "Im Feld »URL«, geben Sie ein:\n" "\n" " %1\n" "\n" "Im Feld »Speichern in«, geben Sie das Verzeichnis ein, das dem\n" "Verzeichnis übergeordnet ist, in dem sich diese Datei befindet. Wenn\n" "Sie dieses Verzeichnis nicht verschoben haben, ist der korrekte Wert:\n" " %2\n" "\n" "\n" "[Download]\n" "URL=%1\n" "Destination=%2\n" #: gtk/gui.cc:208 msgid "" "Jigsaw Download 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.\n" "\n" "In addition, as a special exception, the author gives permission to link the " "jigdo code with the OpenSSL project's \"OpenSSL\" library (or with modified " "versions of it that use the same license as the \"OpenSSL\" library), and to " "distribute the linked executables.\n" msgstr "" "Jigsaw Download ist freie Software: Weitergabe und/oder Änderungen sind " "erlaubt unter den Bedingungen der GNU General Public License, Version 2, " "herausgegeben von der Free Software Foundation.\n" "\n" "Als Ausnahme gestattet der Autor zusätzlich das Linken des jigdo-Code mit " "der »OpenSSL«-Bibliothek des OpenSSL-Projekts (oder mit modifizierten " "Versionen dieser Bibliothek, die dieselbe Lizenz benutzen) sowie die " "Verbreitung der resultierenden Programme.\n" #: util/configfile.cc:80 msgid "Label name contains invalid character `%1'" msgstr "Name des Labels enthält ungültiges Zeichen »%1«" #: util/configfile.cc:75 msgid "Label name is not followed by `='" msgstr "Es folgt kein »=« auf den Namen des Labels" #: gtk/interface.cc:1702 msgid "License for Jigsaw Download" msgstr "Lizenz von Jigsaw Download" #: jigdoconfig.cc:155 msgid "" "Line contains more than one URI, ignoring part after `%1' (maybe you need to " "use \"\" quotes?)" msgstr "" "Zeile enthält mehr als einen URI, Teil nach »%1« wird ignoriert (vielleicht " "\"\"-Quotes verwenden?)" #: job/jigdo-io.cc:479 msgid "Loop of [Include] directives" msgstr "Schleife von [Include]-Directiven" #: util/progress.cc:50 util/progress.cc:53 msgid "MB" msgstr "MB" #: jigdo-file.cc:176 msgid "Match of `%1' at offset %2" msgstr "Datei »%1« an Offset %2 gefunden" #: gtk/interface.cc:1542 msgid "Maximum number of simultaneous downloads" msgstr "Maximum für Anzahl paralleler Downloads" #: job/jigdo-io.cc:539 job/jigdo-io.cc:560 job/jigdo-io.cc:568 #: job/jigdo-io.cc:572 job/jigdo-io.cc:603 job/jigdo-io.cc:624 msgid "Missing argument" msgstr "Argument fehlt" #: job/jigdo-io.cc:403 msgid "No `=' after first word" msgstr "Kein »=« nach dem ersten Wort" #: job/jigdo-io.cc:377 msgid "No `[Image]' section found in .jigdo data" msgstr "Kein »[Image]«-Abschnitt in den .jigdo-Daten gefunden" #: job/jigdo-io.cc:415 job/jigdo-io.cc:421 util/configfile.cc:95 #: util/configfile.cc:103 msgid "No closing `]' for section name" msgstr "Keine schließende »]« für Abschnitt-Name" #: gtk/interface.cc:866 msgid "Non-US" msgstr "Non-US" #: gtk/interface.cc:1513 msgid "OK" msgstr "OK" #: jigdo-file-cmd.cc:408 msgid "OK: Checksums match, image is good!" msgstr "OK: Prüfsummen stimmen überein, Image-Datei ist in Ordnung!" #: gtk/interface.cc:180 msgid "Open new .jigdo file or URL" msgstr "Neue .jigdo-Datei oder URL öffnen" #: gtk/gtk-single-url.cc:337 msgid "Output file exists - overwrite it or resume download?" msgstr "" "Ausgabedatei existiert bereits - überschreiben oder Download wieder " "aufnehmen?" #: gtk/gtk-single-url.cc:351 msgid "Please answer the pop-up: Overwrite, resume or cancel?" msgstr "" "Bitte beantworten Sie im Dialog: Überschreiben, Wieder aufnehmen oder " "Abbruch?" #: gtk/jobline.cc:50 msgid "" "Please enter an \"http\" or \"ftp\" URL to download, or the name/URL of a " ".jigdo file to process." msgstr "" "Bitte geben Sie einen »http«- oder »ftp«-URL oder den Namen/URL einer ." "jigdo-Datei ein." #: gtk/gui.cc:221 msgid "" "Please note: The copyright notice below only applies to the text of the GNU " "General Public License; the copyright of the program is as specified above. " "Also note that jigdo is licensed under GPL version _2_ and no other " "version.\n" "\n" "\n" msgstr "" "Hinweis: Der Copyright-Vermerk unten bezieht sich nur auf den Text der GNU " "General Public License; das Copyright des Programms ist wie oben angegeben. " "Zusätzlich ist noch zu beachten, dass jigdo als Lizenz nur Version _2_ der " "GPL und keine andere Version zulässt.\n" "\n" "\n" #: gtk/interface.cc:371 msgid "" "Please specify files/directories on the local filesystem to scan. Any files " "needed by jigdo templates that are found this way will not have to be " "downloaded again.\n" "Note that any directory to be scanned must contain single files which would " "otherwise be downloaded - in the case of CD images, you must supply the " "directory where the CD is mounted, NOT the name of an .iso file." msgstr "" "Bitte lokale Dateien/Verzeichnisse zum Durchsuchen angeben. Von jigdo-" "Templates benötigte Dateien, die auf diesem Weg gefunden werden, brauchen " "nicht erneut heruntergeladen werden.\n" "Hinweis: Durchsuchte Verzeichnisse müssen Dateien enthalten, die andernfalls " "heruntergeladen werden müssten - bei CD-Images müssen Sie also das " "Verzeichnis angeben, unter dem die CD eingebunden (gemountet) ist, NICHT den " "Namen einer .iso-Datei." #: mkimage.cc:485 msgid "Premature end of template data" msgstr "Vorzeitiges Ende der Template-Daten" #: job/url-mapping.cc:191 msgid "Recursive label definition" msgstr "Rekursive Label-Definition" #: gtk/interface.cc:1579 msgid "Remove finished download jobs from list after (seconds)" msgstr "Beendete Downloads aus Liste entfernen nach (Sekunden)" #: gtk/gtk-single-url.cc:742 msgid "Restarted - waiting" msgstr "Neustart - warte..." #: gtk/gtk-single-url.cc:712 msgid "Restarting will discard already downloaded data" msgstr "Beim Neustart geht bereits Heruntergeladenes verloren" #: job/single-url.cc:99 msgid "Resume failed" msgstr "Wieder aufnehmen gescheitert" #: gtk/gtk-single-url.cc:595 #, c-format msgid "Resuming `%LE1' is not possible: %LE2" msgstr "Wiederaufnehmen von »%LE1« nicht möglich: %LE2" #: gtk/gtk-single-url.cc:598 #, c-format msgid "Resuming `%LE1' is not possible: It is not a regular file" msgstr "Wiederaufnehmen von »%LE1« nicht möglich: Ist keine normale Datei" #: gtk/gtk-single-url.cc:152 gtk/gtk-single-url.cc:206 msgid "Resuming download - overlap is %1kB" msgstr "Herunterladen wieder aufgenommen - überlappender Bereich %1kB" #: job/single-url.cc:221 msgid "Resuming... %1kB" msgstr "Wieder aufnehmen... %1kB" #: job/makeimagedl.cc:531 msgid "Retrieving .jigdo" msgstr "Lade .jigdo herunter" #: job/makeimagedl.cc:570 msgid "Retrieving .template" msgstr "Lade .template herunter" #: gtk/interface.cc:182 msgid "Reuse files" msgstr "Dateien" #: gtk/interface.cc:1567 msgid "Save .jigdo files in destination directory" msgstr ".jigdo-Dateien im Zielverzeichnis speichern" #: gtk/interface.cc:1571 msgid "Save .template files in destination directory" msgstr ".template-Dateien im Zielverzeichnis speichern" #: gtk/interface.cc:311 msgid "Save to" msgstr "Speichern in" #: gtk/interface.cc:187 msgid "" "Scan local filesystems for data needed by jigsaw downloads - useful when " "\"upgrading\" e.g. a CD image to a newer version" msgstr "" "Auf Platte nach Dateien suchen, die von Downloads benötigt werden - " "nützlich, wenn z.B. CD-Images auf eine neue Version \"aufgefrischt\" werden" #: job/jigdo-io.cc:442 msgid "Section name invalid" msgstr "Abschnitt-Name ungültig" #: util/configfile.cc:112 msgid "Section name invalid at character `%1'" msgstr "Abschnitt-Name enthält ungültiges Zeichen »%1«" #: gtk/interface.cc:1433 msgid "Select File" msgstr "Datei auswählen" #: gtk/interface.cc:823 msgid "" "Select or enter server URLs for the different types of servers that the ." "jigdo file references. You can enter several URLs for each type of server - " "in this case, jigdo will try to find the fastest." msgstr "" "Wählen Sie für die verschiedenen Arten von Servern jeweils Server-URLs aus " "oder geben Sie sie ein. Pro Art können mehrere URLs eingegeben werden - in " "diesem Fall wird jigdo versuchen, den schnellsten zu finden." #: gtk/interface.cc:810 msgid "Select the country you live in →" msgstr "Land auswählen, in dem Sie wohnen →" #: gtk/interface.cc:797 msgid "" "Select the country you live in. jigdo will then try out all available " "servers on your continent. Sooner or later, it should end up using a server " "that is fast for you." msgstr "" "Wählen Sie das Land aus, in dem Sie wohnen. jigdo wird dann alle verfügbaren " "Server auf ihrem Kontinent ausprobieren. Früher oder später sollte ein " "schneller gefunden sein." #: job/single-url.cc:145 msgid "Server sent more data than expected" msgstr "Server schickte mehr Daten als erwartet" #: gtk/interface.cc:1533 msgid "Settings" msgstr "Einstellungen" #: gtk/interface.cc:1496 msgid "Settings - Jigsaw Download" msgstr "Einstellungen - Jigsaw Download" #: gtk/gtk-single-url.cc:612 msgid "Size of output file changed" msgstr "Größe der Ausgabedatei hat sich geändert" #: recursedir.cc:46 msgid "Skipping object `%1' (%2)" msgstr "Überspringe Objekt »%1« (%2)" #: gtk/gtk-single-url.cc:713 msgid "" "Some data has already been downloaded. Are you sure you want to delete this " "data and restart the download?" msgstr "" "Es wurden bereits Daten heruntergeladen. Sind Sie sicher, dass sie diese " "löschen und das Herunterladen neu starten wollen?" #: mkimage.cc:926 msgid "" "Sorry, at the moment the Windows port of jigdo cannot create files bigger " "than 2 GB. Use the Linux version." msgstr "" "Leider kann die Windows-Version von jigdo keine Dateien erzeugen, die größer " "als 2 GB sind. Benutzen Sie die Linux-Version." #: gtk/interface.cc:403 msgid "Start scan" msgstr "Scan starten" #: gtk/joblist.cc:96 msgid "Status" msgstr "Status" #: mkimage.cc:884 mkimage.cc:941 msgid "Successfully created `%1'" msgstr "»%1« wurde erfolgreich erzeugt" #: gtk/interface.cc:1558 msgid "Switch to download screen once download of a .jigdo file is complete" msgstr "" "Zum Download-Dialog schalten, sobald die .jigdo-Datei komplett " "heruntergeladen wurde" #: gtk/jobline.cc:85 #, c-format msgid "The destination `%LE1' cannot be accessed: %LE2" msgstr "Auf Ausgabe »%LE1« kann nicht zugegriffen werden: %LE2" #: gtk/jobline.cc:71 #, c-format msgid "The destination `%LE1' is present, but cannot be accessed: %LE2" msgstr "Ausgabe »%LE1« vorhanden, aber nicht zugreifbar: %LE2" #: gtk/gtk-single-url.cc:338 msgid "" "The output file `%LE1' already exists with a size of %2 bytes, it is %3 " "old.\n" "Overwrite deletes the data in this file, whereas Resume can be " "used to continue an earlier, interrupted download of the same file." msgstr "" "Die Ausgabedatei »%LE1« existiert bereits, sie ist %2 Bytes lang und %3 " "alt.\n" "Überschreiben löscht die Daten in der Datei, während Wieder " "aufnehmen einen unterbrochenen Download derselben Datei fortsetzt." #: net/download.cc:362 msgid "Transfer interrupted" msgstr "Übertragung unterbrochen" #: gtk/gtk-single-url.cc:449 msgid "Try %1 of %2 after %E3" msgstr "Versuch %1 von %2 nach %E3" #: gtk/interface.cc:687 msgid "" "UNIMPLEMENTED\n" "just click Next..." msgstr "" "NICHT IMPLEMENTIERT\n" "Bitte einfach Weiter klicken..." #: gtk/interface.cc:302 msgid "URL" msgstr "URL" #: job/jigdo-io.cc:548 jigdoconfig.cc:297 msgid "" "Upgrade required - this .jigdo file needs a newer version of the jigdo " "program" msgstr "" "Upgrade nötig - diese .jigdo-Datei funktioniert nur mit einer neueren " "Version des jigdo-Programms" #: gtk/jigdo.cc:107 msgid "" "Usage: %L1 [OPTIONS] [URL]\n" "Options:\n" " -h --help Output help\n" " -Y --proxy=on/off/guess [guess]\n" " Turn proxy on (i.e. use env vars http_proxy,\n" " ftp_proxy, all_proxy) or off, or guess (from\n" " Mozilla/KDE/wget/lynx settings)\n" " -v --version Output version info\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n" msgstr "" "Benutzung: %L1 [OPTIONEN] [URL]\n" "Optionen:\n" " -h --help Hilfetext ausgeben\n" " -Y --proxy=on/off/guess [guess]\n" " Proxy an-/ausschalten (d.h., Umgebungsvariablen\n" " http_proxy/ftp_proxy/all_proxy benutzen) oder\n" " Einstellungen von Mozilla/KDE/wget/lynx übernehmen\n" " -v --version Versionsnummer ausgeben\n" " --debug[=all|SUBSYSTEM1,SUBSYSTEM2...|=help]\n" " Debug-Info ausgeben für alle Subsysteme, oder nur für " "die\n" " angegebenen, oder Liste aller Subsysteme ausgeben.\n" " »~« zum Abschalten, z.B. »all,~make-template«\n" " --no-debug Keine Debug-Info [Voreinstellung]\n" #: job/jigdo-io.cc:559 job/jigdo-io.cc:571 job/jigdo-io.cc:592 #: job/jigdo-io.cc:596 msgid "Value redefined" msgstr "Wert mehrmals definiert" #: gtk/gtk-makeimage.cc:65 gtk/gtk-single-url.cc:69 msgid "Waiting" msgstr "Warte" #: gtk/gtk-single-url.cc:68 gtk/gtk-single-url.cc:129 msgid "Waiting..." msgstr "Warte..." #: jigdo-file-cmd.cc:211 msgid "" "Warning - no files specified. The template will contain the complete image " "contents!" msgstr "" "Achtung - es wurden keine Dateien angegeben. Das Template wird die " "kompletten Image-Daten enthalten!" #: jigdo-file-cmd.cc:339 jigdo-file-cmd.cc:380 msgid "Warning: This does not seem to be a template file" msgstr "Achtung: Dies scheint keine Template-Datei zu sein" #: gtk/gtk-single-url.cc:613 msgid "" "When the download was stopped, the length of the output file `%LE1' was %2 " "bytes, but now it is %3 bytes. Do you really want to resume the download " "(from byte %3)?" msgstr "" "Als das Herunterladen gestoppt wurde, betrug die Länge der Ausgabedatei »%" "LE1« %2 Bytes, aber jetzt sind es %3 Bytes. Wollen Sie den Download wirklich " "wieder (von Byte %3 an) aufnehmen?" #: mkimage.cc:860 msgid "" "Will not create image or temporary file - try again with different input " "files" msgstr "" "Es wird keine Image-Datei oder temporäre Datei erzeugt - versuchen Sie es " "mit anderen Eingabedateien" #: mkimage.cc:766 msgid "Will not reuse existing temporary file `%1' - %2" msgstr "Existierende temporäre Datei »%1« wird nicht verwendet - %2" #: gtk/gtk-single-url.cc:659 msgid "_Abort data stream" msgstr "_Abbruch des Datenstroms" #: gtk/gtk-single-url.cc:656 msgid "_Abort download" msgstr "Herunterladen _stoppen" #: gtk/interface.cc:785 msgid "_Automatic selection: Use \"netselect\" to find the best servers" msgstr "" "_Automatische Auswahl: Benutze \"netselect\", um die schnellsten Server zu " "finden" #: gtk/jobline.cc:127 msgid "_Awesome!" msgstr "_Wahnsinn!" #: gtk/jobline.cc:126 msgid "_Cool!" msgstr "_Cool!" #: gtk/jobline.cc:128 msgid "_Fantastic!" msgstr "_Fantastisch!" #: gtk/interface.cc:662 gtk/interface.cc:760 msgid "_Next" msgstr "_Weiter" #: gtk/gtk-single-url.cc:345 msgid "_Overwrite" msgstr "_Ueberschreiben" #: gtk/gtk-single-url.cc:716 msgid "_Restart" msgstr "_Neustart" #: gtk/gtk-single-url.cc:346 gtk/gtk-single-url.cc:617 msgid "_Resume" msgstr "_Wieder aufnehmen" #: gtk/interface.cc:794 msgid "_Simple selection, by country name: " msgstr "_Einfache Auswahl, nach Land: " #: gtk/interface.cc:728 msgid "_Start scan" msgstr "_Scan starten" #: mkimage.cc:688 msgid "`%1' is not a template file" msgstr "»%1« ist keine Template-Datei" #: job/jigdo-io.cc:462 msgid "`%1=...' line missing in [Image] section" msgstr "Zeile »%1=...« fehlt im [Image]-Abschnitt" #: mkimage.cc:394 msgid "file is too short" msgstr "Datei ist zu klein" #: mkimage.cc:714 msgid "it corresponds to a different image/template." msgstr "sie gehört zu einem anderen Image/Template." #: mkimage.cc:711 msgid "it was not created by jigdo-file, or is corrupted." msgstr "sie wurde nicht von jigdo-file erzeugt oder ist zerstört." #: util/progress.cc:42 util/progress.cc:46 msgid "kB" msgstr "kB" #: gtk/interface.cc:1614 msgid "no proxy" msgstr "kein Proxy" #: jigdo-file.cc:112 msgid "scanning" msgstr "Scanne" #: jigdo-file.cc:137 msgid "scanning image" msgstr "Scanne Image" #: mkimage.cc:716 mkimage.cc:720 msgid "since its creation, the template was regenerated." msgstr "seit seiner Erzeugung wurde das Template neu erzeugt." #: jigdo-file.cc:152 msgid "verifying image" msgstr "Verifiziere Image" #: jigdo-file.cc:168 msgid "writing image" msgstr "Schreibe Image" #~ msgid "Contacting %L1" #~ msgstr "Kontaktiere %L1" #~ msgid "Logging in" #~ msgstr "Login" #~ msgid "Looking up %L1" #~ msgstr "Finde Adresse von %L1" #~ msgid "Resume not supported by server" #~ msgstr "Server unterstützt wieder aufnehmen nicht" #, fuzzy #~ msgid "HTTP error" #~ msgstr "HTTP-Fehler" #, fuzzy #~ msgid "No error" #~ msgstr "Fehler" #~ msgid "Timeout" #~ msgstr "Timeout" #~ msgid "" #~ "Jigsaw Download uses the World Wide Web Consortium's \"libwww\" library " #~ "(see ), to which the following license " #~ "applies:\n" #~ "\n" #~ "Copyright © 1995-1998 World Wide Web Consortium, (Massachusetts Institute " #~ "of Technology, Institut National de Recherche en Informatique et en " #~ "Automatique, Keio University). All Rights Reserved. This program is " #~ "distributed under the W3C's Intellectual Property License. This program " #~ "is distributed in the hope that it will be useful, but WITHOUT ANY " #~ "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS " #~ "FOR A PARTICULAR PURPOSE. See W3C License for more details.\n" #~ "\n" #~ "Copyright © 1995 CERN. \"This product includes computer software created " #~ "and made available by CERN. This acknowledgment shall be mentioned in " #~ "full in any product which includes the CERN computer software included " #~ "herein or parts thereof.\"\n" #~ msgstr "" #~ "Jigsaw Download benutzt die »libwww«-Bibliothek des World Wide Web " #~ "Consortium (siehe ), die die folgenden " #~ "Lizenzbestimmungen benutzt:\n" #~ "\n" #~ "Copyright © 1995-1998 World Wide Web Consortium, (Massachusetts Institute " #~ "of Technology, Institut National de Recherche en Informatique et en " #~ "Automatique, Keio University). All Rights Reserved. This program is " #~ "distributed under the W3C's Intellectual Property License. This program " #~ "is distributed in the hope that it will be useful, but WITHOUT ANY " #~ "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS " #~ "FOR A PARTICULAR PURPOSE. See W3C License for more details.\n" #~ "\n" #~ "Copyright © 1995 CERN. »This product includes computer software created " #~ "and made available by CERN. This acknowledgment shall be mentioned in " #~ "full in any product which includes the CERN computer software included " #~ "herein or parts thereof.«\n" #~ msgid "" #~ "Please specify files/directories on the local filesystem to scan. Any " #~ "files needed by jigdo templates that are found this way will not have to " #~ "be downloaded again." #~ msgstr "" #~ "Bitte geben Sie Daten/Verzeichnisse im Dateisystem zum Scannen an. " #~ "Dateien, die vom jigdo-Template benötigt und so gefunden werden, müssen " #~ "nicht mehr heruntergeladen werden." #~ msgid "" #~ "#\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ msgstr "" #~ "#\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ "x\n" #~ msgid "Information about %1:" #~ msgstr "Informationen über %1:" #~ msgid "URL(s) for that server:" #~ msgstr "URL(s) für den Server:" #~ msgid "Download stopped" #~ msgstr "Herunterladen gestoppt" #~ msgid "Unzipping of .jigdo file failed" #~ msgstr "Dekompression der .jigdo-Daten fehlgeschlagen" #~ msgid "Error opening `%1' for output (%2)" #~ msgstr "Fehler beim Öffnen von »%1« zur Ausgabe (%2)" #~ msgid "#Image" #~ msgstr "#Image" #~ msgid "#Scan" #~ msgstr "#Scannen" #~ msgid "Scanning " #~ msgstr "Scannen von " #~ msgid "Abort" #~ msgstr "Abbruch" #~ msgid "Abort all" #~ msgstr "Abbruch alle" #~ msgid "Abort scan" #~ msgstr "Scan abbrechen" #~ msgid "Close" #~ msgstr "Schließen" #~ msgid "Continue/Resume" #~ msgstr "Weiter / Wieder aufnehmen" #~ msgid "Do _not ask me about this again" #~ msgstr "Diese Frage _nicht mehr anzeigen" #~ msgid "Pause" #~ msgstr "Pause" #~ msgid "Pause/continue" #~ msgstr "Pause/weiter" #~ msgid "Pause/continue all" #~ msgstr "Alle Pause/weiter" #~ msgid "Progress " #~ msgstr "Verlauf " #~ msgid "Restart" #~ msgstr "Neustart" #~ msgid "Save to " #~ msgstr "Speichern in " #~ msgid "Select for download" #~ msgstr "Zum Download auswählen" #~ msgid "Select..." #~ msgstr "Auswählen..." #~ msgid "Servers " #~ msgstr "Server " #~ msgid "Speed " #~ msgstr "Geschwindigkeit " #~ msgid "Stop" #~ msgstr "Stop" #~ msgid "Template " #~ msgstr "Template " #~ msgid "" #~ "Usage: %L1 [OPTIONS] [URL]\n" #~ "Options:\n" #~ " -h --help Output help\n" #~ " -v --version Output version info" #~ msgstr "" #~ "Benutzung: %L1 [OPTIONEN] [URL]\n" #~ "Optionen:\n" #~ " -h --help Hilfetext ausgeben\n" #~ " -v --version Versionsnummer ausgeben" #~ msgid "View raw .jigdo" #~ msgstr ".jigdo ansehen" #~ msgid "_Cancel" #~ msgstr "_Abbrechen" #~ msgid "_Delete & restart" #~ msgstr "_Löschen & Neustart" #~ msgid "%s: illegal option -- %c\n" #~ msgstr "%s: Unerlaubte Option -- %c\n" #~ msgid "%s: invalid option -- %c\n" #~ msgstr "%s: Ungültige Option -- %c\n" #~ msgid "%s: option `%c%s' doesn't allow an argument\n" #~ msgstr "%s: Die Option »%c%s« erlaubt kein Argument\n" #~ msgid "%s: option `%s' is ambiguous\n" #~ msgstr "%s: Die Option »%c%s« ist nicht eindeutig\n" #~ msgid "%s: option `%s' requires an argument\n" #~ msgstr "%s: Die Option »%c%s« erfordert ein Argument\n" #~ msgid "%s: option `--%s' doesn't allow an argument\n" #~ msgstr "%s: Die Option »--%s« erlaubt kein Argument\n" #~ msgid "%s: option `-W %s' doesn't allow an argument\n" #~ msgstr "%s: Die Option »-W %s« erlaubt kein Argument\n" #~ msgid "%s: option `-W %s' is ambiguous\n" #~ msgstr "%s: Die Option »-W %s« ist nicht eindeutig\n" #~ msgid "%s: option requires an argument -- %c\n" #~ msgstr "%s: Die Option erfordert ein Argument -- %c\n" #~ msgid "%s: unrecognized option `%c%s'\n" #~ msgstr "%s: Ungültige Option »%c%s«\n" #~ msgid "%s: unrecognized option `--%s'\n" #~ msgstr "%s: Ungültige Option »--%s«\n" #~ msgid "Cannot access `%1': %2" #~ msgstr "Kann nicht auf »%1« zugreifen : %2" #~ msgid "Cannot save to `%1' because that already exists" #~ msgstr "Kann nicht in »%1« speichern - dieses Objekt existiert bereits" #~ msgid "Couldn't create replacement pixmap." #~ msgstr "Ersatz-Pixmap konnte nicht erzeugt werden." #~ msgid "Description" #~ msgstr "Beschreibung" #~ msgid "Error loading pixmap file: %s" #~ msgstr "Fehler beim Laden der Pixmap-Datei: %s" #~ msgid "Exit" #~ msgstr "Ende" #~ msgid "Filename" #~ msgstr "Dateiname" #~ msgid "Open new" #~ msgstr "Neuer Download" #~ msgid "Please enter an URL to download from" #~ msgstr "Bitte geben Sie an, von welchem URL heruntergeladen werden soll" #~ msgid "Progress" #~ msgstr "Verlauf" #~ msgid "Size" #~ msgstr "Größe" #~ msgid "URL " #~ msgstr "URL " #~ msgid "Widget not found: %s" #~ msgstr "Widget nicht gefunden: %s" jigdo-0.7.3/po/jigdo.pot0000644000175000017500000010324210433370613014667 0ustar richardrichard# SOME DESCRIPTIVE TITLE. # This file is put in the public domain. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-05-19 18:21+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: gtk/interface.cc:427 gtk/interface.cc:943 msgid "\n" msgstr "" #: gtk/gui.cc:201 msgid "" "\n" "\tCopyright © 2001-%1 Richard Atterer \n" "\tJigsaw Download homepage: http://atterer.net/jigdo\n" "\n" msgstr "" #: jigdo-file.cc:288 msgid "" "\n" "Copyright (C) 2001-%1 Richard Atterer \n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License, version 2. See\n" "the file COPYING or for details.\n" "\n" msgstr "" #: gtk/gtk-makeimage.cc:25 msgid "" "\n" "Downloading .jigdo data - please wait..." msgstr "" #: jigdo-file.cc:357 msgid "" "\n" "Further options: (can append 'k', 'M', 'G' to any BYTES argument)\n" " --merge=FILE [make-template] Add FILE contents to output jigdo\n" " --no-force Do not delete existent output files [default]\n" " --min-length=BYTES [default %1]\n" " [make-template] Minimum length of files to search\n" " for in image data\n" " --md5-block-size=BYTES [default %2]\n" " Uninteresting internal parameter -\n" " jigdo-file enforces: min-length < md5-block-size\n" " --readbuffer=BYTES [default %3k]\n" " Amount of data to read at a time\n" " --check-files [default]\n" " [make-template,md5sum] Check if files exist and\n" " get or verify checksums, date and size\n" " [make-image] Verify checksum of files written to\n" " image\n" " --no-check-files [make-template,md5sum] when used with --cache,\n" " [make-image] Do not verify checksums of files\n" " --scan-whole-file [scan] Scan whole file instead of only first block\n" " --no-scan-whole-file [scan] Scan only first block [default]\n" " --greedy-matching [make-template] Prefer immediate matches of small\n" " files now over possible (but uncertain) matches of \n" " larger files later [default]\n" " --no-greedy-matching\n" " [make-template] Skip a smaller match and prefer a\n" " pending larger one, with the risk of missing both\n" " --image-section [default]\n" " --no-image-section\n" " --servers-section [default]\n" " --no-servers-section\n" " [make-template] When creating the jigdo file, do\n" " or do not add the sections `[Image]' or `[Servers]'\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n" " --match-exec=CMD [make-template] Execute command when files match\n" " CMD is passed to a shell, with environment set up:\n" " LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE\n" " e.g. 'mkdir -p \"${LABEL:-.}/$MATCHPATH\" && ln -f \"$FILE" "\" \"${LABEL:-.}/$MATCHPATH$LEAF\"'\n" " --no-hex [default]\n" " --hex [md5sum, list-template] Output checksums in\n" " hexadecimal, not Base64\n" " --gzip [default] Use gzip compression, not --bzip2\n" msgstr "" #: jigdo-file.cc:318 msgid "" "\n" "Important options:\n" " -i --image=FILE Output/input filename for image file\n" " -j --jigdo=FILE Input/output filename for jigdo file\n" " -t --template=FILE\n" " Input/output filename for template file\n" " -T --files-from=FILE\n" " Read further filenames from FILE (`-' for stdin)\n" " -r --report=default|noprogress|quiet|grep\n" " Control format of status reports to stderr (or\n" " stdout in case of `grep')\n" " -f --force Silently delete existent output files\n" " --label Label=%1%2path\n" " [make-template] Replace name of input file\n" " `%1%2path%3a%2file%4txt' (note the `%3') with\n" " `Label:a/file%4txt' in output jigdo\n" " --uri Label=http://www.site.com\n" " [make-template] Add mapping from Label to given\n" " URI instead of default `file:' URI\n" " [print-missing] Override mapping in input jigdo\n" " -0 to -9 Set amount of compression in output template\n" " --bzip2 Use bzip2 compression instead of default --gzip\n" " --cache=FILE Store/reload information about any files scanned\n" msgstr "" #: gtk/gui.cc:200 msgid "" "\n" "Jigsaw Download License\n" msgstr "" #: jigdo-file.cc:296 msgid "" "\n" "Usage: %1 COMMAND [OPTIONS] [FILES...]\n" "Commands:\n" " make-template mt Create template and jigdo from image and files\n" " make-image mi Recreate image from template and files (can merge\n" " files in >1 steps, uses `IMG%2tmp' for --image=IMG)\n" " print-missing pm After make-image, print files still missing for\n" " the image to be completely recreated\n" msgstr "" #: jigdo-file.cc:344 msgid "" " --no-cache Do not cache information about scanned files\n" " --cache-expiry=SECONDS[h|d|w|m|y]\n" " Remove cache entries if last access was longer\n" " ago than given amount of time [default 30 days]\n" " -h --help Output short help\n" " -H --help-all Output this help\n" msgstr "" #: jigdo-file.cc:351 msgid "" " -h --help Output this help\n" " -H --help-all Output more detailed help\n" msgstr "" #: jigdo-file.cc:354 msgid " -v --version Output version info" msgstr "" #: jigdo-file.cc:314 msgid "" " list-template ls Print low-level listing of contents of template\n" " data or tmp file\n" msgstr "" #: jigdo-file.cc:306 msgid "" " print-missing-all pma\n" " Print all URIs for each missing file\n" " scan sc Update cache with information about supplied files\n" msgstr "" #: jigdo-file.cc:310 msgid "" " verify ver Check whether image matches checksum from template\n" " md5sum md5 Print MD5 checksums similar to md5sum(1)\n" msgstr "" #: util/progress.cc:86 msgid " bytes)" msgstr "" #: util/progress.cc:65 util/progress.cc:77 util/progress.cc:83 msgid " of " msgstr "" #: gtk/interface.cc:242 gtk/interface.cc:259 gtk/interface.cc:509 #: gtk/interface.cc:519 gtk/interface.cc:538 gtk/interface.cc:548 #: gtk/interface.cc:1061 gtk/interface.cc:1071 gtk/interface.cc:1081 #: gtk/interface.cc:1091 msgid "#" msgstr "" #: gtk/interface.cc:623 msgid "#ShortInfo" msgstr "" #: gtk/interface.cc:585 msgid "#Download" msgstr "" #: gtk/interface.cc:631 msgid "" "#Info\n" "x\n" "x\n" "x" msgstr "" #: gtk/interface.cc:1107 msgid "#Jigdo" msgstr "" #: gtk/interface.cc:351 msgid "#Open" msgstr "" #: gtk/interface.cc:413 msgid "#Reuse" msgstr "" #: util/progress.cc:75 msgid "%, " msgstr "" #: util/progress.cc:95 #, c-format msgid "%02d:%02d:%02d remaining" msgstr "" #: job/jigdo-io.cc:219 job/jigdo-io.cc:228 msgid "%1 (at end of %3)" msgstr "" #: job/jigdo-io.cc:219 job/jigdo-io.cc:228 msgid "%1 (line %2 in %3)" msgstr "" #: gtk/gtk-single-url.cc:330 msgid "%1 days and %2 hours" msgstr "" #: gtk/gtk-single-url.cc:332 msgid "%1 hours and %2 minutes" msgstr "" #: jigdo-file-cmd.cc:347 msgid "%1 list-template: %2" msgstr "" #: jigdo-file-cmd.cc:321 msgid "%1 list-template: --template not specified.\n" msgstr "" #: jigdo-file-cmd.cc:326 msgid "%1 list-template: Sorry, cannot read from standard input.\n" msgstr "" #: jigdo-file-cmd.cc:285 msgid "" "%1 make-image: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" #: jigdo-file-cmd.cc:231 msgid "%1 make-template: Could not read `%2' (%3)" msgstr "" #: jigdo-file-cmd.cc:272 msgid "%1 make-template: Could not write `%2' (%3)" msgstr "" #: jigdo-file-cmd.cc:204 jigdo-file-cmd.cc:444 msgid "" "%1 make-template: Not all of --image, --jigdo, --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" #: gtk/gtk-single-url.cc:334 msgid "%1 minutes" msgstr "" #: jigdo-file-cmd.cc:481 msgid "%1 print-missing: %2" msgstr "" #: jigdo-file-cmd.cc:559 msgid "%1 scan: Please specify a --cache file.\n" msgstr "" #: jigdo-file-cmd.cc:385 msgid "%1 verify: %2" msgstr "" #: jigdo-file-cmd.cc:391 msgid "%1 verify: Invalid template data - corrupted file?" msgstr "" #: jigdo-file-cmd.cc:364 msgid "" "%1 verify: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n" msgstr "" #: jigdo-file-cmd.cc:353 jigdo-file-cmd.cc:397 msgid "%1: %2" msgstr "" #: jigdo-file-cmd.cc:45 jigdo-file-cmd.cc:67 msgid "%1: Could not open `%2' for input: %3" msgstr "" #: jigdo-file-cmd.cc:98 jigdo-file-cmd.cc:113 msgid "%1: Could not open `%2' for output: %3" msgstr "" #: jigdo-file-cmd.cc:156 msgid "%1: Invalid argument to --label: `%2'" msgstr "" #: jigdo-file.cc:583 msgid "" "%1: Invalid argument to --report (allowed: default noprogress quiet grep)" msgstr "" #: jigdo-file-cmd.cc:138 msgid "%1: Invalid argument to --uri: `%2'" msgstr "" #: jigdo-file.cc:689 msgid "" "%1: Invalid command `%2'\n" "(Must be one of:%3)" msgstr "" #: jigdo-file.cc:427 msgid "%1: Invalid size specifier `%2'" msgstr "" #: jigdo-file.cc:453 msgid "%1: Invalid time specifier `%2'" msgstr "" #: jigdo-file.cc:481 msgid "%1: Out of memory - aborted." msgstr "" #: jigdo-file-cmd.cc:84 msgid "%1: Output file `%2' already exists - delete it or use --force" msgstr "" #: jigdo-file.cc:646 msgid "%1: Please specify a command" msgstr "" #: jigdo-file.cc:736 msgid "%1: Try `%1 -h' or `man jigdo-file' for more information" msgstr "" #: util/log.cc:94 msgid "%1: Unit `%2' not found while scanning --debug argument" msgstr "" #: gtk/jigdo.cc:89 msgid "%L1: Please specify `on', `off' or `guess' after --proxy" msgstr "" #: gtk/jigdo.cc:56 msgid "%L1: Try `%L1 -h' or `man jigdo' for more information" msgstr "" #: gtk/gtk-single-url.cc:543 util/progress.cc:101 msgid ", " msgstr "" #: gtk/gtk-single-url.cc:537 msgid ", paused" msgstr "" #: gtk/gtk-single-url.cc:541 msgid ", stalled" msgstr "" #: gtk/interface.cc:705 msgid "..." msgstr "" #: gtk/gtk-single-url.cc:545 msgid "/s" msgstr "" #: util/progress.cc:103 msgid "/sec" msgstr "" #: net/download.cc:375 msgid "305 Use Proxy" msgstr "" #: net/download.cc:376 msgid "400 Bad Request" msgstr "" #: net/download.cc:377 msgid "401 Unauthorized" msgstr "" #: net/download.cc:378 msgid "403 Forbidden" msgstr "" #: net/download.cc:379 msgid "404 Not Found" msgstr "" #: net/download.cc:380 msgid "407 Proxy Authentication Required" msgstr "" #: net/download.cc:381 msgid "408 Request Timeout" msgstr "" #: net/download.cc:382 msgid "500 Internal Server Error" msgstr "" #: net/download.cc:383 msgid "501 Not Implemented" msgstr "" #: net/download.cc:384 msgid "503 Service Unavailable" msgstr "" #: gtk/gtk-single-url.cc:420 msgid "%1" msgstr "" #: gtk/gtk-makeimage.cc:164 gtk/gtk-single-url.cc:453 #, c-format msgid "%E1" msgstr "" #: gtk/gtk-makeimage.cc:227 #, c-format msgid "%EF1 (%EF2)" msgstr "" #: gtk/interface.cc:764 msgid "?: Reuse files" msgstr "" #: gtk/gui.cc:370 msgid "" "Close this download and all its children, and remove them from the " "list below" msgstr "" #: gtk/gui.cc:318 msgid "Close this download and remove it from the list below" msgstr "" #: gtk/gui.cc:349 msgid "" "Continue all child downloads (after Pause) or Resume them " "(after Stop or error)" msgstr "" #: gtk/gui.cc:299 msgid "Continue download (after Pause) or Resume it (after Stop)" msgstr "" #: gtk/interface.cc:613 msgid "" "Note: Even though some data is already being downloaded, you\n" "need to complete step 2 before the actual file download can start!" msgstr "" #: gtk/gtk-single-url.cc:169 gtk/gtk-single-url.cc:197 msgid "Open of output file failed" msgstr "" #: gtk/gui.cc:354 msgid "Pause all child downloads, but leave connections to servers open" msgstr "" #: gtk/gui.cc:304 msgid "Pause download, but leave connection to server open" msgstr "" #: gtk/interface.cc:576 gtk/interface.cc:1034 msgid "Progress " msgstr "" #: gtk/gui.cc:313 msgid "Restart download - the data downloaded so far is discarded" msgstr "" #: gtk/gui.cc:364 msgid "" "Restart download and processing of .jigdo - the .jigdo data downloaded so far (and only it) is discarded" msgstr "" #: gtk/interface.cc:681 msgid "Reuse local files for generation of %1:" msgstr "" #: gtk/interface.cc:567 gtk/interface.cc:1025 msgid "Saving to " msgstr "" #: gtk/interface.cc:779 msgid "Select servers to download data from:" msgstr "" #: gtk/interface.cc:529 gtk/interface.cc:1043 msgid "Status " msgstr "" #: gtk/gui.cc:359 msgid "Stop download by closing connections of all children" msgstr "" #: gtk/gui.cc:309 msgid "Stop download by closing the connection" msgstr "" #: gtk/interface.cc:843 msgid "Type of server: " msgstr "" #: gtk/interface.cc:558 gtk/interface.cc:1052 msgid "URL " msgstr "" #: gtk/interface.cc:851 msgid "" "URL(s) for that server: (space-separated list, preferred URLs first)" msgstr "" #: gtk/gtk-single-url.cc:493 msgid "Write to output file failed" msgstr "" #: gtk/interface.cc:667 msgid "_1: Information" msgstr "" #: gtk/interface.cc:928 msgid "_2: Select servers" msgstr "" #: gtk/interface.cc:1101 msgid "_3: Download" msgstr "" #: gtk/interface.cc:923 msgid "_OK, start download!" msgstr "" #: gtk/interface.cc:291 msgid "_Start" msgstr "" #: gtk/gui.cc:108 msgid "" "This is Free Software, distributable under the terms " "of the GNU GPL v2." msgstr "" #: gtk/gui.cc:99 msgid "" "Jigsaw " "Download %F1\n" "Copyright 2001-%2 Richard Atterer\n" "http://atterer.net/jigdo" msgstr "" #: job/jigdo-io.cc:534 msgid "A new section must be started after [Include]" msgstr "" #: gtk/interface.cc:820 msgid "Ad_vanced: " msgstr "" #: gtk/interface.cc:1518 msgid "Apply" msgstr "" #: util/progress.cc:38 msgid "B" msgstr "" #: gtk/interface.cc:334 gtk/interface.cc:340 gtk/interface.cc:385 msgid "Browse..." msgstr "" #: util/log.cc:101 msgid "" "By default, debug output is disabled except for `assert'. Argument\n" "to --debug is a comma-separated list of unit names, or `all' for\n" "all units. Just `--debug' is equivalent to`--debug=all'. Output for\n" "the listed units is enabled, precede a name with `~' to disable it.\n" "Registered units:" msgstr "" #: job/makeimagedl.cc:229 job/makeimagedl.cc:241 msgid "Cache entry %1" msgstr "" #: gtk/interface.cc:1523 msgid "Cancel" msgstr "" #: mkimage.cc:868 msgid "Cannot create image because of missing files" msgstr "" #: gtk/gtk-single-url.cc:98 #, c-format msgid "Cannot save to `%LE1' because that already exists as a device/link" msgstr "" #: gtk/gtk-single-url.cc:84 #, c-format msgid "Cannot save to `%LE1' because that already exists as a directory" msgstr "" #: gtk/interface.cc:198 msgid "Change preferences" msgstr "" #: util/gunzip.cc:240 util/gunzip.cc:251 util/gunzip.cc:262 util/gunzip.cc:273 msgid "Checksum is wrong" msgstr "" #: gtk/interface.cc:408 msgid "Clear cache" msgstr "" #: gtk/joblist.cc:116 msgid "Click on lines below to display corresponding info above" msgstr "" #: mktemplate.cc:435 msgid "Command supplied with --match-exec failed" msgstr "" #: mkimage.cc:664 msgid "" "Copied input files to temporary file `%1' - repeat command and supply more " "files to continue" msgstr "" #: zstream.cc:202 zstream.cc:232 msgid "Corrupted input data" msgstr "" #: job/makeimagedl.cc:127 msgid "Could not create temporary directory: %L1" msgstr "" #: mkimage.cc:673 msgid "Could not move finished image from `%1' to `%2' (%3)" msgstr "" #: jigdoconfig.cc:61 msgid "Could not open `%1' for input: %2" msgstr "" #: mkimage.cc:911 msgid "Could not open `%1' for output: %2" msgstr "" #: scan.cc:284 msgid "Could not open `%L1' for input - excluded" msgstr "" #: job/cached-url.cc:134 msgid "Could not open `%L1' for input: %L2" msgstr "" #: job/makeimagedl.cc:267 msgid "Could not open `%L1' for output: %L2" msgstr "" #: gtk/gtk-single-url.cc:120 #, c-format msgid "Could not open `%LE1' for output: %LE2" msgstr "" #: scan.cc:166 msgid "Could not open cache file: %L1" msgstr "" #: gtk/gtk-single-url.cc:170 gtk/gtk-single-url.cc:198 #, c-format msgid "Could not open output file: %LE1" msgstr "" #: job/cached-url.cc:162 msgid "Could not read from `%L1': %L2" msgstr "" #: job/makeimagedl.cc:406 msgid "Could not rename `%L1' to `%L2': %L3" msgstr "" #: mktemplate.cc:427 msgid "Could not set up environment for --match-exec command" msgstr "" #: mkimage.cc:645 msgid "Could not truncate `%1' (%2)" msgstr "" #: mktemplate.cc:934 zstream.cc:85 zstream.cc:96 msgid "Could not write template data" msgstr "" #: gtk/gtk-single-url.cc:494 msgid "Could not write to output file: %L1" msgstr "" #: gtk/support.cc:92 gtk/support.cc:116 #, c-format msgid "Couldn't find pixmap file: %s" msgstr "" #: gtk/gtk-single-url.cc:658 msgid "Data stream active - really abort it?" msgstr "" #: gtk/interface.cc:859 msgid "Debian" msgstr "" #: util/gunzip.cc:28 msgid "Decompression error" msgstr "" #: gtk/interface.cc:1634 msgid "Default servers" msgstr "" #: mkimage.cc:771 msgid "Delete/rename the file or use --force" msgstr "" #: gtk/interface.cc:1553 msgid "Display tooltips" msgstr "" #: gtk/gtk-single-url.cc:655 msgid "Download in progress - really abort it?" msgstr "" #: gtk/gtk-single-url.cc:404 msgid "Download is complete - fetched %1 (%2 bytes)" msgstr "" #: gtk/gtk-single-url.cc:236 msgid "Download is paused" msgstr "" #: job/jigdo-io.cc:113 msgid "Download of .jigdo file failed" msgstr "" #: gtk/gtk-single-url.cc:741 msgid "Download was restarted - waiting..." msgstr "" #: gtk/gtk-single-url.cc:265 msgid "Download was stopped manually" msgstr "" #: jigdo-file-cmd.cc:413 msgid "ERROR: Checksums do not match, image might be corrupted!" msgstr "" #: gtk/interface.cc:1647 gtk/interface.cc:1654 msgid "" "Enter a list of domains (separated with spaces) for which you do not want to " "use the proxies you set up above. Example: \"mycompany.com local.network.lan" "\"" msgstr "" #: gtk/interface.cc:346 msgid "" "Enter location of .jigdo file, and destination directory to save " "files to." msgstr "" #: net/download.cc:355 msgid "Error" msgstr "" #: mkimage.cc:621 msgid "Error - could not access temporary file" msgstr "" #: mkimage.cc:549 msgid "Error - template data's DESC section invalid" msgstr "" #: scan.cc:265 scan.hh:433 msgid "Error accessing cache: %1" msgstr "" #: gtk/gtk-single-url.cc:83 gtk/gtk-single-url.cc:97 gtk/gtk-single-url.cc:119 #: gtk/jobline.cc:70 msgid "Error accessing destination" msgstr "" #: gtk/jobline.cc:84 msgid "Error accessing directory to save to" msgstr "" #: gtk/gtk-single-url.cc:601 msgid "Error accessing output file" msgstr "" #: scan.cc:204 msgid "" "Error during cache expiry: %1. The cache file may be corrupt, consider " "deleting it." msgstr "" #: mktemplate.cc:924 msgid "Error during compression: %1" msgstr "" #: gtk/gui.cc:247 #, c-format msgid "Error loading `%LE1': %LE2" msgstr "" #: scan.cc:281 msgid "Error opening file `-' (using standard input not allowed here)" msgstr "" #: job/jigdo-io.cc:239 msgid "Error processing .jigdo file contents" msgstr "" #: zstream.cc:286 msgid "Error reading compressed data - %1" msgstr "" #: recursedir.cc:94 msgid "Error reading filenames from `%1' (%2)" msgstr "" #: recursedir.cc:91 msgid "Error reading filenames from standard input (%1)" msgstr "" #: mkimage.cc:395 mktemplate.cc:323 msgid "Error reading from `%1' (%2)" msgstr "" #: recursedir.cc:52 msgid "Error reading from directory `%1' (%2)" msgstr "" #: mkimage.cc:224 msgid "Error reading template data (%1)" msgstr "" #: jigdo-file.cc:172 msgid "Error scanning image - abort" msgstr "" #: scan.cc:409 msgid "Error while reading `%1' - file will be ignored (%2)" msgstr "" #: mkimage.cc:558 msgid "Error while writing to `%1' (%2)" msgstr "" #: mkimage.cc:407 msgid "Error: `%1' does not match checksum in template data" msgstr "" #: gtk/interface.cc:205 msgid "Exit from Jigsaw Download" msgstr "" #: gtk/interface.cc:1605 msgid "FTP proxy" msgstr "" #: job/makeimagedl.cc:455 msgid "Failed – see error of child download" msgstr "" #: gtk/gtk-makeimage.cc:166 gtk/gtk-single-url.cc:457 msgid "Failed:" msgstr "" #: gtk/jobline.cc:49 msgid "Field for source URL/filename is empty" msgstr "" #: gtk/gtk-single-url.cc:408 msgid "Finished - fetched %1" msgstr "" #: jigdo-file.cc:182 msgid "Finished - image size is %1 bytes." msgstr "" #: mkimage.cc:839 msgid "Found %1 of the %2 files required by the template" msgstr "" #: gtk/interface.cc:1596 msgid "HTTP proxy" msgstr "" #: gtk/interface.cc:325 msgid "" "If the string you enter does not point to a .jigdo file, jigdo behaves just " "like a normal \"download manager\" and saves the file in the destination " "directory, nothing else." msgstr "" #: gtk/interface.cc:332 msgid "" "If you entered a .jigdo URL above, this must be a directory name. For single-" "file downloads, it can also be a filename." msgstr "" #: mkimage.cc:963 msgid "Ignoring existing temporary file `%1' - %2" msgstr "" #: job/jigdodownload.cc:136 job/jigdo-io.cc:182 msgid "Input .jigdo data contains invalid control characters" msgstr "" #: job/jigdodownload.cc:124 job/jigdodownload.cc:146 job/jigdo-io.cc:97 #: job/jigdo-io.cc:171 job/jigdo-io.cc:192 msgid "Input .jigdo data is not valid UTF-8" msgstr "" #: job/jigdo-io.cc:610 job/jigdo-io.cc:617 msgid "Invalid MD5Sum in Parts section" msgstr "" #: job/jigdo-io.cc:581 job/jigdo-io.cc:588 msgid "Invalid Template-MD5Sum argument" msgstr "" #: job/url-mapping.cc:239 msgid "Invalid argument to --try-first" msgstr "" #: job/url-mapping.cc:248 msgid "Invalid argument to --try-last" msgstr "" #: job/makeimagedl.cc:291 job/makeimagedl.cc:329 job/makeimagedl.cc:352 msgid "Invalid cache entry: `%L1' is not a file" msgstr "" #: job/jigdo-io.cc:445 util/configfile.cc:119 msgid "Invalid characters after closing `]'" msgstr "" #: job/jigdo-io.cc:566 msgid "Invalid image name" msgstr "" #: mkimage.cc:85 mkimage.cc:108 mkimage.cc:126 mkimage.cc:218 mkimage.cc:230 #: mkimage.cc:234 msgid "Invalid template data - corrupted file?" msgstr "" #: gtk/interface.cc:1241 msgid "Jigdo logo" msgstr "" #: gtk/interface.cc:157 msgid "Jigsaw Download" msgstr "" #: job/makeimagedl.cc:144 msgid "" "Jigsaw Download - half-finished download\n" "\n" "This directory contains the data for a half-finished download of a\n" ".jigdo file. Do not change or delete any of the files in this\n" "directory! (Of course you can delete the entire directory if you do\n" "not want to continue with the download.)\n" "\n" "If the jigdo application was stopped and you want it to resume this\n" "download, simply enter again the same values you used the first time.\n" "\n" "In the \"URL\" field, enter:\n" " %1\n" "\n" "In the \"Save to\" field, enter the parent directory of the directory\n" "containing this file. Unless you have moved it around, the correct\n" "value is:\n" " %2\n" "\n" "\n" "[Download]\n" "URL=%1\n" "Destination=%2\n" msgstr "" #: gtk/gui.cc:208 msgid "" "Jigsaw Download 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.\n" "\n" "In addition, as a special exception, the author gives permission to link the " "jigdo code with the OpenSSL project's \"OpenSSL\" library (or with modified " "versions of it that use the same license as the \"OpenSSL\" library), and to " "distribute the linked executables.\n" msgstr "" #: util/configfile.cc:80 msgid "Label name contains invalid character `%1'" msgstr "" #: util/configfile.cc:75 msgid "Label name is not followed by `='" msgstr "" #: gtk/interface.cc:1702 msgid "License for Jigsaw Download" msgstr "" #: jigdoconfig.cc:155 msgid "" "Line contains more than one URI, ignoring part after `%1' (maybe you need to " "use \"\" quotes?)" msgstr "" #: job/jigdo-io.cc:479 msgid "Loop of [Include] directives" msgstr "" #: util/progress.cc:50 util/progress.cc:53 msgid "MB" msgstr "" #: jigdo-file.cc:176 msgid "Match of `%1' at offset %2" msgstr "" #: gtk/interface.cc:1542 msgid "Maximum number of simultaneous downloads" msgstr "" #: job/jigdo-io.cc:539 job/jigdo-io.cc:560 job/jigdo-io.cc:568 #: job/jigdo-io.cc:572 job/jigdo-io.cc:603 job/jigdo-io.cc:624 msgid "Missing argument" msgstr "" #: job/jigdo-io.cc:403 msgid "No `=' after first word" msgstr "" #: job/jigdo-io.cc:377 msgid "No `[Image]' section found in .jigdo data" msgstr "" #: job/jigdo-io.cc:415 job/jigdo-io.cc:421 util/configfile.cc:95 #: util/configfile.cc:103 msgid "No closing `]' for section name" msgstr "" #: gtk/interface.cc:866 msgid "Non-US" msgstr "" #: gtk/interface.cc:1513 msgid "OK" msgstr "" #: jigdo-file-cmd.cc:408 msgid "OK: Checksums match, image is good!" msgstr "" #: gtk/interface.cc:180 msgid "Open new .jigdo file or URL" msgstr "" #: gtk/gtk-single-url.cc:337 msgid "Output file exists - overwrite it or resume download?" msgstr "" #: gtk/gtk-single-url.cc:351 msgid "Please answer the pop-up: Overwrite, resume or cancel?" msgstr "" #: gtk/jobline.cc:50 msgid "" "Please enter an \"http\" or \"ftp\" URL to download, or the name/URL of a " ".jigdo file to process." msgstr "" #: gtk/gui.cc:221 msgid "" "Please note: The copyright notice below only applies to the text of the GNU " "General Public License; the copyright of the program is as specified above. " "Also note that jigdo is licensed under GPL version _2_ and no other " "version.\n" "\n" "\n" msgstr "" #: gtk/interface.cc:371 msgid "" "Please specify files/directories on the local filesystem to scan. Any files " "needed by jigdo templates that are found this way will not have to be " "downloaded again.\n" "Note that any directory to be scanned must contain single files which would " "otherwise be downloaded - in the case of CD images, you must supply the " "directory where the CD is mounted, NOT the name of an .iso file." msgstr "" #: mkimage.cc:485 msgid "Premature end of template data" msgstr "" #: job/url-mapping.cc:191 msgid "Recursive label definition" msgstr "" #: gtk/interface.cc:1579 msgid "Remove finished download jobs from list after (seconds)" msgstr "" #: gtk/gtk-single-url.cc:742 msgid "Restarted - waiting" msgstr "" #: gtk/gtk-single-url.cc:712 msgid "Restarting will discard already downloaded data" msgstr "" #: job/single-url.cc:99 msgid "Resume failed" msgstr "" #: gtk/gtk-single-url.cc:595 #, c-format msgid "Resuming `%LE1' is not possible: %LE2" msgstr "" #: gtk/gtk-single-url.cc:598 #, c-format msgid "Resuming `%LE1' is not possible: It is not a regular file" msgstr "" #: gtk/gtk-single-url.cc:152 gtk/gtk-single-url.cc:206 msgid "Resuming download - overlap is %1kB" msgstr "" #: job/single-url.cc:221 msgid "Resuming... %1kB" msgstr "" #: job/makeimagedl.cc:531 msgid "Retrieving .jigdo" msgstr "" #: job/makeimagedl.cc:570 msgid "Retrieving .template" msgstr "" #: gtk/interface.cc:182 msgid "Reuse files" msgstr "" #: gtk/interface.cc:1567 msgid "Save .jigdo files in destination directory" msgstr "" #: gtk/interface.cc:1571 msgid "Save .template files in destination directory" msgstr "" #: gtk/interface.cc:311 msgid "Save to" msgstr "" #: gtk/interface.cc:187 msgid "" "Scan local filesystems for data needed by jigsaw downloads - useful when " "\"upgrading\" e.g. a CD image to a newer version" msgstr "" #: job/jigdo-io.cc:442 msgid "Section name invalid" msgstr "" #: util/configfile.cc:112 msgid "Section name invalid at character `%1'" msgstr "" #: gtk/interface.cc:1433 msgid "Select File" msgstr "" #: gtk/interface.cc:823 msgid "" "Select or enter server URLs for the different types of servers that the ." "jigdo file references. You can enter several URLs for each type of server - " "in this case, jigdo will try to find the fastest." msgstr "" #: gtk/interface.cc:810 msgid "Select the country you live in →" msgstr "" #: gtk/interface.cc:797 msgid "" "Select the country you live in. jigdo will then try out all available " "servers on your continent. Sooner or later, it should end up using a server " "that is fast for you." msgstr "" #: job/single-url.cc:145 msgid "Server sent more data than expected" msgstr "" #: gtk/interface.cc:1533 msgid "Settings" msgstr "" #: gtk/interface.cc:1496 msgid "Settings - Jigsaw Download" msgstr "" #: gtk/gtk-single-url.cc:612 msgid "Size of output file changed" msgstr "" #: recursedir.cc:46 msgid "Skipping object `%1' (%2)" msgstr "" #: gtk/gtk-single-url.cc:713 msgid "" "Some data has already been downloaded. Are you sure you want to delete this " "data and restart the download?" msgstr "" #: mkimage.cc:926 msgid "" "Sorry, at the moment the Windows port of jigdo cannot create files bigger " "than 2 GB. Use the Linux version." msgstr "" #: gtk/interface.cc:403 msgid "Start scan" msgstr "" #: gtk/joblist.cc:96 msgid "Status" msgstr "" #: mkimage.cc:884 mkimage.cc:941 msgid "Successfully created `%1'" msgstr "" #: gtk/interface.cc:1558 msgid "Switch to download screen once download of a .jigdo file is complete" msgstr "" #: gtk/jobline.cc:85 #, c-format msgid "The destination `%LE1' cannot be accessed: %LE2" msgstr "" #: gtk/jobline.cc:71 #, c-format msgid "The destination `%LE1' is present, but cannot be accessed: %LE2" msgstr "" #: gtk/gtk-single-url.cc:338 msgid "" "The output file `%LE1' already exists with a size of %2 bytes, it is %3 " "old.\n" "Overwrite deletes the data in this file, whereas Resume can be " "used to continue an earlier, interrupted download of the same file." msgstr "" #: net/download.cc:362 msgid "Transfer interrupted" msgstr "" #: gtk/gtk-single-url.cc:449 msgid "Try %1 of %2 after %E3" msgstr "" #: gtk/interface.cc:687 msgid "" "UNIMPLEMENTED\n" "just click Next..." msgstr "" #: gtk/interface.cc:302 msgid "URL" msgstr "" #: job/jigdo-io.cc:548 jigdoconfig.cc:297 msgid "" "Upgrade required - this .jigdo file needs a newer version of the jigdo " "program" msgstr "" #: gtk/jigdo.cc:107 msgid "" "Usage: %L1 [OPTIONS] [URL]\n" "Options:\n" " -h --help Output help\n" " -Y --proxy=on/off/guess [guess]\n" " Turn proxy on (i.e. use env vars http_proxy,\n" " ftp_proxy, all_proxy) or off, or guess (from\n" " Mozilla/KDE/wget/lynx settings)\n" " -v --version Output version info\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n" msgstr "" #: job/jigdo-io.cc:559 job/jigdo-io.cc:571 job/jigdo-io.cc:592 #: job/jigdo-io.cc:596 msgid "Value redefined" msgstr "" #: gtk/gtk-makeimage.cc:65 gtk/gtk-single-url.cc:69 msgid "Waiting" msgstr "" #: gtk/gtk-single-url.cc:68 gtk/gtk-single-url.cc:129 msgid "Waiting..." msgstr "" #: jigdo-file-cmd.cc:211 msgid "" "Warning - no files specified. The template will contain the complete image " "contents!" msgstr "" #: jigdo-file-cmd.cc:339 jigdo-file-cmd.cc:380 msgid "Warning: This does not seem to be a template file" msgstr "" #: gtk/gtk-single-url.cc:613 msgid "" "When the download was stopped, the length of the output file `%LE1' was %2 " "bytes, but now it is %3 bytes. Do you really want to resume the download " "(from byte %3)?" msgstr "" #: mkimage.cc:860 msgid "" "Will not create image or temporary file - try again with different input " "files" msgstr "" #: mkimage.cc:766 msgid "Will not reuse existing temporary file `%1' - %2" msgstr "" #: gtk/gtk-single-url.cc:659 msgid "_Abort data stream" msgstr "" #: gtk/gtk-single-url.cc:656 msgid "_Abort download" msgstr "" #: gtk/interface.cc:785 msgid "_Automatic selection: Use \"netselect\" to find the best servers" msgstr "" #: gtk/jobline.cc:127 msgid "_Awesome!" msgstr "" #: gtk/jobline.cc:126 msgid "_Cool!" msgstr "" #: gtk/jobline.cc:128 msgid "_Fantastic!" msgstr "" #: gtk/interface.cc:662 gtk/interface.cc:760 msgid "_Next" msgstr "" #: gtk/gtk-single-url.cc:345 msgid "_Overwrite" msgstr "" #: gtk/gtk-single-url.cc:716 msgid "_Restart" msgstr "" #: gtk/gtk-single-url.cc:346 gtk/gtk-single-url.cc:617 msgid "_Resume" msgstr "" #: gtk/interface.cc:794 msgid "_Simple selection, by country name: " msgstr "" #: gtk/interface.cc:728 msgid "_Start scan" msgstr "" #: mkimage.cc:688 msgid "`%1' is not a template file" msgstr "" #: job/jigdo-io.cc:462 msgid "`%1=...' line missing in [Image] section" msgstr "" #: mkimage.cc:394 msgid "file is too short" msgstr "" #: mkimage.cc:714 msgid "it corresponds to a different image/template." msgstr "" #: mkimage.cc:711 msgid "it was not created by jigdo-file, or is corrupted." msgstr "" #: util/progress.cc:42 util/progress.cc:46 msgid "kB" msgstr "" #: gtk/interface.cc:1614 msgid "no proxy" msgstr "" #: jigdo-file.cc:112 msgid "scanning" msgstr "" #: jigdo-file.cc:137 msgid "scanning image" msgstr "" #: mkimage.cc:716 mkimage.cc:720 msgid "since its creation, the template was regenerated." msgstr "" #: jigdo-file.cc:152 msgid "verifying image" msgstr "" #: jigdo-file.cc:168 msgid "writing image" msgstr "" jigdo-0.7.3/scripts/.cvsignore0000644000175000017500000000000407701400007016103 0ustar richardrichardcvs jigdo-0.7.3/scripts/convert-cvsmirrors.awk0000644000175000017500000000376707701377777020554 0ustar richardrichard#! /usr/bin/gawk -f # __ _ # |_) /| Copyright (C) 2001 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2. See # the file COPYING for details. # Convert the list of Debian mirrors from Debian CVS into entries for # the [Servers] section of a .jigdo file. function jigdo(str, comment) { if (str in recorded) return; recorded[str]; result = str; #result = substr(str, 7)" "substr(str, 1, 6); if (comment) { result = result substr(" ", length(str)) " # " comment; } print result; } function entry() { for (dummy in x) { # Array empty => ignore comment = x["Country"]; if ("Location" in x) { # Remove repeated country name from Location location = x["Location"]; loc = index(location, substr(comment, 4)); if (loc) { comment = comment " (" substr(location, 1, loc - 1) \ substr(location, loc + length(comment) - 3) ")"; } else { comment = comment " (" location ")"; } sub(/[ ,]+\)$| +\)/, ")", comment); sub(/[ ,]+\( *\)$/, "", comment); sub(/ +/, " ", comment); } if ("Archive-http" in x) jigdo("Debian=http://" x["Site"] x["Archive-http"], comment); else if ("Archive-ftp" in x) jigdo("Debian=ftp://" x["Site"] x["Archive-ftp"], comment); if ("NonUS-http" in x) jigdo("Non-US=http://" x["Site"] x["NonUS-http"], comment); else if ("NonUS-ftp" in x) jigdo("Non-US=ftp://" x["Site"] x["NonUS-ftp"], comment); split("", x); # Clear x[] return; } } /^$/ { entry(); } ($1 ~ /^[A-Za-z0-9_-]+:$/) { field = substr($1, 1, length($1) - 1); line = $0; sub(/^[^ ]+ /, "", line); x[field] = line; } # Entry line continued on new line /^[ ]+[^ ]/ { line = $0; sub(/^[ ]+/, " ", line); x[field] = x[field] line; } END { entry(); } jigdo-0.7.3/scripts/debian-mirrors.jigdo0000644000175000017500000013061510261525207020057 0ustar richardrichardDebian=http://ftp.us.debian.org/debian/ # US United States Debian=http://ftp.debian.org/debian/ # US United States Non-US=http://non-us.debian.org/debian-non-US/ # NL Netherlands (Amsterdam) Debian=http://debian.hands.com/debian/ # GB Great Britain Non-US=http://debian.hands.com/debian-non-US/ # GB Great Britain Debian=http://debian.crosslink.net/debian/ # US United States (Virginia) Debian=http://mirror.aarnet.edu.au/debian/ # AU Australia (Canberra) Non-US=http://mirror.aarnet.edu.au/debian-non-US/ # AU Australia (Canberra) Debian=http://ftp.wa.au.debian.org/debian/ # AU Australia (Perth, Western) Non-US=http://ftp.wa.au.debian.org/debian-non-US/ # AU Australia (Perth, Western) Debian=http://ftp.monash.edu.au/pub/linux/debian/ # AU Australia (Melbourne) Non-US=http://ftp.monash.edu.au/pub/linux/debian-non-US/ # AU Australia (Melbourne) Non-US=http://ftp.progsoc.uts.edu.au/pub/Linux/debian-non-US/ # AU Australia (Sydney) Debian=ftp://ftp.nluug.nl/pub/os/Linux/distr/debian/ # NL Netherlands Debian=ftp://ftp.demon.co.uk/pub/mirrors/linux/debian/ # GB Great Britain Debian=ftp://ftp.uwa.edu.au/mirrors/linux/debian/ # AU Australia (Perth, Western) Non-US=ftp://ftp.uwa.edu.au/mirrors/linux/debian-non-US/ # AU Australia (Perth, Western) Debian=http://mirror.eftel.com/debian/ # AU Australia (Perth, Western) Non-US=http://mirror.eftel.com/debian-non-US/ # AU Australia (Perth, Western) Debian=ftp://sunsite.ust.hk/pub/debian/ # HK Hong Kong Debian=http://ftp.tiscali.be/debian/ # BE Belgium Non-US=http://ftp.tiscali.be/debian-non-US/ # BE Belgium Debian=http://ftp.kulnet.kuleuven.ac.be/debian/ # BE Belgium (Leuven) Non-US=http://ftp.kulnet.kuleuven.ac.be/debian-non-US/ # BE Belgium (Leuven) Debian=http://mirrors.sunsite.dk/debian/ # DK Denmark (Aalborg) Non-US=http://mirrors.sunsite.dk/debian-non-US/ # DK Denmark (Aalborg) Debian=http://debian.efis.ucr.ac.cr/debian/ # CR Costa Rica (San Jose) Non-US=http://debian.efis.ucr.ac.cr/debian-non-US/ # CR Costa Rica (San Jose) Debian=http://ftp.it.debian.org/debian/ # IT Italy (Milan) Non-US=http://ftp.it.debian.org/debian-non-US/ # IT Italy (Milan) Debian=http://ftp2.it.debian.org/debian/ # IT Italy (Bologna) Non-US=http://ftp2.it.debian.org/debian-non-US/ # IT Italy (Bologna) Debian=ftp://ftp-linux.cc.gatech.edu/debian/ # US United States (Atlanta, Georgia) Debian=http://ftp.egr.msu.edu/debian/ # US United States (Lansing, Michigan) Debian=http://distro.ibiblio.org/pub/Linux/distributions/debian/ # US United States (Chapel Hill, North Carolina) Debian=ftp://ftp.mcc.ac.uk/pub/linux/distributions/Debian/ # GB Great Britain Debian=http://ftp.cz.debian.org/debian/ # CZ Czech Republic Non-US=http://ftp.cz.debian.org/debian-non-US/ # CZ Czech Republic Debian=http://ftp.tu-clausthal.de/pub/linux/debian/ # DE Germany (Clausthal-Zellerfeld, Niedersachsen) Non-US=http://ftp.tu-clausthal.de/pub/linux/debian-non-US/ # DE Germany (Clausthal-Zellerfeld, Niedersachsen) Debian=http://ftp.sk.debian.org/debian/ # SK Slovakia (Kosice) Non-US=http://ftp.sk.debian.org/debian-non-US/ # SK Slovakia (Kosice) Debian=http://toxo.com.uvigo.es/debian/ # ES Spain (Vigo, Pontevedra) Non-US=http://toxo.com.uvigo.es/debian-non-US/ # ES Spain (Vigo, Pontevedra) Debian=http://ftp.at.debian.org/debian/ # AT Austria (Vienna) Non-US=http://ftp.at.debian.org/debian-non-US/ # AT Austria (Vienna) Debian=http://ftp.de.debian.org/debian/ # DE Germany (Dresden) Non-US=http://ftp.de.debian.org/debian-non-US/ # DE Germany (Dresden) Debian=http://mirror.switch.ch/ftp/mirror/debian/ # CH Switzerland (Zurich) Non-US=http://mirror.switch.ch/ftp/mirror/debian-non-US/ # CH Switzerland (Zurich) Debian=http://ftp2.jp.debian.org/debian/ # JP Japan (Kashiwa NOC, Chiba) Non-US=http://ftp2.jp.debian.org/debian-non-US/ # JP Japan (Kashiwa NOC, Chiba) Debian=http://ftp.bg.debian.org/debian/ # BG Bulgaria (Sofia) Non-US=http://ftp.bg.debian.org/debian-non-US/ # BG Bulgaria (Sofia) Debian=http://ftp-mirror.internap.com/pub/debian/ # US United States (Seattle) Debian=http://ftp.cerias.purdue.edu/pub/os/debian/ # US United States (West Lafayette, Indiana) Debian=http://ftp.ru.debian.org/debian/ # RU Russia (Chernogolovka, Moscow) Non-US=http://ftp.ru.debian.org/debian-non-US/ # RU Russia (Chernogolovka, Moscow) Debian=http://ftp.bononia.it/debian/ # IT Italy (Rome) Non-US=http://ftp.bononia.it/debian-non-US/ # IT Italy (Rome) Debian=http://ftp.students.cs.unibo.it/debian/ # IT Italy (Bologna) Non-US=http://ftp.students.cs.unibo.it/debian-non-US/ # IT Italy (Bologna) Debian=http://ftp.sunet.se/pub/os/Linux/distributions/debian/ # SE Sweden (Uppsala) Non-US=http://ftp.sunet.se/pub/os/Linux/distributions/debian-non-US/ # SE Sweden (Uppsala) Debian=ftp://ftp.dkuug.dk/pub/debian/ # DK Denmark (Copenhagen) Debian=http://freedom.dicea.unifi.it/ftp/pub/linux/debian/ # IT Italy (Firenze) Non-US=http://freedom.dicea.unifi.it/ftp/pub/linux/debian-non-US/ # IT Italy (Firenze) Debian=http://ftp.pl.debian.org/debian/ # PL Poland (Gdańsk) Non-US=http://ftp.pl.debian.org/debian-non-US/ # PL Poland (Gdańsk) Debian=http://ftp2.de.debian.org/debian/ # DE Germany (Guetersloh) Non-US=http://ftp2.de.debian.org/debian-non-US/ # DE Germany (Guetersloh) Debian=http://ftp.iut-bm.univ-fcomte.fr/debian/ # FR France (Belfort) Non-US=http://ftp.iut-bm.univ-fcomte.fr/debian-non-US/ # FR France (Belfort) Debian=http://ring.asahi-net.or.jp/archives/linux/debian/debian/ # JP Japan (Tokyo) Non-US=http://ring.asahi-net.or.jp/archives/linux/debian/debian-non-US/ # JP Japan (Tokyo) Debian=http://ftp.eudil.fr/debian/ # FR France (Lille) Non-US=http://ftp.eudil.fr/debian-non-US/ # FR France (Lille) Debian=ftp://ftp.cs.unm.edu/mirrors/debian/ # US United States (Santa Fe, New Mexico) Debian=http://ftp.uevora.pt/debian/ # PT Portugal (Évora) Non-US=http://ftp.uevora.pt/debian-non-US/ # PT Portugal (Évora) Debian=ftp://ftp.arnes.si/packages/debian/ # SI Slovenia Non-US=ftp://ftp.arnes.si/packages/debian-non-US/ # SI Slovenia Debian=http://ftp.fr.debian.org/debian/ # FR France Non-US=http://ftp.fr.debian.org/debian-non-US/ # FR France Debian=ftp://ftp.proxad.net/mirrors/ftp.debian.org/ # FR France Non-US=ftp://ftp.proxad.net/mirrors/nonus.debian.org/ # FR France Debian=http://debian.uni-essen.de/debian/ # DE Germany (Essen, Nordrhein-Westfalen) Debian=http://www.mirror.ac.uk/sites/ftp.debian.org/debian/ # GB Great Britain (Canterbury and Lancaster) Non-US=http://www.mirror.ac.uk/sites/non-us.debian.org/debian-non-US/ # GB Great Britain (Canterbury and Lancaster) Debian=ftp://ftp.minet.net/debian/ # FR France (Evry) Non-US=ftp://ftp.minet.net/debian-non-US/ # FR France (Evry) Debian=http://ftp.is.co.za/debian/ # ZA South Africa (Johannesburg) Non-US=http://ftp.is.co.za/debian-non-US/ # ZA South Africa (Johannesburg) Debian=ftp://ftp.index.hu/debian/ # HU Hungary (Budapest) Non-US=ftp://ftp.index.hu/debian-non-US/ # HU Hungary (Budapest) Debian=ftp://ftp.dti.ad.jp/pub/Linux/debian/ # JP Japan (Tokyo) Non-US=ftp://ftp.dti.ad.jp/pub/Linux/debian-non-US/ # JP Japan (Tokyo) Debian=http://ftp.esat.net/pub/linux/debian/ # IE Ireland (Dublin) Non-US=http://ftp.esat.net/pub/linux/debian-non-US/ # IE Ireland (Dublin) Debian=http://ftp.du.se/debian/ # SE Sweden Non-US=http://ftp.du.se/debian-non-US/ # SE Sweden Debian=http://mirror.direct.ca/linux/debian/ # CA Canada (Vancouver) Non-US=http://mirror.direct.ca/linux/debian-non-US/ # CA Canada (Vancouver) Debian=http://ftp.si.debian.org/debian/ # SI Slovenia (Maribor) Non-US=http://ftp.si.debian.org/debian-non-US/ # SI Slovenia (Maribor) Debian=http://mirror.cs.wisc.edu/pub/mirrors/linux/debian/ # US United States (Madison, Wisconsin) Debian=ftp://ftp.linux.co.za/pub/distributions/debian/ # ZA South Africa (Johannesburg) Debian=http://ftp.kfki.hu/debian/ # HU Hungary (Budapest) Non-US=http://ftp.kfki.hu/debian-non-US/ # HU Hungary (Budapest) Debian=http://ftp.hk.debian.org/debian/ # HK Hong Kong Non-US=http://ftp.hk.debian.org/debian-non-US/ # HK Hong Kong Debian=http://ftp.kornet.net/pub/Linux/debian/ # KR Korea (Seoul) Debian=ftp://ftp.nuri.net/pub/debian/ # KR Korea (Seoul) Debian=http://ftp.eu.uu.net/debian/ # NL Netherlands (Amsterdam) Non-US=http://ftp.eu.uu.net/debian-non-US/ # NL Netherlands (Amsterdam) Debian=http://gd.tuwien.ac.at/opsys/linux/debian/ # AT Austria (Vienna) Non-US=http://gd.tuwien.ac.at/opsys/linux/debian-non-US/ # AT Austria (Vienna) Debian=http://ftp.info.iut-tlse3.fr/debian/ # FR France Non-US=http://ftp.info.iut-tlse3.fr/debian-non-US/ # FR France Debian=http://ftp.latnet.lv/linux/debian/ # LV Latvia (Riga) Non-US=http://ftp.latnet.lv/linux/debian-non-US/ # LV Latvia (Riga) Debian=http://ftp.no.debian.org/debian/ # NO Norway (Oslo) Non-US=http://ftp.no.debian.org/debian-non-US/ # NO Norway (Oslo) Debian=http://ftp.icm.edu.pl/pub/Linux/debian/ # PL Poland (Warsaw) Non-US=http://ftp.icm.edu.pl/pub/Linux/distributions/debian-non-US/ # PL Poland (Warsaw) Debian=http://ftp.ee.debian.org/debian/ # EE Estonia Non-US=http://ftp.ee.debian.org/debian-non-US/ # EE Estonia Debian=http://ftp.freenet.de/debian/ # DE Germany (Düsseldorf) Non-US=http://ftp.freenet.de/debian-non-US/ # DE Germany (Düsseldorf) Debian=ftp://ftp.uwsg.indiana.edu/linux/debian/ # US United States (Bloomington, Indiana) Debian=http://debian.tod.net/debian/ # US United States (Cleveland, Ohio) Debian=http://ftp.hu.debian.org/debian/ # HU Hungary (Budapest) Non-US=http://ftp.hu.debian.org/debian-non-US/ # HU Hungary (Budapest) Debian=http://debian.inf.elte.hu/debian/ # HU Hungary (Budapest) Non-US=http://debian.inf.elte.hu/debian-non-US/ # HU Hungary (Budapest) Debian=http://ftp.nz.debian.org/debian/ # NZ New Zealand (Wellington) Non-US=http://ftp.nz.debian.org/debian-non-US/ # NZ New Zealand (Wellington) Debian=http://ftp.eq.uc.pt/software/Linux/debian/ # PT Portugal (Coimbra) Non-US=http://ftp.eq.uc.pt/software/Linux/debian-non-US/ # PT Portugal (Coimbra) Debian=http://debian.mur.at/debian/ # AT Austria (Graz) Non-US=http://debian.mur.at/debian-non-US/ # AT Austria (Graz) Debian=http://ftp.uni-erlangen.de/pub/Linux/debian/ # DE Germany (Erlangen) Non-US=http://ftp.uni-erlangen.de/pub/Linux/debian/debian-non-US/ # DE Germany (Erlangen) Debian=http://debian.otenet.gr/debian/ # GR Greece (Athens) Non-US=http://debian.otenet.gr/debian-non-US/ # GR Greece (Athens) Debian=http://dennou-t.ms.u-tokyo.ac.jp/library/Linux/debian/ # JP Japan (Tokyo) Non-US=http://dennou-t.ms.u-tokyo.ac.jp/library/Linux/debian-non-US/ # JP Japan (Tokyo) Debian=http://dennou-k.gaia.h.kyoto-u.ac.jp/library/Linux/debian/ # JP Japan (Kyoto) Non-US=http://dennou-k.gaia.h.kyoto-u.ac.jp/library/Linux/debian-non-US/ # JP Japan (Kyoto) Debian=http://dennou-q.geo.kyushu-u.ac.jp/library/Linux/debian/ # JP Japan (Fukuoka) Non-US=http://dennou-q.geo.kyushu-u.ac.jp/library/Linux/debian-non-US/ # JP Japan (Fukuoka) Debian=http://natasha.stmarytx.edu/debian/ # US United States (San Antonio, Texas) Debian=http://ftp2.fr.debian.org/debian/ # FR France Non-US=http://ftp2.fr.debian.org/debian-non-US/ # FR France Debian=http://ftp.gul.uc3m.es/debian/ # ES Spain (Leganes, Madrid) Non-US=http://ftp.gul.uc3m.es/debian-non-US/ # ES Spain (Leganes, Madrid) Debian=http://ftp.ndlug.nd.edu/mirrors/debian/ # US United States (Notre Dame, Indiana) Debian=ftp://debian.das.ufsc.br/pub/debian/ # BR Brazil (Florianópolis) Non-US=ftp://debian.das.ufsc.br/pub/debian-non-US/ # BR Brazil (Florianópolis) Debian=http://debian.ua.pt/debian/ # PT Portugal (Aveiro) Debian=http://debian.uchicago.edu/debian/ # US United States (Chicago, Illinois) Debian=http://ftp.irb.hr/debian/ # HR Croatia (Zagreb) Non-US=http://ftp.irb.hr/debian-non-US/ # HR Croatia (Zagreb) Debian=http://ftp.es.debian.org/debian/ # ES Spain (Madrid) Non-US=http://ftp.es.debian.org/debian-non-US/ # ES Spain (Madrid) Debian=http://ftp.se.debian.org/debian/ # SE Sweden (Umeå) Non-US=http://ftp.se.debian.org/debian-non-US/ # SE Sweden (Umeå) Debian=http://carroll.aset.psu.edu/pub/linux/distributions/debian/ # US United States (Pennsylvania) Debian=http://sunsite.informatik.rwth-aachen.de/ftp/pub/Linux/debian/ # DE Germany (Aachen) Non-US=http://sunsite.informatik.rwth-aachen.de/ftp/pub/Linux/debian-non-US/ # DE Germany (Aachen) Debian=ftp://ftp.lip6.fr/pub/linux/distributions/debian/ # FR France (Jussieu) Debian=http://debian.fifi.org/debian/ # US United States (California) Debian=http://ftp.fi.debian.org/debian/ # FI Finland (Tampere) Non-US=http://ftp.fi.debian.org/debian-non-US/ # FI Finland (Tampere) Debian=http://ftp-stud.fht-esslingen.de/debian/ # DE Germany (Esslingen) Non-US=http://ftp-stud.fht-esslingen.de/debian-non-US/ # DE Germany (Esslingen) Debian=http://debian.sh.cvut.cz/debian/ # CZ Czech Republic (Prague) Non-US=http://debian.sh.cvut.cz/debian-non-US/ # CZ Czech Republic (Prague) Debian=http://ftp.tku.edu.tw/OS/Linux/distributions/debian/ # TW Taiwan (TamSui) Non-US=http://ftp.tku.edu.tw/OS/Linux/distributions/debian/non-US/ # TW Taiwan (TamSui) Debian=ftp://gladiator.real-time.com/linux/debian/ # US United States (Minneapolis, Minnesota) Debian=http://mirrors.kernel.org/debian/ # US United States (Santa Clara, California) Debian=http://mirrors.xmission.com/debian/ # US United States (Salt Lake City, Utah) Debian=http://mirrors.rcn.net/debian/ # US United States (Fairfax, Virginia) Debian=http://ftp.keystealth.org/debian/ # US United States (Redwood City, California) Debian=http://debian.yorku.ca/debian/ # CA Canada (Toronto, Ontario) Non-US=http://debian.yorku.ca/debian/non-US/ # CA Canada (Toronto, Ontario) Debian=ftp://ftp.stw-bonn.de/pub/mirror/debian/ # DE Germany (Bonn) Debian=ftp://ftp.edisontel.com/pub/Debian_Mirror/ # IT Italy Debian=http://debian.ens-cachan.fr/ftp/debian/ # FR France Debian=http://ftp.easynet.be/ftp/debian/ # BE Belgium (Brussels) Non-US=http://ftp.easynet.be/ftp/debian-non-US/ # BE Belgium (Brussels) Debian=http://ameba.sc-uni.ktu.lt/debian/ # LT Lithuania (Kaunas) Non-US=http://ameba.sc-uni.ktu.lt/debian-non-US/ # LT Lithuania (Kaunas) Debian=http://ftp.ticklers.org/debian/ # GB Great Britain (London) Non-US=http://ftp.ticklers.org/debian/non-US/ # GB Great Britain (London) Debian=ftp://ftp.stealth.net/pub/mirrors/ftp.debian.org/ # US United States (New York) Debian=http://ftp.lug.udel.edu/debian/ # US United States (Newark, Delaware) Non-US=http://ftp.lug.udel.edu/debian-non-US/ # US United States (Newark, Delaware) Debian=ftp://debian.marked.no/debian/ # NO Norway (Oslo) Non-US=ftp://debian.marked.no/debian-non-US/ # NO Norway (Oslo) Debian=ftp://ftp.linux.co.uk/pub/debian/ # GB Great Britain Non-US=ftp://ftp.linux.co.uk/pub/debian/non-US/ # GB Great Britain Debian=ftp://ftp.gui.uva.es/debian/ # ES Spain (Valladolid) Debian=http://ftp.rediris.es/debian/ # ES Spain (Madrid) Non-US=http://ftp.rediris.es/debian-non-US/ # ES Spain (Madrid) Debian=http://download.unesp.br/linux/debian/ # BR Brazil (São Paulo) Non-US=http://download.unesp.br/linux/debian-non-US/ # BR Brazil (São Paulo) Debian=http://mirror.pacific.net.au/debian/ # AU Australia (Sydney) Non-US=http://mirror.pacific.net.au/debian-non-US/ # AU Australia (Sydney) Debian=http://debian.lcs.mit.edu/debian/ # US United States (Cambridge, Massachusetts) Debian=http://ftp.u-picardie.fr/mirror/debian/ # FR France (Amiens) Non-US=http://ftp.u-picardie.fr/mirror/debian-non-US/ # FR France (Amiens) Debian=http://ftp.belnet.be/debian/ # BE Belgium (Brussels) Non-US=http://ftp.belnet.be/debian-non-US/ # BE Belgium (Brussels) Debian=http://ftp.au.debian.org/debian/ # AU Australia Non-US=http://ftp.au.debian.org/debian-non-US/ # AU Australia Debian=http://linux.sarang.net/ftp/mirror/os/linux/distribution/debian/ # KR Korea (Seoul) Non-US=http://linux.sarang.net/ftp/mirror/os/linux/distribution/debian-non-US/ # KR Korea (Seoul) Debian=http://ftp.rutgers.edu/pub/debian/ # US United States (Piscataway, New Jersey) Debian=http://debian.rutgers.edu/ # US United States (Piscataway, New Jersey) Non-US=ftp://debian.hanyang.ac.kr/debian-non-US/ # KR Korea (Ansan) Debian=http://ftp.surfnet.nl/os/Linux/distr/debian/ # NL Netherlands Debian=ftp://download.xs4all.nl/pub/mirror/debian/ # NL Netherlands (AMS-IX, Amsterdam) Debian=http://debian.mirrors.easynet.fr/ # FR France (Paris) Non-US=ftp://debian.mirrors.easynet.fr/debian-non-US/ # FR France (Paris) Debian=http://ftp.debian.nl/debian/ # NL Netherlands (Alphen aan den Rijn) Non-US=http://ftp.debian.nl/debian-non-US/ # NL Netherlands (Alphen aan den Rijn) Debian=http://www.zentek-international.com/mirrors/debian/debian/ # HK Hong Kong Debian=http://ftp.u-strasbg.fr/debian/ # FR France Non-US=http://ftp.u-strasbg.fr/debian-non-US/ # FR France Debian=http://archive.progeny.com/debian/ # US United States (Indianapolis, Indiana) Debian=http://mirror.csit.fsu.edu/debian/ # US United States (Tallahassee, Florida) Non-US=http://mirror.csit.fsu.edu/debian-non-US/ # US United States (Tallahassee, Florida) Debian=http://kebo.vlsm.org/debian/ # ID Indonesia (Surabaya) Non-US=http://kebo.vlsm.org/debian-non-US/ # ID Indonesia (Surabaya) Debian=http://ftp.br.debian.org/debian/ # BR Brazil (Curitiba) Non-US=http://ftp.br.debian.org/debian-non-US/ # BR Brazil (Curitiba) Debian=http://ftp.linuxforum.net/ftp/debian/ # CN China Non-US=http://ftp.linuxforum.net/ftp/debian-non-US/ # CN China Debian=http://debian.nettuno.it/debian/ # IT Italy Non-US=http://debian.nettuno.it/debian-non-US/ # IT Italy Debian=http://ftp.carnet.hr/pub/debian/ # HR Croatia (Zagreb) Non-US=http://ftp.carnet.hr/pub/debian/debian-non-US/ # HR Croatia (Zagreb) Debian=http://ftp.nl.debian.org/debian/ # NL Netherlands (Enschede) Non-US=http://ftp.nl.debian.org/debian-non-US/ # NL Netherlands (Enschede) Debian=http://ftp.iinet.net.au/debian/debian/ # AU Australia (Perth) Non-US=http://ftp.iinet.net.au/debian/debian-non-US/ # AU Australia (Perth) Debian=http://ftp.xgate.co.kr/debian/ # KR Korea (Seoul) Non-US=http://ftp.xgate.co.kr/debian-non-US/ # KR Korea (Seoul) Debian=http://debian.balt.net/debian/ # LT Lithuania (Vilnius) Non-US=http://debian.balt.net/debian-non-US/ # LT Lithuania (Vilnius) Debian=http://debian.3wsi.net/debian/ # ID Indonesia (World Trade Center, Jakarta) Non-US=http://debian.3wsi.net/debian-non-US/ # ID Indonesia (World Trade Center, Jakarta) Debian=http://linux.csua.berkeley.edu/debian/ # US United States (Berkeley, California) Non-US=http://linux.csua.berkeley.edu/debian-non-US/ # US United States (Berkeley, California) Debian=http://mirror.devolus.org/debian/ # KR Korea Non-US=http://mirror.devolus.org/debian-non-US/ # KR Korea Debian=http://ftp.silug.org/pub/debian/ # US United States (Illinois) Debian=http://ftp.externet.hu/debian/ # HU Hungary Debian=ftp://ftp.fu-berlin.de/pub/unix/linux/mirrors/debian/ # DE Germany (Berlin) Non-US=ftp://ftp.fu-berlin.de/pub/unix/linux/mirrors/debian-non-US/ # DE Germany (Berlin) Debian=http://ftp.funet.fi/pub/linux/mirrors/debian/ # FI Finland Non-US=http://ftp.funet.fi/pub/linux/mirrors/debian-non-US/ # FI Finland Debian=http://debian.iskon.hr/debian/ # HR Croatia (Zagreb) Non-US=http://debian.iskon.hr/debian-non-US/ # HR Croatia (Zagreb) Debian=http://debian.tu-bs.de/debian/ # DE Germany (Braunschweig) Non-US=http://debian.tu-bs.de/debian-non-US/ # DE Germany (Braunschweig) Debian=http://ftp.uni-koeln.de/debian/ # DE Germany (Köln) Non-US=http://ftp.uni-koeln.de/debian-non-US/ # DE Germany (Köln) Debian=ftp://ftp.nectec.or.th/pub/linux-distributions/Debian/ # TH Thailand Non-US=ftp://ftp.nectec.or.th/pub/linux-distributions/Debian-non-US/ # TH Thailand Debian=http://debian.nsu.ru/debian/ # RU Russia (Novosibirsk) Non-US=http://debian.nsu.ru/debian-non-US/ # RU Russia (Novosibirsk) Debian=http://ftp.iitm.ac.in/debian/ # IN India (Madras) Non-US=http://ftp.iitm.ac.in/debian-non-US/ # IN India (Madras) Debian=http://debian.pffa.de/mirrors/debian/ # DE Germany (Berlin) Non-US=http://debian.pffa.de/mirrors/debian-non-US/ # DE Germany (Berlin) Debian=ftp://linux.csie.nctu.edu.tw/debian/ # TW Taiwan Non-US=ftp://linux.csie.nctu.edu.tw/distributions/debian/debian-non-US/ # TW Taiwan Debian=http://ftp.is.debian.org/debian/ # IS Iceland (Reykjavik) Non-US=http://ftp.is.debian.org/debian-non-US/ # IS Iceland (Reykjavik) Debian=ftp://debian.pipcom.com/debian/debian/ # CA Canada (Peterborough, Ontario) Non-US=ftp://debian.pipcom.com/debian/debian-non-US/ # CA Canada (Peterborough, Ontario) Debian=http://debian.experimentos.cl/debian/ # CL Chile (Valparaiso) Non-US=http://debian.experimentos.cl/debian-non-US/ # CL Chile (Valparaiso) Debian=http://ftp.debian.skynet.be/ftp/debian/ # BE Belgium (Brussels) Non-US=http://ftp.debian.skynet.be/ftp/debian-non-US/ # BE Belgium (Brussels) Debian=http://debian.secsup.org/ # US United States Debian=http://debian.psu.ru/debian/ # RU Russia (Perm) Non-US=http://debian.psu.ru/debian-non-US/ # RU Russia (Perm) Debian=http://ftp.tu-graz.ac.at/mirror/debian/ # AT Austria (Graz) Non-US=http://ftp.tu-graz.ac.at/mirror/debian-non-US/ # AT Austria (Graz) Debian=http://ftp.dk.debian.org/debian/ # DK Denmark Non-US=http://ftp.dk.debian.org/debian-non-US/ # DK Denmark Debian=http://ftp.lugs.org.sg/debian/ # SG Singapore Debian=http://linux.org.by/debian/ # BY Belarus (Minsk) Non-US=http://linux.org.by/debian-non-US/ # BY Belarus (Minsk) Debian=ftp://ftp.mpi-sb.mpg.de/pub/linux/distributions/debian/debian/ # DE Germany (Saarbrücken) Non-US=ftp://ftp.mpi-sb.mpg.de/pub/linux/distributions/debian/non-us/debian-non-US/ # DE Germany (Saarbrücken) Debian=http://ftp.cl.debian.org/debian/ # CL Chile (Santiago) Non-US=http://ftp.cl.debian.org/debian-non-US/ # CL Chile (Santiago) Debian=http://obelix.umh.es/pub/debian/debian/ # ES Spain (Elche) Non-US=http://obelix.umh.es/pub/debian/debian-non-US/ # ES Spain (Elche) Debian=http://ftp.tiscali.nl/debian/ # NL Netherlands (Amsterdam) Non-US=http://ftp.tiscali.nl/debian-non-US/ # NL Netherlands (Amsterdam) Debian=http://debian.teleglobe.net/ # US United States (Newark, New Jersey and London, Great Britain) Non-US=http://debian.teleglobe.net/non-US/ # US United States (Newark, New Jersey and London, Great Britain) Debian=http://ftp.zcu.cz/ftp/pub/linux/debian/ # CZ Czech Republic (Plzen) Non-US=http://ftp.zcu.cz/ftp/pub/linux/debian-non-US/ # CZ Czech Republic (Plzen) Debian=http://ftp.jyu.fi/debian/ # FI Finland (Jyväskylä) Non-US=http://ftp.jyu.fi/debian-non-US/ # FI Finland (Jyväskylä) Debian=http://ftp.unina.it/pub/linux/distributions/debian/debian/ # IT Italy (Napoli) Non-US=http://ftp.unina.it/pub/linux/distributions/debian/debian-non-US/ # IT Italy (Napoli) Debian=http://kalle.csb.ki.se/pub/linux/debian/ # SE Sweden (Huddinge) Non-US=http://kalle.csb.ki.se/pub/linux/debian-non-US/ # SE Sweden (Huddinge) Debian=http://ftp.ntua.gr/pub/linux/debian/ # GR Greece (Athens) Non-US=http://ftp.ntua.gr/pub/linux/debian-non-US/ # GR Greece (Athens) Debian=http://ftp.ipv6.opentransit.net/debian/ # FR France (Paris) Non-US=http://ftp.ipv6.opentransit.net/debian-non-US/ # FR France (Paris) Debian=http://ftp.leo.org/debian/ # DE Germany (Munich) Non-US=http://ftp.leo.org/debian-non-US/ # DE Germany (Munich) Debian=http://ftp.matrix.net.br/pub/debian/ # BR Brazil (Sao Paulo) Debian=http://techweb.rfa.org/debian/ # US United States (Washington, D.C.) Debian=http://mirror.pudas.net/debian/ # SE Sweden Non-US=http://mirror.pudas.net/debian-non-US/ # SE Sweden Debian=http://debian.oregonstate.edu/debian/ # US United States (Corvallis, Oregon) Debian=http://debian.csie.ntu.edu.tw/debian/ # TW Taiwan Non-US=http://debian.csie.ntu.edu.tw/debian-non-US/ # TW Taiwan Debian=http://ftp.tiscali.de/pub/debian/debian/ # DE Germany (Frankfurt am Main) Non-US=http://ftp.tiscali.de/pub/debian/debian-non-US/ # DE Germany (Frankfurt am Main) Debian=http://lyre.mit.edu/debian/ # US United States (Cambridge, Massachusetts) Debian=http://debian.udsu.ru/debian/ # RU Russia Non-US=http://debian.udsu.ru/debian-non-US/ # RU Russia Debian=http://debian.linux.org.tw/debian/ # TW Taiwan (Taipei) Non-US=http://debian.linux.org.tw/debian-non-US/ # TW Taiwan (Taipei) Debian=http://debian.lami.univ-evry.fr/debian/ # FR France (Evry) Non-US=http://debian.lami.univ-evry.fr/debian-non-US/ # FR France (Evry) Debian=http://sft.if.usp.br/debian/ # BR Brazil (Sao Paulo) Non-US=http://sft.if.usp.br/debian-non-US/ # BR Brazil (Sao Paulo) Debian=http://mirror.cict.fr/debian/ # FR France (Toulouse) Non-US=http://mirror.cict.fr/debian-non-US/ # FR France (Toulouse) Debian=http://ftp.cprm.net/debian/ # PT Portugal (Lisboa) Non-US=http://ftp.cprm.net/debian/non-US/ # PT Portugal (Lisboa) Debian=http://wuarchive.wustl.edu/mirrors/debian/ # US United States (St. Louis, Missouri) Debian=ftp://mir1.ovh.net/debian/ # FR France Non-US=ftp://mir1.ovh.net/debian-non-US/ # FR France Debian=http://mir2.ovh.net/debian/ # FR France Non-US=http://mir2.ovh.net/debian-non-US/ # FR France Debian=http://linux.cdpa.nsysu.edu.tw/debian/ # TW Taiwan Non-US=http://linux.cdpa.nsysu.edu.tw/debian-non-US/ # TW Taiwan Debian=ftp://mirror.ipartners.pl/pub/debian/ # PL Poland Non-US=ftp://mirror.ipartners.pl/pub/debian-non-US/ # PL Poland Debian=http://mirror.mcs.anl.gov/debian/ # US United States (Chicago, Illinois) Debian=http://linorg.usp.br/debian/ # BR Brazil (Sao Paulo) Non-US=http://linorg.usp.br/debian-non-US/ # BR Brazil (Sao Paulo) Debian=http://koyanet.lv/ftp/debian/ # LV Latvia (Riga) Non-US=http://koyanet.lv/ftp/debian-non-US/ # LV Latvia (Riga) Debian=http://softcity.libero.it/debian/ # IT Italy (Milano) Non-US=http://softcity.libero.it/debian-non-US/ # IT Italy (Milano) Debian=http://debian.serveftp.net/debian/ # DE Germany (Nürnberg) Non-US=http://debian.serveftp.net/debian-non-US/ # DE Germany (Nürnberg) Debian=http://mirror.averse.net/debian/ # SG Singapore Non-US=http://mirror.averse.net/debian-non-US/ # SG Singapore Debian=ftp://ftp.nerim.net/debian/ # FR France (Paris) Non-US=ftp://ftp.nerim.net/debian-non-US/ # FR France (Paris) Debian=http://ftp.pop-ce.rnp.br/debian/ # BR Brazil (Fortaleza, Ceará) Debian=http://debian.osdn.org.ua/debian/ # UA Ukraine (Kiev) Non-US=http://debian.osdn.org.ua/debian-non-US/ # UA Ukraine (Kiev) Debian=http://ftp.kreonet.re.kr/pub/Linux/debian/ # KR Korea (Daejeon) Non-US=http://ftp.kreonet.re.kr/pub/Linux/debian-non-US/ # KR Korea (Daejeon) Debian=http://debian.goldweb.com.au/debian/ # AU Australia (Canberra) Debian=ftp://ftp.tu-chemnitz.de/pub/linux/debian/debian/ # DE Germany (Chemnitz) Non-US=ftp://ftp.tu-chemnitz.de/pub/linux/debian/debian-non-US/ # DE Germany (Chemnitz) Debian=http://ftp.tr.debian.org/debian/ # TR Turkey (Ankara) Non-US=http://ftp.tr.debian.org/debian-non-US/ # TR Turkey (Ankara) Debian=ftp://www.buraphalinux.org/pub/debian/ # TH Thailand (Chonburi) Debian=http://ftp.sun.ac.za/ftp/debian/ # ZA South Africa (Stellenbosch) Non-US=http://ftp.sun.ac.za/ftp/debian/non-US/ # ZA South Africa (Stellenbosch) Debian=http://debian.essentkabel.com/debian/ # NL Netherlands (Zwolle) Non-US=http://debian.essentkabel.com/debian-non-US/ # NL Netherlands (Zwolle) Debian=http://ftp.uni-stuttgart.de/debian/ # DE Germany (Stuttgart) Non-US=http://ftp.uni-stuttgart.de/debian-non-US/ # DE Germany (Stuttgart) Debian=http://debian.2z.net/debian/ # US United States (Hibbing, Minnesota) Debian=http://ftp.psn.ru/debian/ # RU Russia (Pushchino, Moscow Region) Non-US=http://ftp.psn.ru/debian-non-US/ # RU Russia (Pushchino, Moscow Region) Debian=http://debian.ludost.net/debian/ # BG Bulgaria (Sofia) Non-US=http://debian.ludost.net/debian-non-US/ # BG Bulgaria (Sofia) Debian=http://debian.blueyonder.co.uk/ # GB Great Britain (London, Heathrow) Non-US=http://debian.blueyonder.co.uk/non-US/ # GB Great Britain (London, Heathrow) Debian=http://mirror.datafast.net.au/linux/debian/ # AU Australia (Geelong, Victoria) Debian=ftp://ftp.europeonline.net/debian/ # LU Luxembourg Non-US=ftp://ftp.europeonline.net/debian/non-US/ # LU Luxembourg Debian=http://sluglug.ucsc.edu/debian/ # US United States (Santa Cruz, California) Debian=ftp://ftp.cs.stevens-tech.edu/pub/Linux/distributions/debian/ # US United States (Hoboken, New Jersey) Debian=http://mi.mirror.garr.it/mirrors/debian/ # IT Italy (Milano) Non-US=http://mi.mirror.garr.it/mirrors/debian/non-US/ # IT Italy (Milano) Debian=http://ftp.uni-kl.de/pub/linux/debian/ # DE Germany (Kaiserslautern) Debian=http://ftp.hr.debian.org/debian/ # HR Croatia (Zagreb) Non-US=http://ftp.hr.debian.org/debian-non-US/ # HR Croatia (Zagreb) Debian=http://ftp.univie.ac.at/systems/linux/debian/debian/ # AT Austria (Wien) Non-US=http://ftp.univie.ac.at/systems/linux/debian/debian-non-US/ # AT Austria (Wien) Debian=http://nisamox.fciencias.unam.mx/debian/ # MX Mexico Non-US=http://nisamox.fciencias.unam.mx/debian-non-US/ # MX Mexico Debian=http://mirror.positive-internet.com/debian/ # GB Great Britain (London) Debian=http://ftp.bme.hu/OS/Linux/dist/debian/ # HU Hungary (Budapest) Debian=http://ftp.duth.gr/debian/ # GR Greece (Xanthi) Non-US=http://ftp.duth.gr/debian-non-US/ # GR Greece (Xanthi) Debian=http://ftp.linux.pt/pub/mirrors/debian/ # PT Portugal (Lisboa) Non-US=http://ftp.linux.pt/pub/mirrors/debian/non-US/ # PT Portugal (Lisboa) Debian=http://debian.fastweb.it/debian/ # IT Italy (Milano) Non-US=http://debian.fastweb.it/debian-non-US/ # IT Italy (Milano) Debian=http://debian.kida.net/debian.mirror/debian/ # CA Canada (Kelowna, British Columbia) Debian=http://ftp3.nrc.ca/debian/ # CA Canada (Ottawa) Non-US=http://ftp3.nrc.ca/debian-non-US/ # CA Canada (Ottawa) Debian=http://debian.ihug.com.au/debian/ # AU Australia (Surry Hills, Sydney) Non-US=http://debian.ihug.com.au/debian-non-US/ # AU Australia (Surry Hills, Sydney) Debian=http://ftp.ie.debian.org/debian/ # IE Ireland (Dublin) Non-US=http://ftp.ie.debian.org/debian-non-US/ # IE Ireland (Dublin) Debian=http://debian.ubiobio.cl/debian/ # CL Chile (Concepcion) Non-US=http://debian.ubiobio.cl/debian-non-US/ # CL Chile (Concepcion) Debian=http://linux.iq.usp.br/debian/ # BR Brazil (São Paulo) Non-US=http://linux.iq.usp.br/debian-non-US/ # BR Brazil (São Paulo) Debian=ftp://mirror.nus.edu.sg/pub/Debian/ # SG Singapore Non-US=ftp://mirror.nus.edu.sg/pub/Debian-non-US/ # SG Singapore Debian=http://the.earth.li/debian/ # GB Great Britain (London) Non-US=http://the.earth.li/debian-non-US/ # GB Great Britain (London) Debian=http://slugsite.louisville.edu/debian/ # US United States (Louisville, Kentucky) Debian=http://cudlug.cudenver.edu/debian/ # US United States (Denver, Colorado) Debian=http://ftp.softnet.tuc.gr/ftp/linux/debian/ # GR Greece (Chania) Non-US=http://ftp.softnet.tuc.gr/ftp/linux/debian-non-US/ # GR Greece (Chania) Debian=ftp://ftp.uos.ac.kr/mirror/Linux/debian/debian/ # KR Korea Non-US=ftp://ftp.uos.ac.kr/mirror/Linux/debian/debian-non-US/ # KR Korea Debian=ftp://ftp.linux.org.tr/pub/mirrors/debian/ # TR Turkey (Ankara) Debian=http://ftp.uk.debian.org/debian/ # GB Great Britain (London) Non-US=http://ftp.uk.debian.org/debian-non-US/ # GB Great Britain (London) Debian=http://mirror.ox.ac.uk/debian/ # GB Great Britain (Oxford) Non-US=http://mirror.ox.ac.uk/debian-non-US/ # GB Great Britain (Oxford) Debian=ftp://mirrors.geeks.org/debian/ # US United States (Minneapolis, Minnesota) Debian=http://mirrors.engr.arizona.edu/debian/ # US United States (Tucson, Arizona) Debian=http://debian.ambra.ro/debian/ # RO Romania (Piatra Neamt) Non-US=http://debian.ambra.ro/debian-non-US/ # RO Romania (Piatra Neamt) Debian=http://mirrors.terrabox.com/debian/ # US United States (Dallas, Texas) Debian=http://ftp.crihan.fr/debian/ # FR France (Rouen) Non-US=http://ftp.crihan.fr/debian-non-US/ # FR France (Rouen) Debian=http://ftp.ro.debian.org/debian/ # RO Romania (Iasi) Non-US=http://ftp.ro.debian.org/debian-non-US/ # RO Romania (Iasi) Debian=http://opensource.nchc.org.tw/debian/ # TW Taiwan Non-US=http://opensource.nchc.org.tw/debian-non-US/ # TW Taiwan Debian=http://mirrors.geekbone.org/debian/ # CN China (Shanghai) Non-US=http://mirrors.geekbone.org/debian-non-US/ # CN China (Shanghai) Debian=http://debian.midco.net/debian/ # US United States Debian=http://debian.uni.edu.ni/debian/ # NI Nicaragua (Managua) Non-US=http://debian.uni.edu.ni/debian/ # NI Nicaragua (Managua) Debian=http://ftp.ps.pl/pub/Linux/debian/ # PL Poland (Szczecin) Non-US=http://ftp.ps.pl/pub/Linux/debian-non-us/ # PL Poland (Szczecin) Debian=http://ftp.port80.se/debian/ # SE Sweden (Stockholm) Non-US=http://ftp.port80.se/debian-non-US/ # SE Sweden (Stockholm) Debian=http://debian.inode.at/debian/ # AT Austria (Vienna) Non-US=http://debian.inode.at/debian-non-US/ # AT Austria (Vienna) Debian=http://mirrors.usc.edu/pub/linux/distributions/debian/ # US United States (Los Angeles, California) Debian=http://mirror.here.dk/debian/ # DK Denmark (Copenhagen) Non-US=http://mirror.here.dk/debian-non-US/ # DK Denmark (Copenhagen) Debian=http://jane.uab.es/debian/ # ES Spain (Barcelona, Catalunya) Non-US=http://jane.uab.es/debian-non-US/ # ES Spain (Barcelona, Catalunya) Debian=http://mirrors.sec.informatik.tu-darmstadt.de/debian/debian/ # DE Germany (Darmstadt) Non-US=http://mirrors.sec.informatik.tu-darmstadt.de/debian/debian-non-US/ # DE Germany (Darmstadt) Debian=http://debian.uni-c.dk/debian/ # DK Denmark (Copenhagen) Non-US=http://debian.uni-c.dk/debian-non-US/ # DK Denmark (Copenhagen) Debian=http://ftp.caliu.info/debian/ # ES Spain (Barcelona) Non-US=http://ftp.caliu.info/debian-non-US/ # ES Spain (Barcelona) Debian=http://mirror.hamakor.org.il/pub/mirrors/debian/ # IL Israel (Haifa, Actcom) Non-US=http://mirror.hamakor.org.il/pub/mirrors/debian-non-US/ # IL Israel (Haifa, Actcom) Debian=http://ftp.isu.edu.tw/pub/Linux/Debian/debian/ # TW Taiwan (Kaohsiung) Debian=http://debian.mirrors.pair.com/ # US United States (Pittsburgh, PA) Debian=ftp://debian.ihug.co.nz/debian/ # NZ New Zealand (Auckland) Non-US=ftp://debian.ihug.co.nz/debian-non-US/ # NZ New Zealand (Auckland) Debian=ftp://ftp.tuxfamily.org/debian/ # FR France (Paris) Non-US=ftp://ftp.tuxfamily.org/debian-non-US/ # FR France (Paris) Debian=http://debian.spark.net.gr/debian/ # GR Greece (Thessaloniki) Non-US=http://debian.spark.net.gr/debian-non-US/ # GR Greece (Thessaloniki) Debian=http://debian.fapeal.br/debian/ # BR Brazil (Maceió, Alagoas) Non-US=http://debian.fapeal.br/debian-non-US/ # BR Brazil (Maceió, Alagoas) Debian=http://debian.luxadmin.org/debian/ # LU Luxembourg (Hamm) Non-US=http://debian.luxadmin.org/debian-non-US/ # LU Luxembourg (Hamm) Debian=http://lug.mtu.edu/debian/ # US United States (Houghton, Michigan) Debian=http://ftp.jp.debian.org/debian/ # JP Japan (Nara) Non-US=http://ftp.jp.debian.org/debian-non-US/ # JP Japan (Nara) Debian=http://ftp.yz.yamagata-u.ac.jp/debian/ # JP Japan (Yonezawa, Yamagata) Non-US=http://ftp.yz.yamagata-u.ac.jp/debian-non-US/ # JP Japan (Yonezawa, Yamagata) Debian=http://debian.vicnet.net.au/debian/ # AU Australia (Melbourne) Non-US=http://debian.vicnet.net.au/debian-non-US/ # AU Australia (Melbourne) Debian=http://debian.mirror.solnet.ch/debian/ # CH Switzerland (Solothurn) Non-US=http://debian.mirror.solnet.ch/debian-non-US/ # CH Switzerland (Solothurn) Debian=ftp://ftp.solnet.ch/mirror/Debian/debian/ # CH Switzerland (Solothurn) Non-US=ftp://ftp.solnet.ch/mirror/Debian/debian-non-US/ # CH Switzerland (Solothurn) Debian=ftp://less.cogeco.net/pub/debian/ # CA Canada (Burlington, Ontario) Debian=http://debian.org.ua/debian/ # UA Ukraine (Kiev) Non-US=http://debian.org.ua/debian-non-US/ # UA Ukraine (Kiev) Debian=http://ftp.kr.debian.org/debian/ # KR Korea (Daejeon) Non-US=http://ftp.kr.debian.org/debian-non-US/ # KR Korea (Daejeon) Debian=http://gulus.usherbrooke.ca/debian/ # CA Canada (Université de Sherbrooke, Québec) Non-US=http://gulus.usherbrooke.ca/debian-non-US/ # CA Canada (Université de Sherbrooke, Québec) Debian=http://ftp.lug.ro/debian/ # RO Romania (Bucharest) Non-US=http://ftp.lug.ro/debian-non-US/ # RO Romania (Bucharest) Debian=ftp://ftp.corbina.ru/pub/Linux/debian/ # RU Russia (Moscow) Debian=http://ftp.sayclub.com/pub/debian/ # KR Korea (KIDC) Non-US=http://ftp.sayclub.com/pub/debian-non-us/ # KR Korea (KIDC) Debian=http://debian.indika.net.id/debian/ # ID Indonesia ( Data Center, Cyber Building, Jakarta) Debian=http://debian.samara.ru/debian/ # RU Russia (Samara) Debian=ftp://ftp.pucpr.br/debian/ # BR Brazil (Curitiba) Non-US=ftp://ftp.pucpr.br/debian-non-US/ # BR Brazil (Curitiba) Debian=http://debian.mines.inpl-nancy.fr/debian/ # FR France (Nancy) Non-US=http://debian.mines.inpl-nancy.fr/debian-non-US/ # FR France (Nancy) Debian=http://ftp.uni-bayreuth.de/linux/Debian/debian/ # DE Germany (Bayreuth) Non-US=http://ftp.uni-bayreuth.de/linux/Debian/debian-non-US/ # DE Germany (Bayreuth) Debian=http://ftp.ds.hj.se/pub/Linux/distributions/debian/ # SE Sweden (Jönköping) Debian=http://debian.logiclinux.com/debian/ # AR Argentina (CF) Non-US=http://debian.logiclinux.com/debian-non-US/ # AR Argentina (CF) Debian=ftp://ftp.cica.es/debian/ # ES Spain (Sevilla) Non-US=ftp://ftp.cica.es/debian-non-US/ # ES Spain (Sevilla) Debian=ftp://ftp.uni-sofia.bg/debian/ # BG Bulgaria (Sofia) Non-US=ftp://ftp.uni-sofia.bg/debian-non-US/ # BG Bulgaria (Sofia) Debian=http://debian.nctu.edu.tw/debian/ # TW Taiwan (HsinChu) Non-US=http://debian.nctu.edu.tw/debian-non-US/ # TW Taiwan (HsinChu) Debian=ftp://ftp.3logic.net/debian/ # UA Ukraine (Kherson) Non-US=ftp://ftp.3logic.net/debian-non-US/ # UA Ukraine (Kherson) Debian=http://debian.im.nuk.edu.tw/pub/debian/debian/ # TW Taiwan (Nan-Tzu, Kaohsiung) Non-US=http://debian.im.nuk.edu.tw/pub/debian/debian-non-US/ # TW Taiwan (Nan-Tzu, Kaohsiung) Debian=http://debian.cn99.com/debian/ # CN China (Changzhou) Non-US=http://debian.cn99.com/debian-non-US/ # CN China (Changzhou) Debian=http://ftp.coe.psu.ac.th/debian/ # TH Thailand (Computer Engineering Dept.) Non-US=http://ftp.coe.psu.ac.th/debian-non-US/ # TH Thailand (Computer Engineering Dept.) Debian=http://mirror.cpsc.ucalgary.ca/debian/ # CA Canada (Calgary, Alberta) Non-US=http://mirror.cpsc.ucalgary.ca/debian-non-US/ # CA Canada (Calgary, Alberta) Debian=ftp://ftp.informatik.hu-berlin.de/pub/Mirrors/ftp.de.debian.org/debian/ # DE Germany (Berlin) Non-US=ftp://ftp.informatik.hu-berlin.de/pub/Mirrors/ftp.de.debian.org/debian-non-US/ # DE Germany (Berlin) Debian=ftp://sb.itc.u-tokyo.ac.jp/DEBIAN/debian/ # JP Japan (Tokyo) Non-US=ftp://sb.itc.u-tokyo.ac.jp/DEBIAN/debian-non-US/ # JP Japan (Tokyo) Debian=http://ftp.riken.go.jp/pub/Linux/debian/debian/ # JP Japan (Saitama) Debian=http://debian.vinita.lt/debian/ # LT Lithuania (Vilnius) Non-US=http://debian.vinita.lt/debian-non-US/ # LT Lithuania (Vilnius) Debian=http://debian.wow-vision.com.sg/debian/ # SG Singapore Non-US=http://debian.wow-vision.com.sg/debian-non-US/ # SG Singapore Debian=http://debian.shimpinomori.net/debian/ # JP Japan (Tokyo) Non-US=http://debian.shimpinomori.net/debian-non-US/ # JP Japan (Tokyo) Debian=http://mirror.peer1.net/debian/ # CA Canada Non-US=http://mirror.peer1.net/debian-non-US/ # CA Canada Debian=ftp://ftp.debian.ikoula.com/debian/ # FR France (Ikoula http://www.ikoula.com/) Non-US=ftp://ftp.debian.ikoula.com/debian-non-US/ # FR France (Ikoula http://www.ikoula.com/) Debian=http://ring.hosei.ac.jp/archives/linux/debian/debian/ # JP Japan (Tokyo) Non-US=http://ring.hosei.ac.jp/archives/linux/debian/debian-non-US/ # JP Japan (Tokyo) Debian=http://debian.internet.gr/debian/ # GR Greece (Agios Stefanos, Athens) Non-US=http://debian.internet.gr/debian-non-US/ # GR Greece (Agios Stefanos, Athens) Debian=http://fatboy.umng.edu.co/debian/ # CO Colombia (Bogota DC) Non-US=http://fatboy.umng.edu.co/debian/ # CO Colombia (Bogota DC) Debian=ftp://ftp.scarlet.be/pub/debian/ # BE Belgium (Vilvoorde) Non-US=ftp://ftp.scarlet.be/pub/debian-non-US/ # BE Belgium (Vilvoorde) Debian=http://mirrors.chipset.or.id/debian/ # ID Indonesia Debian=http://debian.savoirfairelinux.net/debian/ # CA Canada (Montreal) Non-US=http://debian.savoirfairelinux.net/debian-non-US/ # CA Canada (Montreal) Debian=ftp://ftp.man.szczecin.pl/pub/Linux/debian/ # PL Poland (Szczecin) Non-US=ftp://ftp.man.szczecin.pl/pub/Linux/debian-non-US/ # PL Poland (Szczecin) Debian=http://www.las.ic.unicamp.br/pub/debian/ # BR Brazil (Campinas - SP) Debian=http://ftp.gwdg.de/pub/linux/debian/debian/ # DE Germany (Göttingen) Non-US=http://ftp.gwdg.de/pub/linux/debian/debian-non-US/ # DE Germany (Göttingen) Debian=http://casa.callbright.com/debian/ # MA Morocco (Casablanca) Non-US=http://casa.callbright.com/debian-non-US/ # MA Morocco (Casablanca) Debian=http://ftp.vthd-net.com/debian/ # FR France (Rocquencourt) Non-US=http://ftp.vthd-net.com/pub/linux/debian-non-US/ # FR France (Rocquencourt) Debian=ftp://ftp.tomstroubleshooting.com/debian/ # US United States (Orlando, Florida) Debian=http://www.ring.gr.jp/archives/linux/debian/debian/ # JP Japan Non-US=http://www.ring.gr.jp/archives/linux/debian/debian-non-US/ # JP Japan Debian=http://debian.mirror.cygnal.ca/debian/ # CA Canada (Burlington, Ontario) Non-US=http://debian.mirror.cygnal.ca/debian-non-US/ # CA Canada (Burlington, Ontario) Debian=http://komo.vlsm.org/debian/ # ID Indonesia (Gedung Cyber, Jakarta) Non-US=http://komo.vlsm.org/debian-non-US/ # ID Indonesia (Gedung Cyber, Jakarta) Debian=http://debian.mirrors.tds.net/debian/ # US United States (Wisconsin) Debian=http://debian.ethz.ch/debian/ # CH Switzerland (Zurich) Non-US=http://debian.ethz.ch/debian-non-US/ # CH Switzerland (Zurich) Debian=http://ftp.de.debian.org/debian-amd64/debian/ # DE Germany (Dresden) Debian=http://bytekeeper.as28747.net/debian-amd64/debian/ # BE Belgium Debian=http://bach.hpc2n.umu.se/debian-amd64/debian/ # SE Sweden Debian=http://hanzubon.jp/debian-amd64/debian/ # JP Japan Debian=ftp://mirror.switch.ch/mirror/debian-amd64/debian/ # CH Switzerland Debian=http://mirror.espri.arizona.edu/debian-amd64/debian/ # US USA, Arizona Debian=http://ftp.nl.debian.org/debian-amd64/debian/ # NL Netherlands (Enschede) Debian=ftp://ftp2.caliu.info/debian-amd64/debian/ # ES Spain (Barcelona) Debian=http://debian.csail.mit.edu/debian-amd64/debian/ # US USA, Massachusetts Debian=http://planetmirror.com/pub/debian-amd64/debian/ # AU Australia Debian=http://ftp.ru.debian.org/debian-amd64/ # RU Russia (Chernogolovka, Moscow) Debian=http://ftp.it.debian.org/debian-amd64/ # IT Italy (Milan) Debian=http://ftp2.it.debian.org/debian-amd64/ # IT Italy (Bologna) Debian=http://ftp.lug.ro/debian-amd64/ # RO Romania (Bucharest) Debian=http://ftp.es.debian.org/debian-amd64/debian/ # ES Spain (Madrid) Debian=http://ftp.jp.debian.org/debian-amd64/debian/ # JP Japan (Nara) Debian=http://ftp.cl.debian.org/debian-amd64/debian/ # CL Chile (Santiago) Debian=http://mirror.hamakor.org.il/pub/mirrors/debian-amd64/ # IL Israel (Haifa, Actcom) Debian=http://ftp.belnet.be/debian-amd64/debian/ # BE Belgium (Brussels) Debian=ftp://debian.gala.net/debian-amd64/ # UA Ukraine jigdo-0.7.3/scripts/depend.awk0000644000175000017500000002002710107660236016063 0ustar richardrichard#! /usr/bin/env awk -f # __ _ # |_) /| Copyright (C) 2000 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2. See # the file COPYING for details. # Syntax: awk -f depend.awk srcdir subdir1 subdir2... - file1 file2... # Reads all the files and looks for #include directives. If any file # being included can be found in srcdir/subdir or ./subdir, it is also # recursively scanned. Finally, writes a list of dependencies for each # file to srcdir+"Makedeps" (can also append the list of dependencies # to Makefile and Makefile.in; see output = "..." below) # Supports my two-level or three-level #include hierarchy: # - in "fh" (forward decl) files, may only include other "fh" files # - in "hh" (normal hdr) files, may include "fh" and "hh" files # - in "ih" (inline defs) files, may include "fh", "hh" and "ih" files # Furthermore, depending on whether NOINLINE has been #define'd, # INLINE functions are either compiled inline or non-inline. The # latter is useful during development, to avoid excessive # recompilation after changes to "hh" headers. The idea is that the # implementations in "ih" will need many more other headers than the # decls in "hh". IMPORTANT: The generated dependencies assume that you # *have* defined NOINLINE during development. # Differences to "makedepend" behaviour: ignores ".ih" files unless # included from ".cc" files; never takes #ifdef and friends into # account; does not scan include dirs in specified order, but instead # scans all include dirs for each file; Untested behaviour if the same # source filename appears more than once in different source # directories. # Another extension: If a source file contains "#makefile" at the # start of the line (whitespace allowed), everything after the # space/tab following this string is written to the Makefile verbatim. # Furthermore, if the source file contains "#test-deps" followed by a # (possibly empty) list of object files, an appropriate Makefile entry # is added to build the unit test's executable. A "#test-ldflags" line # can specify flags to pass to the linker, e.g. "$(GTKFLAGS)" #______________________________________________________________________ # recursively find dependencies for specified file. function makeDeps(dir, file) { finalDeps = makeDeps2(dir, file); return substr(finalDeps, 2, length(finalDeps) - 2); } function makeDeps2(dir, file, l_deps, l_dir, l_recurseFile, l_line, l_exists, l_lineNr, l_fileType, l_includedType, l_depsLine, l_depsLineNr, l_depsLdflags) { if (deps[file] == "-") return ""; # avoid infinite recursion else if (deps[file] != "") return deps[file]; # already know about that deps[file] = "-"; l_deps = " "; l_lineNr = 0; l_fileType = substr(file, length(file) - 1); # e.g. "cc" or ".h" l_depsLine = ""; l_depsLdflags = ""; while ((getline l_line < (dir file)) == 1) { # read each line of file exists[file]; # have read at least 1 line, so the file is there ++l_lineNr; if (l_line ~ /^[ \t]*\#[ \t]*makefile[ \t]/) { sub(/^[ \t]*\#[ \t]*makefile[ \t]/, "", l_line); inlinedMakefile = inlinedMakefile "# " file ":" l_lineNr "\n" \ l_line "\n"; continue; } if (l_line ~ /^[ \t]*\#[ \t]*test-deps([ \t]|$)/) { sub(/^[ \t]*\#[ \t]*test-deps[ \t]?/, "", l_line); l_depsLine = l_depsLine " " l_line; l_depsLineNr = l_lineNr; continue; } if (l_line ~ /^[ \t]*\#[ \t]*test-ldflags([ \t]|$)/) { sub(/^[ \t]*\#[ \t]*test-ldflags[ \t]?/, "", l_line); l_depsLdflags = l_depsLdflags " " l_line; continue; } if (l_line !~ /^[ \t]*\#[ \t]*include[ \t]+["<][a-zA-Z0-9.-]+[">]/) continue; # found #include line match(l_line, /["<][a-zA-Z0-9.-]+[">]/); l_includedType = substr(l_line, RSTART + RLENGTH - 3, 2); l_exists = 0; # skip for loop to ignore .ih files except when included from .cc if (l_fileType == "cc" || l_includedType != "ih") { for (l_dir in includeDir) { # try each in include path l_recurseFile = substr(l_line, RSTART + 1, RLENGTH - 2); split(makeDeps2(dir, l_dir l_recurseFile), newDeps); # recurse # eliminate duplicates by only adding new files to l_deps for (i in newDeps) { if (index(l_deps, " " newDeps[i] " ") == 0) l_deps = l_deps newDeps[i] " "; } newDep = includeDir[l_dir] l_recurseFile; if ((l_dir l_recurseFile) in exists) { l_exists = 1; if (index(l_deps, " " newDep " ") == 0) l_deps = l_deps newDep " "; } } } # maybe complain about wrong includes if (l_line !~ /\/\* *NOINLINE/ && l_exists \ && type[l_fileType] < type[l_includedType]) printf("%s:%d: #including file `%s' violates policy\n", file, l_lineNr, l_recurseFile); } close((dir file)); if (l_depsLine != "") { base = file; sub(/^(.\/)*/, "", base); sub(/\.(c|cc|cpp|C)$/, "",base); l_depsLine = base".o $(TEST-DEFAULTOBJS)" l_depsLine; inlinedMakefile = inlinedMakefile "# " file ":" l_depsLineNr "\n" \ base "$(EXE): " l_depsLine "\n" \ "\t$(LD) -o "base"$(EXE) " l_depsLine " $(TEST-LDFLAGS)" \ l_depsLdflags "\n"; } deps[file] = l_deps; return l_deps; } #______________________________________________________________________ # Read file until separator line found, set depContent to rest function readSepFile(file, l_line, l_ret) { l_ret = ""; while ((getline l_line < file) == 1) { l_ret = l_ret l_line "\n"; if (l_line == "# DO NOT DELETE THIS LINE -- make depend depends on it."){ depContent = ""; while ((getline l_line < file) == 1) depContent = depContent l_line "\n"; close(file); return l_ret; } } print "depend.awk: No separator line found in `" file "'!?"; exit(1); } # Read entire file contents into depContent function readFile(file, l_RS) { l_RS = RS; RS = "\x7f"; getline depContent < file; close(file); RS = l_RS; return ""; } #______________________________________________________________________ BEGIN { # Output filename. Special case: output="" means: Append # dependencies to Makefile and Makefile.in output = "Makedeps"; # lower number => may include all with higher number type["cc"] = type[".c"] = 4; type["ih"] = 3; type["hh"] = type[".h"] = 2; type["fh"] = 1; arg = 0; srcDir = ARGV[++arg]; # first arg is directory containing sources if (substr(srcDir, length(srcDir)) != "/") srcDir = srcDir "/"; if (substr(srcDir, 1, 2) == "./") srcDir = substr(srcDir, 3); includeDir[srcDir] = ""; # look in source dir includeDir[""] = ""; # also look in current dir and its subdirs while (ARGV[++arg] != "-") { # read subdirectories until "-" includeDir[srcDir ARGV[arg] "/"] = ARGV[arg] "/"; includeDir[ARGV[arg] "/"] = ARGV[arg] "/"; } if (output) { md = srcDir output; readFile(md); newDepContent = ""; } else { # Makefile/Makefile.in mf1 = srcDir "Makefile.in"; mf2 = "Makefile"; makeFileInContent = readSepFile(mf1); newDepContent = "\n"; } # build dependencies inlinedMakefile = ""; while (++arg < ARGC) { f = ARGV[arg]; sub(/^\.\//, "", f); base = f; sub(/\.(c|cc|cpp|C)$/, "", base); target = base ".o"; newDepContent = newDepContent \ sprintf("%s: %s %s\n", target, f, makeDeps(srcDir, f)); } newDepContent = inlinedMakefile "\n" newDepContent; # write files if (depContent == newDepContent) { print "No dependency changes"; } else { if (output) { system("mv -f " md " " md ".bak"); printf("%s", newDepContent) > md; print "Updated `" md "'"; } else { # Makefile/Makefile.in makeFileContent = readSepFile(mf2); system("mv -f " mf1 " " mf1 ".bak"); printf("%s%s", makeFileInContent, newDepContent) > mf1; printf("%s%s", makeFileContent, newDepContent) > mf2; print "Updated `" srcDir "Makefile.in' and `Makefile'"; } } } jigdo-0.7.3/scripts/glade-filter.awk0000644000175000017500000001136010261546437017172 0ustar richardrichard#! /usr/bin/env awk -f # __ _ # |_) /| Copyright (C) 2001-2003 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2. See # the file COPYING for details. # Syntax: glade-filter some/path/gtk-interface # where some/path/gtk-interface.cc.tmp was generated by glade. Will # post-process that file to generate a simple class that contains # pointers to all the widgets, and the create_ functions, so you don't # need to look up widgets by name. # Overwrites some/path/gtk-interface.cc and some/path/gtk-interface.hh #______________________________________________________________________ # Add code line to hh, with right indentation function addhh(string) { sub(/^ +/, "", string); if (substr(string, 1, 1) == "}") hhindent = substr(hhindent, 3); string = hhindent string; sub(/ +$/, "", string); hh = hh string "\n"; if (substr(string, length(string)) == "{") hhindent = hhindent " "; } function addcc(string) { cc = cc string "\n"; } #______________________________________________________________________ BEGIN { if (ARGC != 2) { print "Syntax: glade-filter.awk some/path/gtk-interface"; exit 1; } stem = ARGV[1]; leaf = stem; gsub(/.*\//, "", leaf); outputcc = stem".cc"; outputhh = stem".hh"; hhGuard = toupper(outputhh); gsub(/.*\//, "", hhGuard); gsub(/[^A-Z0-9]/, "_", hhGuard); namespace = "GUI"; cc = "// Automatically created from `"leaf".cc.tmp' by glade-filter.awk\n\n"; addcc("#include "); hh = cc; #addcc("#include <"outputhh">"); addhh("#ifndef "hhGuard); addhh("#define "hhGuard); addhh(""); addhh("#include "); addhh("#include "); addhh(""); addhh("namespace "namespace" {"); collectDecl = 0; ARGV[1] = stem".cc.tmp"; } #______________________________________________________________________ /^#include / { sub(/ "/, " <"); sub(/"$/, ">"); sub(/\.cc?\.tmp/, ".cc"); sub(/\.hh?\.tmp/, ".hh"); } /^# *include $/ { afterConfigInclude = 1; } /^$/ && afterConfigInclude { # RA 2004-05-29: glade-2 output uses deprecated stuff addcc("#ifdef GTK_DISABLE_DEPRECATED\n# undef GTK_DISABLE_DEPRECATED\n#endif"); afterConfigInclude = 0; } #/@VERSION@/ { # gsub(/@VERSION@/, version); #} # Fixup #defines for GLADE_HOOKUP_OBJECT[_NO_REF] *not* to set the # name string - I never use lookup_widget(), so no need to waste space # for all the strings /g_object_set_data_full \(G_OBJECT \(component\), name,|g_object_set_data \(G_OBJECT \(component\), name, widget\)/ { sub(/, name,/, ", \"\","); } /^create_[a-zA-Z]+ *\( *(void *)?\) *$/ { # New function definition => new GUI class guiElem = toupper(substr($0, 8, 1)) substr($0, 9, index($0, " ") - 9); addcc(namespace"::"guiElem"::create()"); addhh(""); addhh("struct "guiElem" {"); addhh(prevLine" create();"); collectDecl = 1; next; } /^ *} *$/ { if (length(guiElem) != 0) { # End of function definition addhh("};"); guiElem = ""; } } # Weed out decls for vars whose names are likely to have been # generated by glade: All lowercase, ends with >=1 digits. These # remain local vars of the create() function, rather than becoming # members of the struct /^ *[A-Z][a-zA-Z0-9_]*( +| *\* *)[a-z]+[0-9]+ *(= *[a-zA-Z0-9_]+ *)?; *$/ { if (collectDecl) { # leave in cc, don't add to hh addcc($0); next; } } # "GtkWidget *jigdo_abortButton;" or "GSList *radiobutton1_group = NULL;" /^ *[A-Z][a-zA-Z0-9_]*( +| *\* *)[a-z][a-zA-Z0-9_]+ *(= *[a-zA-Z0-9_]+ *)?; *$/ { if (collectDecl) { # GtkSomething declaration; - remove from cc, add to hh inside # class. If var contains initializer, only put that in cc if (index($0, "=")) { l = $0; sub(/^ *[A-Z][a-zA-Z0-9_]*( +| *\* *)/, "", l); addcc(l); sub(/=[^;]*/, "", $0); } addhh($0); next; } } /(^ *$)|=/ { collectDecl = 0; } { cc = cc $0 "\n"; # By default, copy over code into output file prevLine = $0; } #______________________________________________________________________ END { if (!outputcc) exit 1; addhh(""); addhh("} // namespace "namespace); addhh(""); addhh("#endif /* "hhGuard" */"); printf("%s", cc) > outputcc; printf("%s", hh) > outputhh; # To avoid recompilation, only update .cc if contents changed #old = ""; #while ((getline line < outputcc) == 1) old = old line "\n"; #if (old == cc) print "`"outputcc"' is unchanged"; #else printf("%s", cc) > outputcc; # To avoid recompilation, only update .hh if contents changed #old = ""; #while ((getline line < outputhh) == 1) old = old line "\n"; #if (old == hh) print "`"outputhh"' is unchanged"; #else printf("%s", hh) > outputhh; } jigdo-0.7.3/scripts/html-beautify.awk0000644000175000017500000001164107731716572017416 0ustar richardrichard#! /usr/bin/env awk -f # __ _ # |_) /| Copyright (C) 2000 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2. See # the file COPYING for details. function appendWord(word, spaceAfterWord) { #print "appendWord \"" word "\" \"" gensub(/\n/, "\\\\n", "g", spaceAfterWord) "\""; if (prevSpaceAfterWord == "\n") { # Linebreak while inside
    doc = doc substr(indentStr, 1, ind) docLine "\n";
    docLine = word;
    ind = 0;
    prevSpaceAfterWord = spaceAfterWord;
    return;
  }

  if (ind + length(docLine) + length(word) < curMaxLen) {
    # Append
    if (word != "" || doPreserve > 0)
      docLine = docLine prevSpaceAfterWord word;
    prevSpaceAfterWord = spaceAfterWord;
  } else {
    # New line
    if (docLine != "") doc = doc substr(indentStr, 1, ind) docLine "\n";
    #print ">>> " docLine;
    docLine = word;
    ind = nextInd;
    prevSpaceAfterWord = spaceAfterWord;
  }
}
#______________________________________________________________________

BEGIN {
  get = ARGV[1];
  put = ARGV[2];
  maxLen = 75;
  indent = 1;
  indentStr = "                    "; # Won't indent by more than this
  killClass = 1; # If nonzero, remove all " class=...>" attributes

  # Only tags that come with closing tags are allowed!
  tags["html"]=1; tags["body"]=1; tags["head"]; tags["title"];
  tags["div"]; tags["h1"]; tags["h2"]; tags["h3"]; tags["h4"]; tags["h5"];
  tags["h6"]; tags["p"]; tags["dl"]; tags["dt"]; tags["dd"]; tags["table"];
  tags["tr"]; tags["td"];
  preserve["pre"];

  maxTagLength = 100;
  curMaxLen = maxLen;
  # Join lines
  getline rest < get;
  while ((getline line < get) == 1)
    rest = rest line "\n";

  if (killClass)
    gsub(/[ \t\n]+(class|CLASS)=("[^"]*"|'[^']*')[ \t\n]*>/, ">", rest); #"

  # Split lines at whitespace and some tags
  nextInd = ind = 0; # Nr of characters of indentation
  doc = ""; # Ouput document
  docLine = ""; # Current line to append words to
  doPreserve = 0; # Nesting level of 
  while (match(rest, /([ \n\t]+|< *(\/ *)?)/)) {
    #print "MATCH \"" substr(rest, RSTART, RLENGTH) "\"";
    #print "xxx "nextInd" " gensub(/\n/, "\\\\n", "g", substr(rest, 1, 90));

    if (substr(rest, RSTART, 1) == "<") {
      # Tag found
      tagName = tolower(substr(rest, RSTART + RLENGTH, maxTagLength));
      gsub(/[^a-z0-9].*$/, "", tagName);
      closing = index(substr(rest, RSTART + 1, RLENGTH - 1), "/");

      # Is tag 
?
      if (tagName in preserve) {
        if (closing && doPreserve > 0) {
          appendWord(substr(rest, 1, RSTART + RLENGTH + length(tagName) \
                            - 1), "");
          nextInd -= indent;
          --doPreserve;
          if (doPreserve == 0) {
            curMaxLen = maxLen; nextInd = nonpreserveInd;
          }
          rest = substr(rest, RSTART + RLENGTH + length(tagName));
          continue;
        }
        if (!closing) {
          # Disable indentation while inside 
          if (doPreserve == 0) { nonpreserveInd = nextInd; nextInd = 0; }
          ++doPreserve; curMaxLen = 9999999;
        }
      }

      if (!(tagName in tags)) {
        # No known tag name
        appendWord(substr(rest, 1, RSTART + RLENGTH + length(tagName) - 1),
                   "");
        rest = substr(rest, RSTART + RLENGTH + length(tagName));
        continue;
      } else if (closing) {
        #print "---/" tagName;
        # Closing tag
        if (tags[tagName] == 0) {
          appendWord(substr(rest, 1, RSTART + RLENGTH + length(tagName) \
                            - 1), "");
          nextInd -= indent;
        } else {
          nextInd -= indent;
          appendWord(substr(rest, 1, RSTART - 1), "");
          curMaxLen = 0; # Force new line with next appendWord()
          appendWord(substr(rest, RSTART, RLENGTH + length(tagName)), "");
          curMaxLen = maxLen;
        }
      } else {
        #print "--- " tagName;
        # Opening tag
        appendWord(substr(rest, 1, RSTART - 1), "");
        curMaxLen = 0; # Force new line with next appendWord()
        appendWord(substr(rest, RSTART, RLENGTH + length(tagName)), "");
        curMaxLen = maxLen;
        nextInd += indent;
      }
      rest = substr(rest, RSTART + RLENGTH + length(tagName));
      continue;
    } # endif tag found

    # Whitespace
    #print "dop " doPreserve ", RSTART=" RSTART ", RLENGTH=" RLENGTH;
    if (doPreserve) {
      # Preserve spaces and newlines in output
      appendWord(substr(rest, 1, RSTART - 1), substr(rest, RSTART, 1));
      rest = substr(rest, RSTART + 1);
    } else {
      # Wrap words
      if (substr(rest, RSTART + RLENGTH, 1) == ">")
        appendWord(substr(rest, 1, RSTART - 1), "");
      else
        appendWord(substr(rest, 1, RSTART - 1), " ");
      rest = substr(rest, RSTART + RLENGTH);
    }

  }
  doc = doc substr(indentStr, 1, ind) docLine rest;

  print doc;

}
jigdo-0.7.3/scripts/jigdo-lite0000755000175000017500000006502710262760530016106 0ustar  richardrichard#! /bin/sh
# Poor man's jigdo - download and assemble Jigsaw Download files
# Copyright 2001-2005 Richard Atterer
# Portability improvements by J.A. Bezemer, Jan 2002
# License: GPL version 2

# These 4 variables can be overridden in ~/.jigdo-lite if necessary:
jigdoOpts="--cache jigdo-file-cache.db"
wgetOpts="--passive-ftp --dot-style=mega --continue --timeout=30"
mirrors="mirrors.jigdo"
tmpDir="."

filesPerFetch=10
maxMissing=30 # Don't try fallback servers if x% or more of files missing
rcFile="$HOME/.jigdo-lite"

if test "x$OSTYPE" = "xmsys"; then
  windows=true
  OSTYPE=Windows
  mirrors="jigdo-bin/mirrors.jigdo"
  rcFile="jigdo-lite-settings.txt"
  egrep() { grep -E "$@"; }
  expr() { echo "$(($@))"; }
  filesPerFetch=5 # What's the command line length limit?
  nl='\r\n'
else
  windows=false
  nl='\n'
fi
#______________________________________________________________________

# read with readline, only if running bash >=2.03 (-e gives error on POSIX)
readLine="read"
if test "x$BASH_VERSION" != "x"; then
  if test "x${BASH_VERSION#[2-9].}" != "x$BASH_VERSION"; then
    if test "x${BASH_VERSION#2.0[012]}" = "x$BASH_VERSION"; then
      readLine="read -e -r"
    fi
  fi
else
  # Non-bash: Check whether "read -r" supported
  if (echo | read -r REPLY 2>/dev/null); then
    readLine="read -r"
  fi
fi
#______________________________________________________________________

# isURI 
# Returns 0 (true) if the supplied string is a HTTP/FTP URL, otherwise 1
isURI() {
  case "$1" in
    http:*|ftp:*|HTTP:*|FTP:*|file:*|FILE:*) return 0;;
    *) return 1;
  esac
}
#______________________________________________________________________

strEqual() { test "x$1" = "x$2"; }
strNotEqual() { test "x$1" != "x$2"; }
strEmpty() { test "x$1" = "x"; }
strNotEmpty() { test "x$1" != "x"; }
#______________________________________________________________________

# fetch ...
# Download a file, storing it in the current dir
fetch() {
  if test "$#" -eq 0; then return 0; fi
  wget --user-agent="$userAgent" $wgetOpts "$@" || return 1
}
#______________________________________________________________________

# Given URLs, fetch them into $imageTmp, then merge them into image
fetchAndMerge() {
  if test "$#" -eq 0; then return 0; fi
  fetch --force-directories --directory-prefix="$imageTmp" -- "$@"
  # Merge into the image
  $jigdoFile $jigdoOpts --no-cache make-image --image="$image" \
    --jigdo="$jigdoF" --template="$template" "$imageTmp"
  jigdoErr="$?"
  if test "$jigdoErr" -ge 3; then
    echo "jigdo-file failed with code $jigdoErr - aborting."
    $error 1
  fi
  # Delete imageTmp, to avoid taking up more space than necessary
  rm -rf "$imageTmp"
  return 0
}
#______________________________________________________________________

# Prompt user to input value, assign result to $REPLY. If user just
# presses Return, assign supplied default value instead.
# input  
input() {
  prompt=""
  REPLY=""
  if strNotEmpty "$2"; then prompt=" [$2]"; fi
  printf "%s%s: " "$1" "$prompt"
  if $nonInteractive; then echo; else $readLine REPLY; fi
  if strEmpty "$REPLY"; then REPLY="$2"; fi
}
#______________________________________________________________________

# Read from $jigdoF and create a menu of images contained in the file.
# If invoked just as "imageMenu", print out the menu. If invoked as
# "imageMenu 5", set $image and $templateURI to the filename/URI of
# the 5th menu entry.
# The scan for [Image] sections stops when at least one such section
# has been read and a non-[Image] section follows. This is not
# correct, but speeds things up a lot because in many cases the large
# [Parts] section does not have to be scanned.
imageMenu() {
  imageSel="$1"
  curImageCount=0
  section=""
  exec 3<"$jigdoF"
  l=""
  while true; do
    case "$l" in
      "[Image]"*)
        # Read image section contents
        unset image templateURI templateMD5 shortInfo info
        while $readLine l <&3; do
          case "$l" in
            "["*"]"*) break;;
            Filename=*) image="`echo $l | sed -e 's/^Filename= *//; s%[['\\''\"$\\\`|&/]%%g'`";;
            Template=*) templateURI="`echo $l | sed -e 's/^Template= *//; s%[['\\''\"$\\\`|&]%%g'`";;
            Template-MD5Sum=*) templateMD5="`echo $l | sed -e 's/^Template-MD5Sum= *//; s%[['\\''\"$\\\`|&/]%%g'`";;
            Template-MD5Sum=*) templateMD5="`echo $l | sed -e 's/^Template-MD5Sum= *//; s%[^a-zA-Z0-9_-]%%g'`";;
            ShortInfo=*) shortInfo="`echo $l | sed -e 's/^ShortInfo= *//; s%[[$\\\`|]%%g'`";;
            Info=*) info="`echo $l | sed -e 's/^Info= *//; s%[['\\''\"$\\\`|]%%g'`";;
          esac
        done
        # Image section read, check for validity
        if strNotEmpty "$image" && strNotEmpty "$templateURI"; then
          curImageCount="`expr $curImageCount + 1`"
          if strNotEmpty "$imageSel"; then
            # Return info for image selected via $imageSel
            if test "$imageSel" -eq "$curImageCount"; then exec 3<&-; return 0; fi
          else
            # Print image menu
            if strEmpty "$shortInfo"; then
              printf "%3d: %s\n" "$curImageCount" "$image"
            else
              printf "%3d: %s (%s)\n" "$curImageCount" "$shortInfo" "$image"
            fi
          fi
        fi
        case "$l" in "[Image]"*) continue;; esac
        # Abort early, avoid reading [Parts]
        imageCount="$curImageCount"; exec 3<&-; return 0;;
      *)
        # Skip other parts of the file
        while $readLine l <&3; do
            case "$l" in "["*"]"*) break;; esac
        done
        case "$l" in "["*"]"*) continue;; esac
        imageCount="$curImageCount"; exec 3<&-; return 0;;
    esac
  done
  imageCount="$curImageCount"
  exec 3<&-
  return 0
}
#______________________________________________________________________

# Output a horizontal rule
hrule() {
  echo
  echo "-----------------------------------------------------------------"
}
#______________________________________________________________________

# Download template, unless already present in current dir
fetchTemplate() {
  if $fetchedTemplate; then return 0; fi
  echo

  template=`basename "$templateURI"`

  if strEmpty "$templateMD5"; then
    echo "[WARNING - \`Template-MD5Sum' missing from image section]"
    echo
  fi
  if test -r "$template" && strNotEmpty "$templateMD5"; then
    set -- `$jigdoFile md5sum --report=quiet "$template"`
    if test "$1" = "$templateMD5"; then
      echo "Not downloading .template file - \`$template' already present"
      fetchedTemplate=true
      return 0
    fi
#  elif test -r "$template"; then
#    echo "Not downloading .template file - \`$template' already present"
#    fetchedTemplate=true
#    return 0
  fi
  if isURI "$templateURI"; then
    # Absolute template URL
    echo 'Downloading .template file'
#    rm -f "$template"
    fetch --continue -- "$templateURI"
  elif isURI "$url"; then
    # Template URI is relative to absolute jigdo URL
    echo 'Downloading .template file'
#    rm -f "$template"
    fetch --continue -- `echo "$url" | sed 's%[^/]*$%%'`"$templateURI"
  else
    # Template URI is relative to local jigdo filename
    if $windows; then
      # This is a bit broken - we ought to replace / with \ in templateURI too
      template=`echo "$url" | sed 's%[^\]*$%%'`"$templateURI"
    else
      template=`echo "$url" | sed 's%[^/]*$%%'`"$templateURI"
    fi
  fi
  fetchedTemplate=true
  # Does template exist now?
  if test ! -r "$template"; then
    echo "File \`$template' does not exist!"
    $error 1
  fi
  if strEmpty "$templateMD5"; then return 0; fi
  set -- `$jigdoFile md5sum --report=quiet "$template"`
  if strEqual "$1" "$templateMD5"; then return 0; fi
  echo "Error - template checksum mismatch!"
  echo "The .template file does not belong to the .jigdo file - the"
  echo "chances are high that the image generation process will break."
  echo "I will abort now. If you know better than me and want this error"
  echo "to be ignored, enter the string \"42\" to proceed."
  echo
  echo "Note that you might get this error if you resumed the download of a"
  echo ".template file, and that .template file has changed on the server in"
  echo "the meantime. In this case, you may be able to fix the problem by"
  echo "deleting the .template file and restarting jigdo-lite."
  input ""
  case $REPLY in *42*) return 0;; esac
  $error 1
}
#______________________________________________________________________

# Write $rcFile
saveOptions() {
  printf "jigdo='%s'${nl}debianMirror='%s'${nl}nonusMirror='%s'${nl}" \
    "$jigdo" "$debianMirror" "$nonusMirror" >"$rcFile"
  printf "tmpDir='%s'${nl}jigdoOpts='%s'${nl}" \
    "$tmpDir" "$jigdoOpts" >>"$rcFile"
  printf "wgetOpts='%s'${nl}scanMenu='%s'${nl}" \
    "$wgetOpts" "$scanMenu" >>"$rcFile"
}
#______________________________________________________________________

finished() {
  hrule
echo "Finished!"
if $batch; then true; else
echo "The fact that you got this far is a strong indication that \`$image'"
echo "was generated correctly. I will perform an additional, final check,"
echo "which you can interrupt safely with Ctrl-C if you do not want to wait."
fi
echo
  $jigdoFile verify --image="$image" --jigdo="$jigdoF" --template="$template" \
    $jigdoOpts
  return 0
}
#______________________________________________________________________

# $0 is an URL or filename. Download/process it.
selectImage() {
  url="$1"

  # Arg can be either URL or filename. Maybe download file
  if isURI "$url"; then
    jigdoF=`basename "$url"`
    echo
    if test -r "$jigdoF"; then
      echo "Not downloading .jigdo file - \`$jigdoF' already present"
    else
      echo "Downloading .jigdo file"
      fetch -- "$url"
    fi
  else
    jigdoF="$url"
  fi
  # Does jigdo exist now?
  if test ! -r "$jigdoF"; then
    echo "File \`$jigdoF' does not exist!"
    $error 1
  fi
  # Try to gunzip it. In case of error, assume that it wasn't gzipped
  if gzip -cd "$jigdoF" >"$jigdoF.unpacked" 2>/dev/null; then
    jigdoF="$jigdoF.unpacked"
  else
    rm -f "$jigdoF.unpacked"
  fi
  #________________________________________

  if $batch; then
    # Batch - download all images
    hrule
    echo "Images offered by \`$url':"
    imageMenu # print out menu, set $imageCount
    imageNr=1
    while test "$imageNr" -le "$imageCount"; do
      imageMenu "$imageNr" # set $image and $templateURI
      hrule
      if test "$imageCount" -eq 1; then
        echo "Batch mode: Will download \`$image'"
      else
        echo "Batch mode: Will download \`$image' (image $imageNr out of $imageCount)"
      fi
      imageDownload
      imageNr="`expr $imageNr + 1`"
      askQuestions=false
    done

  else

    # Interactive - ask
    while true; do
      hrule
      echo "Images offered by \`$url':"
      imageMenu # print out menu, set imageCount
      if test "$imageCount" -eq 1; then
        imageMenu "1" # set $image and $templateURI
        imageDownload
        break # Only 1 image - don't loop asking for images to download
      else
        input "Number of image to download" ""
        if strEmpty "$REPLY"; then continue; fi
        if test "$REPLY" -ge 1 -a "$REPLY" -le "$imageCount"; then
          imageMenu "$REPLY" # set $image and $templateURI
          imageDownload || return 1
        fi
      fi
    done

  fi
  case "$jigdoF" in *.unpacked) rm -f "$jigdoF";; esac
}
#______________________________________________________________________

scanFiles() {
  # If --scan on command line, never ask, just scan that path
  if strNotEmpty "$opt_filesToScan"; then
    echo "Path to scan: $opt_filesToScan"
    if $batch; then return 0; fi
    # Retrieve template if necessary, then supply files
    fetchTemplate || return 1
    $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
      --template="$template" $jigdoOpts "$opt_filesToScan"
    jigdoErr="$?"
    if test "$jigdoErr" -eq 0 -a -r "$image"; then
      finished
      $error 0 # All files were present on local filesystem
    elif test "$jigdoErr" -ge 3; then
      echo "jigdo-file failed with code $jigdoErr - aborting."
      $error 1
    fi
    return 0
  fi

  # Ask user for any parts on local filesystems
  while true; do
    hrule
    echo "If you already have a previous version of the CD you are"
    echo "downloading, jigdo can re-use files on the old CD that are also"
    echo "present in the new image, and you do not need to download them"
    if $windows; then
    echo "again. Enter the path to the old CD ROM's contents (e.g. \`d:\\')."
    else
    echo "again. Mount the old CD ROM and enter the path it is mounted under"
    echo "(e.g. \`/mnt/cdrom')."
    fi
    echo "Alternatively, just press enter if you want to start downloading"
    echo "the remaining files."
    shift "$#" # Solaris /bin/sh doesn't understand "set --"
    set -- $scanMenu
    if strNotEmpty "$1"; then
      echo
      echo "You can also enter a single digit from the list below to"
      echo "select the respective entry for scanning:"
      echo "  1: $1"
      if strNotEmpty "$2"; then echo "  2: $2"; fi
      if strNotEmpty "$3"; then echo "  3: $3"; fi
      if strNotEmpty "$4"; then echo "  4: $4"; fi
      if strNotEmpty "$5"; then echo "  5: $5"; fi
    fi
    input "Files to scan"; filesToScan="$REPLY"
    if strEmpty "$filesToScan"; then return 0; fi
    # Do not add supplied string to menu if...
    case "$filesToScan" in
      *" "*|*"'"*|*'`'*|*'"'*|*'$'*) ;; # ...it has bad chars
      1) filesToScan="$1";;
      2) filesToScan="$2";;
      3) filesToScan="$3";;
      4) filesToScan="$4";;
      5) filesToScan="$5";;
      *) case " $1 $2 $3 $4 $5 " in
        *" $filesToScan "*) ;; # ...it is already in the menu
        *)  set -- "$filesToScan" $scanMenu
          scanMenu="$1 $2 $3 $4 $5"
          saveOptions;;
      esac;;
    esac
    if strEmpty "$filesToScan"; then continue; fi

    # In batch mode, postpone template download, scan later
    if $batch; then return 0; fi

    # Retrieve template if necessary, then supply files
    fetchTemplate || return 1
    $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
      --template="$template" $jigdoOpts "$filesToScan"
    jigdoErr="$?"
    if test "$jigdoErr" -eq 0 -a -r "$image"; then
      finished
      $error 0 # All files were present on local filesystem
    elif test "$jigdoErr" -ge 3; then
      echo "jigdo-file failed with code $jigdoErr - aborting."
      $error 1
    fi
  done
}
#______________________________________________________________________

selectServers() {
  if $askQuestions; then true; else return; fi

  # Crude check for whether any entry in the [Parts] section uses a
  # "Debian" or "Non-US" label. If yes, start Debian mirror selection
  # below.
  if $batch; then
    # If in batch mode, always ask the server questions; even though
    # the current .jigdo file might not use Debian servers, a later
    # one might, but we can't ask then.
    usesDebian=true
    usesNonus=true
  else
    usesDebian=false
    if egrep '^[^=]+= *["'\'']?Debian:' <"$jigdoF" >/dev/null; then
      usesDebian=true
    fi
    usesNonus=false
    if egrep '^[^=]+= *["'\'']?Non-US:' <"$jigdoF" >/dev/null; then
      usesNonus=true
    fi
  fi

  # Extra options to pass to jigdo-file for server selection
  uriOpts=""

  while $usesDebian; do
    hrule
    echo "The jigdo file refers to files stored on Debian mirrors. Please"
    echo "choose a Debian mirror as follows: Either enter a complete URL"
    echo "pointing to a mirror (in the form"
    echo "\`ftp://ftp.debian.org/debian/'), or enter any regular expression"
    echo "for searching through the list of mirrors: Try a two-letter"
    echo "country code such as \`de', or a country name like \`United"
    echo "States', or a server name like \`sunsite'."
    input "Debian mirror" "$debianMirror"
    # Special-case two-letter country codes
    case "$REPLY" in [a-z][a-z]) REPLY="[. ]$REPLY[/. ]";; esac
    if isURI "$REPLY"; then
      # Turn any "file:/opt/mirror" into "file:/opt/mirror/"
      debianMirror=`echo $REPLY | sed -e 's%^ *\([^ ]*[^/ ]\)/*\( .*\)*$%\1/%'`
      saveOptions
      uriOpts="--uri Debian='$debianMirror'"
      break;
    fi
    egrep -i "$REPLY" "$mirrors" | sed -n -e 's/^Debian=//p'
    echo
    echo "An up-to-date copy of the above list is available at"
    echo "ftp://ftp.debian.org/debian/README.mirrors.txt"
  done

  while $usesNonus; do
    hrule
    echo "The jigdo file also refers to the Non-US section of the Debian"
    echo "archive. Please repeat the mirror selection for Non-US. Do not"
    echo "simply copy the URL you entered above; this does not work because"
    echo "the path on the servers differs!"
    input "Debian non-US mirror" "$nonusMirror"
    case "$REPLY" in [a-z][a-z]) REPLY="[. ]$REPLY[/. ]";; esac
    if isURI "$REPLY"; then
      # Turn any "file:/opt/mirror" into "file:/opt/mirror/"
      nonusMirror=`echo $REPLY | sed -e 's%^ *\([^ ]*[^/ ]\)/*\( .*\)*$%\1/%'`
      saveOptions
      uriOpts="$uriOpts --uri Non-US='$nonusMirror'";
      break;
    fi
    egrep -i "$REPLY" "$mirrors" | sed -n -e 's/^Non-US=//p'
    echo
    echo "An up-to-date copy of the above list is available at"
    echo "ftp://ftp.debian.org/debian/README.non-US"
  done
}
#______________________________________________________________________

# $image: image filename
# $templateURI: Absolute/relative URI from .jigdo file
# $info: Info=... entry from .jigdo file
imageDownload() {

  fetchedTemplate=false

  if strNotEmpty "$info"; then
    printf "\nFurther information about \`%s':\n" "$image"
    echo "$info"
  fi
  list="$image.list"

  # Create name of temporary dir, by stripping extension from $image
  imageTmp="`echo $image | sed 's%\.\(tmp|iso|raw\)%%'`"
  if test -f "$imageTmp" -o "x$imageTmp" = "x$image"; then
    imageTmp="$imageTmp.tmpdir"
  fi

  # Deal with leftover tmpdir from previous, interrupted download
  if $askQuestions && test -d "$imageTmp"; then
    hrule
    echo "The temporary directory \`$imageTmp' already exists. Its contents"
    echo "ARE GOING TO BE DELETED (possibly after having been copied to the"
    echo "image, if they are of interest for it). If you do not want this"
    echo "to happen, press Ctrl-C now. Otherwise, press Return to proceed."
    input ""
  fi

  if $askQuestions; then
    # Ask questions and scan dirs, set up $filesToScan
    scanFiles || return 1
    if $batch; then selectServers; fi
  fi

  if $batch && strNotEmpty "$filesToScan$opt_filesToScan"; then
    # Retrieve template if necessary, then supply files. One of the
    # two variables $filesToScan and $opt_filesToScan is always empty
    fetchTemplate || return 1
    $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
      --template="$template" $jigdoOpts "$filesToScan$opt_filesToScan"
    jigdoErr="$?"
    if test "$jigdoErr" -eq 0 -a -r "$image"; then
      finished
      return 0 # All files were present on local filesystem
    elif test "$jigdoErr" -ge 3; then
      echo "jigdo-file failed with code $jigdoErr - aborting."
      return 1
    fi
  fi

  # Read any files from non-empty tmpDir. Don't delete tmpDir yet, as
  # wget may resume some half-finished files
  if test -d "$imageTmp"; then
    fetchTemplate || return 1
    # Merge into the image
    $jigdoFile $jigdoOpts --no-cache make-image --image="$image" \
      --jigdo="$jigdoF" --template="$template" "$imageTmp"
  fi

  # Download files and merge them into the image. We instruct wget to
  # download 10 files at a time and then merge them. This way, compared
  # to downloading everything, the peak disc space usage is not twice
  # the size of the final image.
  while true; do

    if $batch; then true; else selectServers; fi
    fetchTemplate || return 1
    hrule

    # If a "file:" URI was given instead of a server URL, try to merge
    # any files into the image.
    echo "Merging parts from \`file:' URIs, if any..."
    $jigdoFile print-missing-all --image="$image" --jigdo="$jigdoF" \
        --template="$template" $jigdoOpts $uriOpts \
    | egrep -v '^([a-zA-Z0-9.+_-]+:|$)' \
    | $jigdoFile make-image --image="$image" --jigdo="$jigdoF" \
        --template="$template" $jigdoOpts --files-from=-
    jigdoErr="$?"
    if test "$jigdoErr" -ge 3; then
      echo "jigdo-file failed with code $jigdoErr - aborting."
      $error 1
    fi

    # First try to download all files using the first URL in the
    # print-missing-all list. If any files remain missing, add another
    # pass, this time try to download the missing files using the 2nd
    # URL, and so on.
    noMorePasses=false
    for pass in x xx xxx xxxx xxxxx xxxxxx xxxxxxx xxxxxxxx; do
      $jigdoFile print-missing-all --image="$image" --jigdo="$jigdoF" \
        --template="$template" $jigdoOpts $uriOpts \
      | egrep -i '^(http:|ftp:|$)' >"$list"
      missingCount=`egrep '^$' <"$list" | wc -l | sed -e 's/ *//g'`
      # Accumulate URLs in $@, pass them to fetchAndMerge in batches
      shift "$#" # Solaris /bin/sh doesn't understand "set --"
      count=""
      exec 3<"$list"
      while $readLine url <&3; do
        count="x$count"
        if strEmpty "$url"; then count=""; continue; fi
        if test "$count" != "$pass"; then continue; fi
        if $noMorePasses; then
          hrule
          echo "$missingCount files not found in previous pass, trying"
          echo "alternative download locations:"
          echo
        fi
        noMorePasses=false
        set -- "$@" "$url"
        if test "$#" -ge "$filesPerFetch"; then
          if fetchAndMerge "$@"; then true; else exec 3<&-; return 1; fi
          shift "$#" # Solaris /bin/sh doesn't understand "set --"
        fi
      done
      exec 3<&-
      if test "$#" -ge 1; then fetchAndMerge "$@" || return 1; fi
      if $noMorePasses; then break; fi
      if test -r "$image"; then break; fi
      noMorePasses=true
    done

    rm -f "$list"
    if test -r "$image"; then break; fi

    hrule
    echo "Aaargh - $missingCount files could not be downloaded. This should not"
    echo "happen! Depending on the problem, it may help to retry downloading"
    echo "the missing files."
    if $batch; then return 1; fi
    if $usesDebian || $usesNonus; then
    echo "Also, you could try changing to another Debian or Non-US server,"
    echo "in case the one you used is out of sync."
    fi
    echo
    echo "However, if all the files downloaded without errors and you"
    echo "still get this message, it means that the files changed on the"
    echo "server, so the image cannot be generated."
    if $usesDebian || $usesNonus; then
    echo "As a last resort, you could try to complete the CD image download"
    echo "by fetching the remaining data with rsync."
    fi
    echo
    echo "Press Return to retry downloading the missing files."
    echo "Press Ctrl-C to abort. (If you re-run jigdo-lite later, it will"
    echo "resume from here, the downloaded data is not lost if you press"
    echo "Ctrl-C now.)"
    input ""

  done
  finished
}
#======================================================================

echo
echo 'Jigsaw Download "lite"'
echo "Copyright (C) 2001-2005  |  jigdo@"
echo "Richard Atterer          |  atterer.net"

jigdoFile="jigdo-file"
jigdo-file --version >/dev/null 2>/dev/null
if test "$?" -ne 0; then
    # Using ./jigdo-file is possibly a security risk, so only use if
    # nothing else is there
    if test -x "./jigdo-file"; then jigdoFile="./jigdo-file"; fi
fi
jigdoFileSameDir="`dirname $0`/jigdo-file"
if test -x "$jigdoFileSameDir"; then jigdoFile="$jigdoFileSameDir"; fi

mirrorsSameDir="`dirname $0`/$mirrors"
if test -r "$mirrorsSameDir"; then mirrors="$mirrorsSameDir"; fi

# Check for programs
if $windows; then
    jigdoFile=jigdo-file
else
    for prog in wget egrep sed gzip wc expr; do
        which "$prog" >/dev/null \
            || echo "Could not find program \`$prog' - please install it!"
    done
fi

userAgent="jigdo-lite/`$jigdoFile --version 2>/dev/null | ($readLine jf v ver; echo $ver)` (`wget --version 2>/dev/null | ($readLine ver; echo $ver)`; $OSTYPE)"

# Load preferences file, if present
if test -f "$rcFile"; then
    echo "Loading settings from \`$rcFile'"
    mirrorsX="$mirrors"
    . "$rcFile"
    mirrors="$mirrorsX"
    # When upgrading from versions <0.6.9, update value of tmpDir
    if strEqual "$tmpDir" "tmp" || strEqual "$tmpDir" "temp"; then
        tmpDir="."
    fi
fi

# If running for the first time, try to read mirror info from sources.list
if strEmpty "$debianMirror$nonusMirror" && test -f "/etc/apt/sources.list"; then
    echo "Getting mirror information from /etc/apt/sources.list"
    while $readLine deb url dist rest; do
        case "$deb $dist" in
            "deb "*/non-US)
                test "$nonusMirror" = "" && \
                nonusMirror=`echo "$url"|sed 's%^copy://%file://%; s%//*$%/%'`;;
            "deb "*)
                test "$debianMirror" = "" && \
                debianMirror=`echo "$url"|sed 's%^copy://%file://%; s%//*$%/%'`;;
        esac
    done <"/etc/apt/sources.list"
fi
#________________________________________

# Process command line switches
batch=false
nonInteractive=false
while true; do
  case "$1" in
  --help|-h)
    echo
    echo "Usage: $0 [OPTIONS] [FILES or URLS...]"
    echo "  -h  --help       Print this message"
    echo "  -v  --version    Output version information"
    echo "      --scan PATH  Don't ask for \"Files to scan\", use this path"
    echo "      --noask      \"Press Return automatically\" for all "
    echo "                   questions, accepting the offered defaults"
    exit 0;;
  --version|-v)
    echo
    echo "$userAgent"
    exit 0;;
  --scan)
    opt_filesToScan="$2"
    shift 2;;
  --noask)
    nonInteractive=true
    shift 1;;
  *)
    break;;
    esac
done
#________________________________________

# No cmd line argument => prompt user
if test "$#" -eq 0; then
    hrule
    echo "To resume a half-finished download, enter name of .jigdo file."
    echo "To start a new download, enter URL of .jigdo file."
    echo "You can also enter several URLs/filenames, separated with spaces,"
    echo "or enumerate in {}, e.g. \`http://server/cd-{1_NONUS,2,3}.jigdo'"
    input "jigdo" "$jigdo"
    jigdo="$REPLY"
    set -- "$REPLY"
    saveOptions
fi
# Expand "a{b,c{x,y}}" into "ab acx acy". How's that for a sed script?
# Test cases:
# a{b,c{x,y},d{,2}}   FAILS
# fl{a,e,i,o,u}b
# {wo{x,}of}
# {a,b}x{1,2}
set -- `echo "$*" | sed '
:a
s%{\([^,]*\)}%\1%g
s%{\(\([^{},]*,\)*\)\([^{},]*\){\([^,{}]*\),\{0,1\}\([^{}]*\)}\([^{}]*\)}%{\1\3\4,\3{\5}\6}%
t a
:b
s%{\([^,]*\)}%\1%g
s%\([^{ ]*\){\([^,{}]*\),\{0,1\}\([^{}]*\)}\([^ ]*\)%\1\2\4 \1{\3}\4%
t b'`
if test "$#" -gt 1; then
    hrule
    echo "You have asked me to process several files/URLs:"
    for file in "$@"; do echo "  $file"; done
    batch=true
    echo
    echo "Entering batch mode"
    error="return"
else
    error="exit"
fi
#________________________________________

askQuestions=true
for url in "$@"; do
    selectImage "$url"
done
exit 0
jigdo-0.7.3/scripts/jigdo-mirror0000644000175000017500000003264107701400007016446 0ustar  richardrichard#! /bin/sh
#  __   _
#  |_) /|  Copyright (C) 2002  |  richard@
#  | \/¯|  Richard Atterer     |  atterer.net
#  ¯ '` ¯
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License, version 2. See
#  the file COPYING for details.

# Mirror script for Debian CD images, using Jigsaw Download. You first
# need to set up a conventional mirror (rsync/http/ftp-based) for the
# jigdo and template files, then this script can use those files and
# your local Debian mirror to create the full images automatically.


# This directory will be scanned for .jigdo files
jigdoDir="/home/ftp/debian/jigdo"

# For any file $jigdoDir/somedir/file.jigdo, an attempt will be made
# to create all the images offered by file.jigdo in $imageDir/somedir/
imageDir="/home/ftp/debian-cd"

# Temporary dir to use for creating images. Should be on the same
# partition as $imageDir, because mv is used to put finished images
# into $imageDir.
tmpDir="/home/jigdo-mirror-tmpdir"

# Local Debian/Non-US mirrors. Can use http/ftp URLs, but beware that
# this may cause huge amounts of data to be downloaded repeatedly -
# the ftp/http server had better be on your LAN.
debianMirror="file:/home/ftp/debian"
nonusMirror="file:/home/ftp/debian/non-US"

# Where to put the logfile. If undefined, log output goes to stdout.
# If defined, stderr is also redirected to the logfile
#logfile="$tmpDir/jigdo-mirror-`date +%y%m%d`.log"

# Include and exclude certain files. These are two regular extended
# expressions, matched case-sensitively. The input given to them is
# the filename of the .iso files relative to $imageDir, in the form
# "somedir/image.iso", e.g. "3.0rev0/i386/woody-i386-1.iso". The
# filtering is equivalent to
#  echo $name | egrep $include | egrep -v $exclude
# That is, first the list of files is restricted to those matching
# $include, then anything matching $exclude is removed.
include='.'  # include all files,
exclude='$^' # then exclude none
# Examples:
# US sites: Exclude non-US stuff
#exclude='_NONUS'
# US sites: All i386 images, images 1 and 2 of the rest
#include='i386/|-[12]'; exclude='source/|_NONUS'
# Sites outside the US: All i386 images, images 1 and 2 of the rest
#include='i386/|-[12]'; exclude='source/|-1\.'

# How to call jigdo-file or jigdo-port.
# CAREFUL: Make sure that jigdo-cache.db is not publically accessible
# from the internet since it contains local path info.
jigdoFile="jigdo-file --cache=$tmpDir/jigdo-cache.db --cache-expiry=1w --report=noprogress --no-check-files"
#jigdoFile="jigdo-port"; havePMA=false

# Any files older than $maxAge days are deleted from $imageDir, except
# when the variable is unset; in that case, nothing happens. WARNING:
# This really means *any* files, not just files generated by
# jigdo-mirror.
#maxAge=8

# In case only a few files are missing for the image to be complete,
# will download them from any fallback servers specified in the .jigdo
# file. Maximum number of missing files to download:
maxMissing=100

filesPerFetch=10
wgetOpts="--passive-ftp --no-directories --non-verbose"

# To find the template file, first the leafname of the template's URL
# is extracted from the .jigdo file. Next, for a .jigdo file named
# $jigdoDir/somedir/file.jigdo, the file
# $templateDir/somedir/leafnameFromURL is tried. If that isn't
# present, either the template is downloaded (if the URL is absolute)
# or looked for in $jigdoDir/somedir/templateURL (if the URL is
# relative). Default if unset is templateDir=$jigdoDir.
#templateDir="$jigdoDir"

# If it is inconvenient for you to set the variables above, you can
# either specify a config file with settings on the command line...
if test "$1"; then
    . "$1"
elif test -r ~/.jigdo-mirror; then
    # ... or put the commands in "~/.jigdo-mirror"
    . ~/.jigdo-mirror
fi
#======================================================================
#  No user-serviceable parts below
#======================================================================

# fetch ...
# Download a file, storing it in the current dir
fetch() {
    if test "$#" -eq 0; then return 0; fi
    wget --user-agent="$userAgent" $wgetOpts "$@" || return 1
}
userAgent="jigdo-mirror/1.0 (`wget --version 2>/dev/null | (read ver; echo $ver)`)"
#______________________________________________________________________

# isURI 
# Returns 0 (true) if the supplied string is a HTTP/FTP URL, otherwise 1
isURI() {
    case "$1" in
        http:*|ftp:*|HTTP:*|FTP:*|file:*|FILE:*) return 0;;
        *) return 1;
    esac
}
#______________________________________________________________________

makeImage() {
    rm -f "image" "image.tmp"
    template=`basename "$templateURI"`
    if test -f "$templateDir/$dirName/$template"; then
        # Check for template in $templateDir
        log "    Found template \`\$templateDir/$dirName/$template'"
        ln -s "$templateDir/$dirName/$template" "template"
    elif isURI "$templateURI"; then
        # Absolute template URL - download
        log "    Template \`\$templateDir/$dirName/$template' not found, will download"
        if fetch "$templateURI" -O "template"; then true; else
            log "    Error getting template file"
            exitCode=1
            rm -f "image" "template"
            return 0
        fi
    elif test -f "$jigdoDir/$dirName/$templateURI"; then
        log "    Found template \`\$jigdoDir/$dirName/$templateURI'"
        ln -s "$jigdoDir/$dirName/$templateURI" "template"
    else
        log "    Template file \`\$templateDir/$dirName/$template' not found"
        log "    Template file \`\$jigdoDir/$dirName/$templateURI' not found"
        exitCode=1
        rm -f "image" "template"
        return 0
    fi

    # If possible, check md5sum of template data
    if test "$templateMD5"; then
        set -- `$jigdoFile md5sum --report=quiet "template"`
        if test "$1" = "$templateMD5"; then
            log "    Template checksum is correct"
        else
            log "    Error - template checksum mismatch"
            exitCode=1
            rm -f "image" "template"
            return 0
        fi
    else
        log "    [WARNING - \`Template-MD5Sum' missing from image section]"
    fi

    # Try to merge any files into the image.
    if $havePMA; then
        $jigdoFile print-missing-all $ijtOpts $uriOpts \
        | egrep -v '^([a-zA-Z0-9.+_-]+:|$)' \
        | $jigdoFile make-image $ijtOpts --files-from=-
        jigdoErr="$?"
    else
        $jigdoFile print-missing $ijtOpts $uriOpts \
        | egrep -v '^([a-zA-Z0-9.+_-]+:|$)' \
        | $jigdoFile make-image $ijtOpts --files-from=-
        jigdoErr="$?"
    fi
    if test "$jigdoErr" -ge 2; then
        log "    Error merging data from local filesystem"
        exitCode=1
        rm -f "image" "template"
        return 0
    fi

    # First try to download all files using the first URL in the
    # print-missing-all list. If any files remain missing, add another
    # pass, this time try to download the missing files using the 2nd
    # URL, and so on.
    noMorePasses=$localMirror
    for pass in x xx xxx xxxx xxxxx xxxxxx xxxxxxx xxxxxxxx; do
        if $havePMA; then
            $jigdoFile print-missing-all $ijtOpts $jigdoOpts $uriOpts \
            | egrep -i '^(http:|ftp:|$)' >"list"
        else
            # Quick hack until jigdo-port supports print-missing-all
            $jigdoFile print-missing $ijtOpts $jigdoOpts $uriOpts \
            | egrep -i '^(http:|ftp:|$)' \
            | sed -n '/./{p;s/^.*$//;p;}' >"list"
        fi
        missingCount=`egrep '^$' <"list" | wc -l | sed -e 's/ *//g'`
        if test "$pass" = "x"; then
            if $localMirror; then true; else missingCount=0; fi
        fi
        if test "$missingCount" -gt "$maxMissing"; then
            log "    Too many files ($missingCount) missing in local mirror"
            exitCode=1
            rm -f "list" "image" "template"
            return 0
        fi
        # Accumulate URLs in $@, pass them to fetchAndMerge in batches
        set --
        count=""
        while read url; do
            count="x$count"
            if test "$url" = ""; then count=""; continue; fi
            if test "$count" != "$pass"; then continue; fi
            if $noMorePasses; then
                log "    $missingCount parts still missing from image"
            fi
            noMorePasses=false
            set -- "$@" "$url"
            if test "$#" -ge "$filesPerFetch"; then
                if fetchAndMerge "$@"; then true; else
                    set --; noMorePasses=true
                fi
                set --
            fi
        done <"list"
        if test "$#" -ge 1; then
            if fetchAndMerge "$@"; then true; else break; fi
        fi
        if $noMorePasses; then break; fi
        if test -r "image"; then break; fi
        noMorePasses=true
    done

    if test -r "image"; then
        # Finished - verify checksum
        if $jigdoFile verify $ijtOpts; then
            log "    Image checksum is correct, moving image into place"
            mkdir -p "$imageDir/$dirName"
            mv "image" "$imageDir/$dirName/$image"
        else
            log "    Error - image checksum mismatch"
            exitCode=1
        fi
    else
        log "    Image creation failed, list of missing files follows"
        $jigdoFile print-missing $ijtOpts $jigdoOpts $uriOpts
        exitCode=1
    fi
    rm -f "list" "image" "image.tmp" "template"
    return 0
}
#______________________________________________________________________

# Given URLs, fetch them into $tmpDir, then merge them into image
fetchAndMerge() {
    (mkdir "$tmpDir/files"; cd "$tmpDir/files"; fetch "$@")
    # Merge into the image
    find "$tmpDir/files" -type f \
    | $jigdoFile make-image $ijtOpts --no-cache --files-from=-
    jigdoErr="$?"
    if test "$jigdoErr" -ge 2; then
        exitCode=1
        exit 1
    fi
    # Delete tmpDir, to avoid taking up more space than necessary
    rm -rf "$tmpDir/files"
}
#______________________________________________________________________

sectionEnd() {
  if test "$section" = "[Image]" -a "$image" \
          -a "$templateURI"; then
    log "  Image \`$image', template \`$templateURI'"
    set -- `echo "$dirName/$image" | egrep -- "$include" | egrep -v -- "$exclude"`
    if test "$#" -eq 0; then
      log "    \`\$imageDir/$dirName/$image' excluded by \$include/\$exclude"
      return
    fi
    if test -f "$imageDir/$dirName/$image"; then
      if test "$jigdoDir/$jigdo" -nt "$imageDir/$dirName/$image";then
        log "    jigdo is newer - updating \`\$imageDir/$dirName/$image'"
        # Remove outdated image *immediately*, even in case
        # the subsequent attempt to regenerate it fails
        rm -f "$imageDir/$dirName/$image"
        makeImage
      else
        log "    \`\$imageDir/$dirName/$image' is up to date"
        test "$maxAge" && touch "$imageDir/$dirName/$image"
      fi
    else
      log "    Attempting to create \`\$imageDir/$dirName/$image'"
      makeImage
    fi
  fi
}
#______________________________________________________________________

if test "$logfile"; then
    true >"$logfile"
    exec >>"$logfile"
    exec 2>>"$logfile"
fi
log() { printf "%s: %s\n" "`date +'%Y-%m-%d %H:%M:%S'`" "$1"; }
#________________________________________

log "imageDir:    $imageDir"
log "jigdoDir:    $jigdoDir"
log "templateDir: $templateDir"
# Remove slashes from dir names
jigdoDir=${jigdoDir%/}
imageDir=${imageDir%/}
templateDir=${templateDir%/}
tmpDir=${tmpDir%/}
debianMirror=${debianMirror%/}
nonusMirror=${nonusMirror%/}
uriOpts="--uri Debian='$debianMirror/' --uri Non-US='$nonusMirror/'"
ijtOpts="--image=image --jigdo=jigdo --template=template"
# Is the main mirror on the local disc?
case "$debianMirror $nonusMirror" in
    "file:"*" file:"*|/*" /"*) localMirror=true;;
    *) localMirror=false;;
esac
if test -z "$havePMA"; then havePMA=true; fi
if test -z "$templateDir"; then templateDir="$jigdoDir"; fi
exitCode=0
mkdir -p "$tmpDir" || true
cd "$tmpDir"
#________________________________________

find "$jigdoDir" -name "*.jigdo" \
| sed -e "sÿ^$jigdoDir/ÿÿ" \
| while read jigdo; do

    log "Found \`\$jigdoDir/$jigdo'"
    dirName=`dirname "$jigdo"`
    if gzip -cd "$jigdoDir/$jigdo" >"jigdo" 2>"/dev/null"; then
        true
    elif test -f "$jigdoDir/$jigdo"; then
        rm -f "jigdo" "template"
        ln -s "$jigdoDir/$jigdo" "jigdo"
    else
        log "  jigdo file not present/could not be unpacked - ignored"
        exitCode=1
        continue
    fi

    # Parse jigdo file, look for images
    section=""
    while read REPLY; do
      set -- `echo "$REPLY" | sed -e 's/^ *\[ *\([^ ]*\) *\] *$/[\1]/; s/ *= */ /; s/['\''"$]//g'`
      case "$1" in
        "["*"]")
          sectionEnd
          unset image templateURI templateMD5 shortInfo info
          section="$1";;
        Filename) image="$2";;
        Template) templateURI="$2";;
        Template-MD5Sum) templateMD5="$2";;
        ShortInfo) shift; shortInfo="$*";;
        Info) shift; info="$*";;
      esac
    done <"jigdo"
    sectionEnd

    rm -f "jigdo"

done
#________________________________________

if cd "$imageDir"; then
    if test "$maxAge"; then
        log "Expiring images older than $maxAge days"
        find . -type f -mtime +"$maxAge" \
        | while read file; do
            log "  Deleting \`\$imageDir/${file#./}'"
            rm -f "$file"
        done
        # Remove empty directories
        find "$imageDir" -depth -mindepth 1 -type d -empty \
            -exec rmdir '{}' ';'
    fi
fi
#________________________________________

log "Exit $exitCode"
exit $exitCode
jigdo-0.7.3/scripts/longrun0000755000175000017500000000174507701400002015525 0ustar  richardrichard#! /bin/bash -e

# Copyright 2001 Richard Atterer
# License: GPL version 2

# Run torture on a machine, possibly going through thousands of test
# cases. At regular intervals, send mail about progress and any failed
# cases.

first=0
last=1024
step=32
mailto="`whoami`"

tmp="/tmp/`whoami`"
trap "rm -rf $tmp" EXIT
mkdir -p "$tmp/ironmaiden"
if test -O "$tmp"; then true; else
    echo "Couldn't create $tmp"
    exit 1
fi
x=$first
cp "torture" "$tmp" \
|| cp "src/torture" "$tmp"\
|| cp "debug/src/torture" "$tmp"
cd "$tmp"

while test "$x" -lt "$last"; do
    y=$((x + step))
    nice ./torture $x $y
    if test "$(set `wc -l ironmaiden/report` && echo $1)" \
       -ne "$((step * 2 ))" \
       || egrep -q '^([^O]..|.[^K].|..[^ ])' "ironmaiden/report"; then
        mail -s "FAIL torture $x $y" "$mailto" <"ironmaiden/report"
        else
        mail -s "OK   torture $x $y" "$mailto" <"/dev/null"
    fi
    x=$y
done
mail -s "torture longrun $first $last finished" "$mailto" <"/dev/null"
jigdo-0.7.3/scripts/make-templates0000755000175000017500000000772407701400007016762 0ustar  richardrichard#! /bin/sh -e
#  __   _
#  |_) /|  Copyright (C) 2001  |  richard@
#  | \/¯|  Richard Atterer     |  atterer.net
#  ¯ '` ¯
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License, version 2. See
#  the file COPYING for details.

# Shell script for batch-processing .iso files
# Syntax: make-templates 

# The script looks for all .iso and .raw files in the directory
# specified on the command line. Then it generates .jigdo and
# .template files with jigdo-file, placing them into two separate
# copies of the directory tree that contains the .iso files. The
# script renices itself to run at minimum priority.
#
# You will probably need to edit the variables below. Set mailto to a
# nonempty value to receive a notification mail once the script has
# finished.

pubHtml="$HOME/public_html"
baseUrl="http://cdimage.debian.org/~`whoami`" # corresponds to $pubHtml
jDir="jigdo" # relative to $pubHtml
tDir="jigdo/templates" # relative to $pubHtml
debianMirror="/home/ftp/debian" # NO slash at end!!!
nonusMirror="/home/ftp/debian/non-US" # NO slash at end!!!
jigdoFile="jigdo-file --cache=$HOME/jigdo-cache.db"
mailto="`whoami`" # set to "" not to send mail
mail="`mktemp /tmp/make-templates-XXXXXXXX`"
#______________________________________________________________________

if test "$#" -eq 0; then
    echo "Syntax: $0 "
    echo "        $0  /file /otherdir ..."
    echo "Duplicates dir structure of ;"
    echo "  - jigdo files in \`$pubHtml/$jDir',"
    echo "  - templates in \`$pubHtml/$tDir'."
    echo "  - \`$pubHtml' must be available as"
    echo "    \`$baseUrl'."
    exit 1
fi
#________________________________________

function log() {
    printf "%s: %s\n" "`date -R`" "$1" >>"$mail"
}
echo >"$mail"
test "$mailto" \
&& trap "mail -s \"make-templates run on `hostname -f` finished\" \
             $mailto <\"$mail\"" EXIT
log "Start"
#________________________________________

renice 20 -p $$

dir="$1"
dirp="`dirname \"$dir\"`"
# Do not search all of $dir if further cmd args were specified
if test "$#" -gt 1; then shift; fi

# Find all ISOs
# Use sed to remove $dirp at front, and also remove all files not in $dir
find "$@" '(' -type f -o -type l ')' \
    '(' -name '*.iso' -o -name '*.raw' ')' \
| sed -n "sÿ^$dirp/\?ÿÿp" \
| while read file; do

    filep="`dirname \"$file\"`" # Parent dir of $file
    filestem="`echo $file | sed 's%\.[^.]\+$%%'`" # Remove .iso from $file
    jigdo="$pubHtml/$jDir/$filestem.jigdo" # Location of .jigdo

    mkdir -p "$pubHtml/$jDir/$filep" "$pubHtml/$tDir/$filep"

    # Supply input files, pipe them into jigdo-file
    find "$nonusMirror//" "$debianMirror//dists" "$debianMirror//doc" \
        "$debianMirror//indices" "$debianMirror//pool" \
        "$debianMirror//project" -type f \
    | egrep -v '/Contents|/Packages|/README|INDEX$|/Maintainers|/Release$|/debian-keyring\.tar\.gz$|/ls-lR|//doc/[^/]+/?[^/]*\.(txt|pdf|html)$' \
    | $jigdoFile make-template --force --files-from=- \
        --image="$dirp/$file" \
        --jigdo="$jigdo.tmp" \
        --template="$pubHtml/$tDir/$filestem.template" \
        --label Non-US="$nonusMirror" \
        --label Debian="$debianMirror" \
        --no-image-section --no-servers-section --report=noprogress

    # Append info to .jigdo
    echo >>"$jigdo.tmp"
    echo "[Image]" >>"$jigdo.tmp"
    echo "Filename=`basename \"$file\"`" >>"$jigdo.tmp"
    echo "Template=$baseUrl/$tDir/$filestem.template" >>"$jigdo.tmp"
    printf "Info='Generated on %s'\n" "`date -R`" >>"$jigdo.tmp"
#    echo >>"$jigdo.tmp"
#    echo "[Servers]" >>"$jigdo.tmp"
#    echo "Debian=ftp://cdimage.debian.org/debian-2.2r6-snapshot/" >>"$jigdo.tmp"
#    echo "Non-US=ftp://cdimage.debian.org/debian-2.2r6-snapshot/non-US/" >>"$jigdo.tmp"
#    echo "MD5Sum=ftp://ftp.fsn.hu/pub/debian-superseded/" >>"$jigdo.tmp"
    gzip -9 "$jigdo.tmp"
    mv "$jigdo.tmp.gz" "$jigdo"
    log "Finished \`$file'"
done
log "Exit"
jigdo-0.7.3/scripts/win-lib-install.sh0000644000175000017500000000716310324230347017464 0ustar  richardrichard#! /bin/sh

# This script will download all necessary libraries to compile jigdo
# and jigdo-file under Windows, or to cross-compile it for
# Windows under Linux. The links are bound to become outdated over
# time, please send updates to the jigdo-user list.

date=050707
# Dest dir. WARNING, the dir will be deleted at the beginning of this
# script! Subdirs called lib, bin, ... will be created.
inst=~/samba/gtkwin-$date

# Dir for downloaded software tarballs
dl=~/samba/gtkwin-$date-dl

# Sourceforge mirror
sf=ovh.dl.sourceforge.net

if test -f ~/.jigdo-win-lib-install; then
  . ~/.jigdo-win-lib-install
fi
#______________________________________________________________________

cmd() {
    echo "$@"
    "$@"
}

cmd rm -rf "$inst/"*
cmd mkdir -p "$inst"
cmd mkdir -p "$dl"
cd "$inst"

get() {
    file="$dl/`basename $1`"
    if test -f "$file"; then
        echo "$file"
    else
        cmd wget -nc --directory-prefix="$dl" "$1"
    fi
}

buntar() {
    echo buntar "$@"
    for f; do
	bzip2 -cd "$f" | tar -xf -
    done
}

unzip() {
    cmd /usr/bin/unzip -q -o "$@"
}
#______________________________________________________________________

# GTK+ for Windows
# http://www.gimp.org/~tml/gimp/win32/downloads.html

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/glib-2.6.5.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/glib-dev-2.6.5.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/gtk+-2.6.8.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/gtk+-dev-2.6.8.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/pango-1.8.0.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/pango-dev-1.8.0.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/atk-1.9.0.zip
unzip "$file"

get ftp://ftp.gtk.org/pub/gtk/v2.6/win32/atk-dev-1.9.0.zip
unzip "$file"

#______________________________________________________________________

# Various dependencies
# http://www.gimp.org/~tml/gimp/win32/downloads.html

get http://$sf/sourceforge/gnuwin32/libpng-1.2.8-lib.zip
unzip "$file"
get http://$sf/sourceforge/gnuwin32/libpng-1.2.8-bin.zip
unzip "$file"

get http://www.zlib.net/zlib122-dll.zip
unzip "$file"
cmd mv zlib1.dll bin/
cp lib/zdll.lib lib/libz.a # allows -lz to be used for linking

get http://$sf/sourceforge/gnuwin32/bzip2-1.0.3-lib.zip
unzip "$file"
get http://$sf/sourceforge/gnuwin32/bzip2-1.0.3-bin.zip
unzip "$file"

get http://www.gimp.org/~tml/gimp/win32/pkgconfig-0.15.zip
unzip "$file"

get http://www.gimp.org/~tml/gimp/win32/libiconv-1.9.1.bin.woe32.zip
unzip "$file"

get http://www.gimp.org/~tml/gimp/win32/gettext-runtime-0.13.1.zip
unzip "$file"
#______________________________________________________________________

# Still missing, but not strictly needed ATM:
# - libdb

# http://curl.haxx.se/download.html
#get http://curl.haxx.se/download/curl-7.13.0-win32-ssl-devel-mingw32.zip
#unzip "$file"
#cmd mv libcurl.a libcurldll.a lib/
#cmd mv curl.exe libcurl.dll bin/

#unzip ~/samba/curl-7.14.0-win32-ssl-devel-mingw32msbitfields-ra050711.zip

# Needs zlibwapi.dll
#get http://curl.haxx.se/download/libcurl-7.14.0-win32-msvc.zip
#unzip "$file"
#cmd mv libcurl.lib lib/
#cmd mv libcurl.dll bin/

# OpenSSL for curl
# http://curl.haxx.se/download.html

# This one only has the DLL:
#get http://curl.haxx.se/download/openssl-0.9.7e-win32-bin.zip
#unzip "$file"
#cmd mv libeay32.dll libssl32.dll openssl.exe bin/

# This one also has the developer libs, headers etc.
get http://$sf/sourceforge/gnuwin32/openssl-0.9.7c-lib.zip
unzip "$file"
get http://$sf/sourceforge/gnuwin32/openssl-0.9.7c-bin.zip
unzip "$file"

# Cross-compile:
# sh go --with-libcurl='-lcurl -lssl -lcrypto -lzdll -lwinmm'
# make X=-DCURL_STATICLIB
jigdo-0.7.3/src/.cvsignore0000644000175000017500000000041210262247425015217 0ustar  richardrichardconfig.h
Makefile
types.h
html
*-test
jigdo-file
jigdo
torture
Makefile.mingw
config.h.mingw
ironmaiden
gtk-gui.cc.tmp
gtk-gui.hh.tmp
gtk-interface.cc.tmp
gtk-interface.hh.tmp
gtk-interface.cc
gtk-interface.hh
gtk-interface.stamp
cvs
*.dll
Makedeps
jigdo.res
apidoc
jigdo-0.7.3/src/Doxyfile0000644000175000017500000013434610154434620014736 0ustar  richardrichard# Doxyfile 1.3.8

# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
#       TAG = value [value, ...]
# For lists items can also be appended using:
#       TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")

#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------

# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
# by quotes) that should identify the project.

PROJECT_NAME           = jigdo

# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
# This could be handy for archiving the generated documentation or 
# if some version control system is used.

PROJECT_NUMBER         = 

# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
# base path where the generated documentation will be put. 
# If a relative path is entered, it will be relative to the location 
# where doxygen was started. If left blank the current directory will be used.

OUTPUT_DIRECTORY       = 

# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
# 4096 sub-directories (in 2 levels) under the output directory of each output 
# format and will distribute the generated files over these directories. 
# Enabling this option can be useful when feeding doxygen a huge amount of source 
# files, where putting all generated files in the same directory would otherwise 
# cause performance problems for the file system.

CREATE_SUBDIRS         = NO

# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
# documentation generated by doxygen is written. Doxygen will use this 
# information to generate all constant output in the proper language. 
# The default language is English, other supported languages are: 
# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
# Swedish, and Ukrainian.

OUTPUT_LANGUAGE        = English

# This tag can be used to specify the encoding used in the generated output. 
# The encoding is not always determined by the language that is chosen, 
# but also whether or not the output is meant for Windows or non-Windows users. 
# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
# forces the Windows encoding (this is the default for the Windows binary), 
# whereas setting the tag to NO uses a Unix-style encoding (the default for 
# all platforms other than Windows).

USE_WINDOWS_ENCODING   = NO

# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
# include brief member descriptions after the members that are listed in 
# the file and class documentation (similar to JavaDoc). 
# Set to NO to disable this.

BRIEF_MEMBER_DESC      = YES

# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
# the brief description of a member or function before the detailed description. 
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
# brief descriptions will be completely suppressed.

REPEAT_BRIEF           = YES

# This tag implements a quasi-intelligent brief description abbreviator 
# that is used to form the text in various listings. Each string 
# in this list, if found as the leading text of the brief description, will be 
# stripped from the text and the result after processing the whole list, is used 
# as the annotated text. Otherwise, the brief description is used as-is. If left 
# blank, the following values are used ("$name" is automatically replaced with the 
# name of the entity): "The $name class" "The $name widget" "The $name file" 
# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"

ABBREVIATE_BRIEF       = 

# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
# Doxygen will generate a detailed section even if there is only a brief 
# description.

ALWAYS_DETAILED_SEC    = NO

# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
# members of a class in the documentation of that class as if those members were 
# ordinary class members. Constructors, destructors and assignment operators of 
# the base classes will not be shown.

INLINE_INHERITED_MEMB  = NO

# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
# path before files name in the file list and in the header files. If set 
# to NO the shortest path that makes the file name unique will be used.

FULL_PATH_NAMES        = YES

# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
# can be used to strip a user-defined part of the path. Stripping is 
# only done if one of the specified strings matches the left-hand part of 
# the path. The tag can be used to show relative paths in the file list. 
# If left blank the directory from which doxygen is run is used as the 
# path to strip.

STRIP_FROM_PATH        = 

# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
# the path mentioned in the documentation of a class, which tells 
# the reader which header file to include in order to use a class. 
# If left blank only the name of the header file containing the class 
# definition is used. Otherwise one should specify the include paths that 
# are normally passed to the compiler using the -I flag.

STRIP_FROM_INC_PATH    = 

# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
# (but less readable) file names. This can be useful is your file systems 
# doesn't support long names like on DOS, Mac, or CD-ROM.

SHORT_NAMES            = NO

# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
# will interpret the first line (until the first dot) of a JavaDoc-style 
# comment as the brief description. If set to NO, the JavaDoc 
# comments will behave just like the Qt-style comments (thus requiring an 
# explicit @brief command for a brief description.

JAVADOC_AUTOBRIEF      = YES

# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
# comments) as a brief description. This used to be the default behaviour. 
# The new default is to treat a multi-line C++ comment block as a detailed 
# description. Set this tag to YES if you prefer the old behaviour instead.

MULTILINE_CPP_IS_BRIEF = NO

# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member 
# documentation.

DETAILS_AT_TOP         = NO

# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
# member inherits the documentation from any documented member that it 
# re-implements.

INHERIT_DOCS           = YES

# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
# tag is set to YES, then doxygen will reuse the documentation of the first 
# member in the group (if any) for the other members of the group. By default 
# all members of a group must be documented explicitly.

DISTRIBUTE_GROUP_DOC   = NO

# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
# Doxygen uses this value to replace tabs by spaces in code fragments.

TAB_SIZE               = 8

# This tag can be used to specify a number of aliases that acts 
# as commands in the documentation. An alias has the form "name=value". 
# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
# put the command \sideeffect (or @sideeffect) in the documentation, which 
# will result in a user-defined paragraph with heading "Side Effects:". 
# You can put \n's in the value part of an alias to insert newlines.

ALIASES                = 

# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
# only. Doxygen will then generate output that is more tailored for C. 
# For instance, some of the names that are used will be different. The list 
# of all members will be omitted, etc.

OPTIMIZE_OUTPUT_FOR_C  = NO

# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
# only. Doxygen will then generate output that is more tailored for Java. 
# For instance, namespaces will be presented as packages, qualified scopes 
# will look different, etc.

OPTIMIZE_OUTPUT_JAVA   = NO

# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
# the same type (for instance a group of public functions) to be put as a 
# subgroup of that type (e.g. under the Public Functions section). Set it to 
# NO to prevent subgrouping. Alternatively, this can be done per class using 
# the \nosubgrouping command.

SUBGROUPING            = YES

#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------

# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
# documentation are documented, even if no documentation was available. 
# Private class members and static file members will be hidden unless 
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES

EXTRACT_ALL            = YES

# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
# will be included in the documentation.

EXTRACT_PRIVATE        = NO

# If the EXTRACT_STATIC tag is set to YES all static members of a file 
# will be included in the documentation.

EXTRACT_STATIC         = YES

# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
# defined locally in source files will be included in the documentation. 
# If set to NO only classes defined in header files are included.

EXTRACT_LOCAL_CLASSES  = YES

# This flag is only useful for Objective-C code. When set to YES local 
# methods, which are defined in the implementation section but not in 
# the interface are included in the documentation. 
# If set to NO (the default) only methods in the interface are included.

EXTRACT_LOCAL_METHODS  = NO

# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
# undocumented members of documented classes, files or namespaces. 
# If set to NO (the default) these members will be included in the 
# various overviews, but no documentation section is generated. 
# This option has no effect if EXTRACT_ALL is enabled.

HIDE_UNDOC_MEMBERS     = NO

# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
# undocumented classes that are normally visible in the class hierarchy. 
# If set to NO (the default) these classes will be included in the various 
# overviews. This option has no effect if EXTRACT_ALL is enabled.

HIDE_UNDOC_CLASSES     = NO

# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
# friend (class|struct|union) declarations. 
# If set to NO (the default) these declarations will be included in the 
# documentation.

HIDE_FRIEND_COMPOUNDS  = NO

# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
# documentation blocks found inside the body of a function. 
# If set to NO (the default) these blocks will be appended to the 
# function's detailed documentation block.

HIDE_IN_BODY_DOCS      = NO

# The INTERNAL_DOCS tag determines if documentation 
# that is typed after a \internal command is included. If the tag is set 
# to NO (the default) then the documentation will be excluded. 
# Set it to YES to include the internal documentation.

INTERNAL_DOCS          = NO

# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
# file names in lower-case letters. If set to YES upper-case letters are also 
# allowed. This is useful if you have classes or files whose names only differ 
# in case and if your file system supports case sensitive file names. Windows 
# and Mac users are advised to set this option to NO.

CASE_SENSE_NAMES       = YES

# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
# will show members with their full class and namespace scopes in the 
# documentation. If set to YES the scope will be hidden.

HIDE_SCOPE_NAMES       = NO

# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
# will put a list of the files that are included by a file in the documentation 
# of that file.

SHOW_INCLUDE_FILES     = YES

# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
# is inserted in the documentation for inline members.

INLINE_INFO            = YES

# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
# will sort the (detailed) documentation of file and class members 
# alphabetically by member name. If set to NO the members will appear in 
# declaration order.

SORT_MEMBER_DOCS       = YES

# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
# brief documentation of file, namespace and class members alphabetically 
# by member name. If set to NO (the default) the members will appear in 
# declaration order.

SORT_BRIEF_DOCS        = NO

# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
# sorted by fully-qualified names, including namespaces. If set to 
# NO (the default), the class list will be sorted only by class name, 
# not including the namespace part. 
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the 
# alphabetical list.

SORT_BY_SCOPE_NAME     = NO

# The GENERATE_TODOLIST tag can be used to enable (YES) or 
# disable (NO) the todo list. This list is created by putting \todo 
# commands in the documentation.

GENERATE_TODOLIST      = YES

# The GENERATE_TESTLIST tag can be used to enable (YES) or 
# disable (NO) the test list. This list is created by putting \test 
# commands in the documentation.

GENERATE_TESTLIST      = YES

# The GENERATE_BUGLIST tag can be used to enable (YES) or 
# disable (NO) the bug list. This list is created by putting \bug 
# commands in the documentation.

GENERATE_BUGLIST       = YES

# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
# disable (NO) the deprecated list. This list is created by putting 
# \deprecated commands in the documentation.

GENERATE_DEPRECATEDLIST= YES

# The ENABLED_SECTIONS tag can be used to enable conditional 
# documentation sections, marked by \if sectionname ... \endif.

ENABLED_SECTIONS       = 

# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
# the initial value of a variable or define consists of for it to appear in 
# the documentation. If the initializer consists of more lines than specified 
# here it will be hidden. Use a value of 0 to hide initializers completely. 
# The appearance of the initializer of individual variables and defines in the 
# documentation can be controlled using \showinitializer or \hideinitializer 
# command in the documentation regardless of this setting.

MAX_INITIALIZER_LINES  = 30

# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
# at the bottom of the documentation of classes and structs. If set to YES the 
# list will mention the files that were used to generate the documentation.

SHOW_USED_FILES        = YES

#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------

# The QUIET tag can be used to turn on/off the messages that are generated 
# by doxygen. Possible values are YES and NO. If left blank NO is used.

QUIET                  = YES

# The WARNINGS tag can be used to turn on/off the warning messages that are 
# generated by doxygen. Possible values are YES and NO. If left blank 
# NO is used.

WARNINGS               = YES

# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
# automatically be disabled.

WARN_IF_UNDOCUMENTED   = YES

# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
# potential errors in the documentation, such as not documenting some 
# parameters in a documented function, or documenting parameters that 
# don't exist or using markup commands wrongly.

WARN_IF_DOC_ERROR      = YES

# The WARN_FORMAT tag determines the format of the warning messages that 
# doxygen can produce. The string should contain the $file, $line, and $text 
# tags, which will be replaced by the file and line number from which the 
# warning originated and the warning text.

WARN_FORMAT            = "$file:$line: $text"

# The WARN_LOGFILE tag can be used to specify a file to which warning 
# and error messages should be written. If left blank the output is written 
# to stderr.

WARN_LOGFILE           = 

#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------

# The INPUT tag can be used to specify the files and/or directories that contain 
# documented source files. You may enter file names like "myfile.cpp" or 
# directories like "/usr/src/myproject". Separate the files or directories 
# with spaces.

INPUT                  = 

# If the value of the INPUT tag contains directories, you can use the 
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
# and *.h) to filter out the source-files in the directories. If left 
# blank the following patterns are tested: 
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm

FILE_PATTERNS          = *.c *.cc *.h *.hh

# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
# should be searched for input files as well. Possible values are YES and NO. 
# If left blank NO is used.

RECURSIVE              = YES

# The EXCLUDE tag can be used to specify files and/or directories that should 
# excluded from the INPUT source files. This way you can easily exclude a 
# subdirectory from a directory tree whose root is specified with the INPUT tag.

EXCLUDE                = 

# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
# that are symbolic links (a Unix filesystem feature) are excluded from the input.

EXCLUDE_SYMLINKS       = NO

# If the value of the INPUT tag contains directories, you can use the 
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
# certain files from those directories.

EXCLUDE_PATTERNS       = interface.* glibwww* libwww*

# The EXAMPLE_PATH tag can be used to specify one or more files or 
# directories that contain example code fragments that are included (see 
# the \include command).

EXAMPLE_PATH           = 

# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
# and *.h) to filter out the source-files in the directories. If left 
# blank all files are included.

EXAMPLE_PATTERNS       = 

# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
# searched for input files to be used with the \include or \dontinclude 
# commands irrespective of the value of the RECURSIVE tag. 
# Possible values are YES and NO. If left blank NO is used.

EXAMPLE_RECURSIVE      = NO

# The IMAGE_PATH tag can be used to specify one or more files or 
# directories that contain image that are included in the documentation (see 
# the \image command).

IMAGE_PATH             = 

# The INPUT_FILTER tag can be used to specify a program that doxygen should 
# invoke to filter for each input file. Doxygen will invoke the filter program 
# by executing (via popen()) the command  , where  
# is the value of the INPUT_FILTER tag, and  is the name of an 
# input file. Doxygen will then use the output that the filter program writes 
# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
# ignored.

INPUT_FILTER           = 

# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
# basis.  Doxygen will compare the file name with each pattern and apply the 
# filter if there is a match.  The filters are a list of the form: 
# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
# is applied to all files.

FILTER_PATTERNS        = 

# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
# INPUT_FILTER) will be used to filter the input files when producing source 
# files to browse (i.e. when SOURCE_BROWSER is set to YES).

FILTER_SOURCE_FILES    = NO

#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------

# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
# be generated. Documented entities will be cross-referenced with these sources. 
# Note: To get rid of all source code in the generated output, make sure also 
# VERBATIM_HEADERS is set to NO.

SOURCE_BROWSER         = NO

# Setting the INLINE_SOURCES tag to YES will include the body 
# of functions and classes directly in the documentation.

INLINE_SOURCES         = NO

# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
# doxygen to hide any special comment blocks from generated source code 
# fragments. Normal C and C++ comments will always remain visible.

STRIP_CODE_COMMENTS    = YES

# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
# then for each documented function all documented 
# functions referencing it will be listed.

REFERENCED_BY_RELATION = YES

# If the REFERENCES_RELATION tag is set to YES (the default) 
# then for each documented function all documented entities 
# called/used by that function will be listed.

REFERENCES_RELATION    = YES

# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
# will generate a verbatim copy of the header file for each class for 
# which an include is specified. Set to NO to disable this.

VERBATIM_HEADERS       = YES

#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------

# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
# of all compounds will be generated. Enable this if the project 
# contains a lot of classes, structs, unions or interfaces.

ALPHABETICAL_INDEX     = NO

# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
# in which this list will be split (can be a number in the range [1..20])

COLS_IN_ALPHA_INDEX    = 5

# In case all classes in a project start with a common prefix, all 
# classes will be put under the same header in the alphabetical index. 
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
# should be ignored while generating the index headers.

IGNORE_PREFIX          = 

#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------

# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
# generate HTML output.

GENERATE_HTML          = YES

# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
# put in front of it. If left blank `html' will be used as the default path.

HTML_OUTPUT            = apidoc
#html

# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
# doxygen will generate files with .html extension.

HTML_FILE_EXTENSION    = .html

# The HTML_HEADER tag can be used to specify a personal HTML header for 
# each generated HTML page. If it is left blank doxygen will generate a 
# standard header.

HTML_HEADER            = 

# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
# each generated HTML page. If it is left blank doxygen will generate a 
# standard footer.

HTML_FOOTER            = 

# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
# style sheet that is used by each HTML page. It can be used to 
# fine-tune the look of the HTML output. If the tag is left blank doxygen 
# will generate a default style sheet. Note that doxygen will try to copy 
# the style sheet file to the HTML output directory, so don't put your own 
# stylesheet in the HTML output directory as well, or it will be erased!

HTML_STYLESHEET        = 

# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
# files or namespaces will be aligned in HTML using tables. If set to 
# NO a bullet list will be used.

HTML_ALIGN_MEMBERS     = YES

# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
# will be generated that can be used as input for tools like the 
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
# of the generated HTML documentation.

GENERATE_HTMLHELP      = NO

# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
# be used to specify the file name of the resulting .chm file. You 
# can add a path in front of the file if the result should not be 
# written to the html output directory.

CHM_FILE               = 

# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
# be used to specify the location (absolute path including file name) of 
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
# the HTML help compiler on the generated index.hhp.

HHC_LOCATION           = 

# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
# controls if a separate .chi index file is generated (YES) or that 
# it should be included in the master .chm file (NO).

GENERATE_CHI           = NO

# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
# controls whether a binary table of contents is generated (YES) or a 
# normal table of contents (NO) in the .chm file.

BINARY_TOC             = NO

# The TOC_EXPAND flag can be set to YES to add extra items for group members 
# to the contents of the HTML help documentation and to the tree view.

TOC_EXPAND             = NO

# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
# top of each HTML page. The value NO (the default) enables the index and 
# the value YES disables it.

DISABLE_INDEX          = NO

# This tag can be used to set the number of enum values (range [1..20]) 
# that doxygen will group on one line in the generated HTML documentation.

ENUM_VALUES_PER_LINE   = 4

# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that 
# is generated for HTML Help). For this to work a browser that supports 
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
# probably better off using the HTML help feature.

GENERATE_TREEVIEW      = NO

# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
# used to set the initial width (in pixels) of the frame in which the tree 
# is shown.

TREEVIEW_WIDTH         = 250

#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------

# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
# generate Latex output.

GENERATE_LATEX         = NO

# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
# put in front of it. If left blank `latex' will be used as the default path.

LATEX_OUTPUT           = latex

# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
# invoked. If left blank `latex' will be used as the default command name.

LATEX_CMD_NAME         = latex

# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
# generate index for LaTeX. If left blank `makeindex' will be used as the 
# default command name.

MAKEINDEX_CMD_NAME     = makeindex

# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
# LaTeX documents. This may be useful for small projects and may help to 
# save some trees in general.

COMPACT_LATEX          = NO

# The PAPER_TYPE tag can be used to set the paper type that is used 
# by the printer. Possible values are: a4, a4wide, letter, legal and 
# executive. If left blank a4wide will be used.

PAPER_TYPE             = a4wide

# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
# packages that should be included in the LaTeX output.

EXTRA_PACKAGES         = 

# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
# the generated latex document. The header should contain everything until 
# the first chapter. If it is left blank doxygen will generate a 
# standard header. Notice: only use this tag if you know what you are doing!

LATEX_HEADER           = 

# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
# contain links (just like the HTML output) instead of page references 
# This makes the output suitable for online browsing using a pdf viewer.

PDF_HYPERLINKS         = NO

# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
# plain latex in the generated Makefile. Set this option to YES to get a 
# higher quality PDF documentation.

USE_PDFLATEX           = NO

# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
# command to the generated LaTeX files. This will instruct LaTeX to keep 
# running if errors occur, instead of asking the user for help. 
# This option is also used when generating formulas in HTML.

LATEX_BATCHMODE        = NO

# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
# include the index chapters (such as File Index, Compound Index, etc.) 
# in the output.

LATEX_HIDE_INDICES     = NO

#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------

# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
# The RTF output is optimized for Word 97 and may not look very pretty with 
# other RTF readers or editors.

GENERATE_RTF           = NO

# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
# put in front of it. If left blank `rtf' will be used as the default path.

RTF_OUTPUT             = rtf

# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
# RTF documents. This may be useful for small projects and may help to 
# save some trees in general.

COMPACT_RTF            = NO

# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
# will contain hyperlink fields. The RTF file will 
# contain links (just like the HTML output) instead of page references. 
# This makes the output suitable for online browsing using WORD or other 
# programs which support those fields. 
# Note: wordpad (write) and others do not support links.

RTF_HYPERLINKS         = NO

# Load stylesheet definitions from file. Syntax is similar to doxygen's 
# config file, i.e. a series of assignments. You only have to provide 
# replacements, missing definitions are set to their default value.

RTF_STYLESHEET_FILE    = 

# Set optional variables used in the generation of an rtf document. 
# Syntax is similar to doxygen's config file.

RTF_EXTENSIONS_FILE    = 

#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------

# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
# generate man pages

GENERATE_MAN           = NO

# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
# put in front of it. If left blank `man' will be used as the default path.

MAN_OUTPUT             = man

# The MAN_EXTENSION tag determines the extension that is added to 
# the generated man pages (default is the subroutine's section .3)

MAN_EXTENSION          = .3

# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
# then it will generate one additional man file for each entity 
# documented in the real man page(s). These additional files 
# only source the real man page, but without them the man command 
# would be unable to find the correct page. The default is NO.

MAN_LINKS              = NO

#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------

# If the GENERATE_XML tag is set to YES Doxygen will 
# generate an XML file that captures the structure of 
# the code including all documentation.

GENERATE_XML           = NO

# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
# put in front of it. If left blank `xml' will be used as the default path.

XML_OUTPUT             = xml

# The XML_SCHEMA tag can be used to specify an XML schema, 
# which can be used by a validating XML parser to check the 
# syntax of the XML files.

XML_SCHEMA             = 

# The XML_DTD tag can be used to specify an XML DTD, 
# which can be used by a validating XML parser to check the 
# syntax of the XML files.

XML_DTD                = 

# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
# dump the program listings (including syntax highlighting 
# and cross-referencing information) to the XML output. Note that 
# enabling this will significantly increase the size of the XML output.

XML_PROGRAMLISTING     = YES

#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------

# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
# generate an AutoGen Definitions (see autogen.sf.net) file 
# that captures the structure of the code including all 
# documentation. Note that this feature is still experimental 
# and incomplete at the moment.

GENERATE_AUTOGEN_DEF   = NO

#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------

# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
# generate a Perl module file that captures the structure of 
# the code including all documentation. Note that this 
# feature is still experimental and incomplete at the 
# moment.

GENERATE_PERLMOD       = NO

# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
# to generate PDF and DVI output from the Perl module output.

PERLMOD_LATEX          = NO

# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
# nicely formatted so it can be parsed by a human reader.  This is useful 
# if you want to understand what is going on.  On the other hand, if this 
# tag is set to NO the size of the Perl module output will be much smaller 
# and Perl will parse it just the same.

PERLMOD_PRETTY         = YES

# The names of the make variables in the generated doxyrules.make file 
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
# This is useful so different doxyrules.make files included by the same 
# Makefile don't overwrite each other's variables.

PERLMOD_MAKEVAR_PREFIX = 

#---------------------------------------------------------------------------
# Configuration options related to the preprocessor   
#---------------------------------------------------------------------------

# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
# evaluate all C-preprocessor directives found in the sources and include 
# files.

ENABLE_PREPROCESSING   = YES

# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
# names in the source code. If set to NO (the default) only conditional 
# compilation will be performed. Macro expansion can be done in a controlled 
# way by setting EXPAND_ONLY_PREDEF to YES.

MACRO_EXPANSION        = NO

# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
# then the macro expansion is limited to the macros specified with the 
# PREDEFINED and EXPAND_AS_PREDEFINED tags.

EXPAND_ONLY_PREDEF     = NO

# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
# in the INCLUDE_PATH (see below) will be search if a #include is found.

SEARCH_INCLUDES        = YES

# The INCLUDE_PATH tag can be used to specify one or more directories that 
# contain include files that are not input files but should be processed by 
# the preprocessor.

INCLUDE_PATH           = 

# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
# patterns (like *.h and *.hpp) to filter out the header-files in the 
# directories. If left blank, the patterns specified with FILE_PATTERNS will 
# be used.

INCLUDE_FILE_PATTERNS  = 

# The PREDEFINED tag can be used to specify one or more macro names that 
# are defined before the preprocessor is started (similar to the -D option of 
# gcc). The argument of the tag is a list of macros of the form: name 
# or name=definition (no spaces). If the definition and the = are 
# omitted =1 is assumed.

PREDEFINED             = DOXYGEN_SKIP

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
# this tag can be used to specify a list of macro names that should be expanded. 
# The macro definition that is found in the sources will be used. 
# Use the PREDEFINED tag if you want to use a different macro definition.

EXPAND_AS_DEFINED      = 

# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
# doxygen's preprocessor will remove all function-like macros that are alone 
# on a line, have an all uppercase name, and do not end with a semicolon. Such 
# function macros are typically used for boiler-plate code, and will confuse the 
# parser if not removed.

SKIP_FUNCTION_MACROS   = YES

#---------------------------------------------------------------------------
# Configuration::additions related to external references   
#---------------------------------------------------------------------------

# The TAGFILES option can be used to specify one or more tagfiles. 
# Optionally an initial location of the external documentation 
# can be added for each tagfile. The format of a tag file without 
# this location is as follows: 
#   TAGFILES = file1 file2 ... 
# Adding location for the tag files is done as follows: 
#   TAGFILES = file1=loc1 "file2 = loc2" ... 
# where "loc1" and "loc2" can be relative or absolute paths or 
# URLs. If a location is present for each tag, the installdox tool 
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen 
# is run, you must also specify the path to the tagfile here.

TAGFILES               = 

# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
# a tag file that is based on the input files it reads.

GENERATE_TAGFILE       = 

# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
# in the class index. If set to NO only the inherited external classes 
# will be listed.

ALLEXTERNALS           = NO

# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
# in the modules index. If set to NO, only the current project's groups will 
# be listed.

EXTERNAL_GROUPS        = YES

# The PERL_PATH should be the absolute path and name of the perl script 
# interpreter (i.e. the result of `which perl').

PERL_PATH              = /usr/bin/perl

#---------------------------------------------------------------------------
# Configuration options related to the dot tool   
#---------------------------------------------------------------------------

# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
# super classes. Setting the tag to NO turns the diagrams off. Note that this 
# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
# recommended to install and use dot, since it yields more powerful graphs.

CLASS_DIAGRAMS         = YES

# If set to YES, the inheritance and collaboration graphs will hide 
# inheritance and usage relations if the target is undocumented 
# or is not a class.

HIDE_UNDOC_RELATIONS   = YES

# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
# available from the path. This tool is part of Graphviz, a graph visualization 
# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
# have no effect if this option is set to NO (the default)

HAVE_DOT               = NO

# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
# will generate a graph for each documented class showing the direct and 
# indirect inheritance relations. Setting this tag to YES will force the 
# the CLASS_DIAGRAMS tag to NO.

CLASS_GRAPH            = YES

# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
# will generate a graph for each documented class showing the direct and 
# indirect implementation dependencies (inheritance, containment, and 
# class references variables) of the class with other documented classes.

COLLABORATION_GRAPH    = YES

# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
# collaboration diagrams in a style similar to the OMG's Unified Modeling 
# Language.

UML_LOOK               = YES

# If set to YES, the inheritance and collaboration graphs will show the 
# relations between templates and their instances.

TEMPLATE_RELATIONS     = YES

# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
# tags are set to YES then doxygen will generate a graph for each documented 
# file showing the direct and indirect include dependencies of the file with 
# other documented files.

INCLUDE_GRAPH          = YES

# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
# documented header file showing the documented files that directly or 
# indirectly include this file.

INCLUDED_BY_GRAPH      = YES

# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
# generate a call dependency graph for every global function or class method. 
# Note that enabling this option will significantly increase the time of a run. 
# So in most cases it will be better to enable call graphs for selected 
# functions only using the \callgraph command.

CALL_GRAPH             = NO

# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
# will graphical hierarchy of all classes instead of a textual one.

GRAPHICAL_HIERARCHY    = YES

# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.

DOT_IMAGE_FORMAT       = png

# The tag DOT_PATH can be used to specify the path where the dot tool can be 
# found. If left blank, it is assumed the dot tool can be found on the path.

DOT_PATH               = 

# The DOTFILE_DIRS tag can be used to specify one or more directories that 
# contain dot files that are included in the documentation (see the 
# \dotfile command).

DOTFILE_DIRS           = 

# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
# this value, doxygen will try to truncate the graph, so that it fits within 
# the specified constraint. Beware that most browsers cannot cope with very 
# large images.

MAX_DOT_GRAPH_WIDTH    = 1024

# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
# this value, doxygen will try to truncate the graph, so that it fits within 
# the specified constraint. Beware that most browsers cannot cope with very 
# large images.

MAX_DOT_GRAPH_HEIGHT   = 1024

# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
# graphs generated by dot. A depth value of 3 means that only nodes reachable 
# from the root by following a path via at most 3 edges will be shown. Nodes that 
# lay further from the root node will be omitted. Note that setting this option to 
# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
# note that a graph may be further truncated if the graph's image dimensions are 
# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
# If 0 is used for the depth value (the default), the graph is not depth-constrained.

MAX_DOT_GRAPH_DEPTH    = 0

# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
# generate a legend page explaining the meaning of the various boxes and 
# arrows in the dot generated graphs.

GENERATE_LEGEND        = YES

# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
# remove the intermediate dot files that are used to generate 
# the various graphs.

DOT_CLEANUP            = YES

#---------------------------------------------------------------------------
# Configuration::additions related to the search engine   
#---------------------------------------------------------------------------

# The SEARCHENGINE tag specifies whether or not a search engine should be 
# used. If set to NO the values of all tags below this one will be ignored.

SEARCHENGINE           = NO
jigdo-0.7.3/src/Makedeps0000644000175000017500000004601410433432676014707 0ustar  richardrichard# job/url-mapping-test.cc:13
job/url-mapping-test$(EXE): job/url-mapping-test.o $(TEST-DEFAULTOBJS) job/url-mapping.o util/configfile.o util/md5sum.o util/glibc-md5.o net/uri.o
	$(LD) -o job/url-mapping-test$(EXE) job/url-mapping-test.o $(TEST-DEFAULTOBJS) job/url-mapping.o util/configfile.o util/md5sum.o util/glibc-md5.o net/uri.o $(TEST-LDFLAGS)
# job/makeimagedl-info-test.cc:10
job/makeimagedl-info-test$(EXE): job/makeimagedl-info-test.o $(TEST-DEFAULTOBJS) job/makeimagedl-info.o net/uri.o
	$(LD) -o job/makeimagedl-info-test$(EXE) job/makeimagedl-info-test.o $(TEST-DEFAULTOBJS) job/makeimagedl-info.o net/uri.o $(TEST-LDFLAGS)
# job/jigdo-io-test.cc:11
job/jigdo-io-test$(EXE): job/jigdo-io-test.o $(TEST-DEFAULTOBJS) job/datasource.o util/gunzip.o util/configfile.o util/md5sum.o util/glibc-md5.o net/uri.o job/url-mapping.o
	$(LD) -o job/jigdo-io-test$(EXE) job/jigdo-io-test.o $(TEST-DEFAULTOBJS) job/datasource.o util/gunzip.o util/configfile.o util/md5sum.o util/glibc-md5.o net/uri.o job/url-mapping.o $(TEST-LDFLAGS) $(LIBS)
# net/proxyguess-test.cc:12
net/proxyguess-test$(EXE): net/proxyguess-test.o $(TEST-DEFAULTOBJS) 
	$(LD) -o net/proxyguess-test$(EXE) net/proxyguess-test.o $(TEST-DEFAULTOBJS)  $(TEST-LDFLAGS)
# net/uri-test.cc:10
net/uri-test$(EXE): net/uri-test.o $(TEST-DEFAULTOBJS) glibcurl/glibcurl.o net/uri.o compat.o
	$(LD) -o net/uri-test$(EXE) net/uri-test.o $(TEST-DEFAULTOBJS) glibcurl/glibcurl.o net/uri.o compat.o $(TEST-LDFLAGS) $(CURLLIBS) $(LDFLAGS_WINSOCK)
# util/string-utf-test.cc:12
util/string-utf-test$(EXE): util/string-utf-test.o $(TEST-DEFAULTOBJS) 
	$(LD) -o util/string-utf-test$(EXE) util/string-utf-test.o $(TEST-DEFAULTOBJS)  $(TEST-LDFLAGS)
# util/gunzip-test.cc:12
util/gunzip-test$(EXE): util/gunzip-test.o $(TEST-DEFAULTOBJS) util/gunzip.o
	$(LD) -o util/gunzip-test$(EXE) util/gunzip-test.o $(TEST-DEFAULTOBJS) util/gunzip.o $(TEST-LDFLAGS) $(LIBS)
# util/mimestream-test.cc:12
util/mimestream-test$(EXE): util/mimestream-test.o $(TEST-DEFAULTOBJS) 
	$(LD) -o util/mimestream-test$(EXE) util/mimestream-test.o $(TEST-DEFAULTOBJS)  $(TEST-LDFLAGS)
# util/autonullptr-test.cc:12
util/autonullptr-test$(EXE): util/autonullptr-test.o $(TEST-DEFAULTOBJS) 
	$(LD) -o util/autonullptr-test$(EXE) util/autonullptr-test.o $(TEST-DEFAULTOBJS)  $(TEST-LDFLAGS)
# util/log-test.cc:12
util/log-test$(EXE): util/log-test.o $(TEST-DEFAULTOBJS) 
	$(LD) -o util/log-test$(EXE) util/log-test.o $(TEST-DEFAULTOBJS)  $(TEST-LDFLAGS)
# util/md5sum-test.cc:12
util/md5sum-test$(EXE): util/md5sum-test.o $(TEST-DEFAULTOBJS) util/glibc-md5.o util/md5sum.o
	$(LD) -o util/md5sum-test$(EXE) util/md5sum-test.o $(TEST-DEFAULTOBJS) util/glibc-md5.o util/md5sum.o $(TEST-LDFLAGS)
# util/rsyncsum-test.cc:15
util/rsyncsum-test$(EXE): util/rsyncsum-test.o $(TEST-DEFAULTOBJS) util/rsyncsum.o
	$(LD) -o util/rsyncsum-test$(EXE) util/rsyncsum-test.o $(TEST-DEFAULTOBJS) util/rsyncsum.o $(TEST-LDFLAGS)
# util/configfile-test.cc:12
util/configfile-test$(EXE): util/configfile-test.o $(TEST-DEFAULTOBJS) util/configfile.o
	$(LD) -o util/configfile-test$(EXE) util/configfile-test.o $(TEST-DEFAULTOBJS) util/configfile.o $(TEST-LDFLAGS)
# jigdoconfig-test.cc:12
jigdoconfig-test$(EXE): jigdoconfig-test.o $(TEST-DEFAULTOBJS) jigdoconfig.o util/configfile.o
	$(LD) -o jigdoconfig-test$(EXE) jigdoconfig-test.o $(TEST-DEFAULTOBJS) jigdoconfig.o util/configfile.o $(TEST-LDFLAGS)

gtk/interface.o: gtk/interface.cc util/unistd-jigdo.h gtk/interface.hh gtk/gui.hh
gtk/treeiter.o: gtk/treeiter.cc gtk/treeiter.hh
gtk/gtk-makeimage.o: gtk/gtk-makeimage.cc jigdoconfig.hh job/makeimagedl.fh util/log.hh util/status.hh util/progress.fh util/debug.hh job/makeimage.hh util/ilist.hh net/download.hh net/uri.hh util/unistd-jigdo.h gtk/joblist.hh util/nocopy.hh gtk/interface.hh job/datasource.hh gtk/gui.hh util/bstream.hh util/smartptr.hh gtk/jobline.hh job/url-mapping.fh job/jigdo-io.fh job/url-mapping.hh util/md5sum.fh util/progress.hh util/md5sum.hh gtk/jobline.fh job/makeimagedl.hh util/configfile.hh job/job.hh util/string-utf.hh job/single-url.hh util/bstream-counted.hh util/string.hh gtk/gtk-makeimage.hh gtk/messagebox.hh gtk/gtk-single-url.hh
gtk/gtk-single-url.o: gtk/gtk-single-url.cc util/autoptr.hh job/job.hh util/bstream.hh job/makeimagedl.fh util/bstream-counted.hh util/progress.fh gtk/jobline.fh util/string-utf.hh util/string.hh util/log.hh net/download.hh util/ilist.hh gtk/joblist.hh job/datasource.hh util/nocopy.hh util/progress.hh gtk/interface.hh job/single-url.hh gtk/gui.hh util/unistd-jigdo.h gtk/jobline.hh util/smartptr.hh gtk/messagebox.hh util/debug.hh gtk/gtk-single-url.hh
gtk/gui.o: gtk/gui.cc job/job.hh util/bstream.hh job/makeimagedl.fh util/bstream-counted.hh util/progress.fh gtk/jobline.fh util/string-utf.hh util/string.hh util/log.hh net/download.hh util/ilist.hh gtk/joblist.hh job/datasource.hh util/nocopy.hh util/progress.hh gtk/interface.hh job/single-url.hh gtk/gui.hh util/unistd-jigdo.h gtk/jobline.hh util/smartptr.hh gtk/messagebox.hh util/debug.hh gtk/gtk-single-url.hh jigdoconfig.hh util/status.hh job/makeimage.hh net/uri.hh job/url-mapping.fh job/jigdo-io.fh job/url-mapping.hh util/md5sum.fh util/md5sum.hh job/makeimagedl.hh util/configfile.hh gtk/gtk-makeimage.hh
gtk/messagebox.o: gtk/messagebox.cc util/debug.hh util/unistd-jigdo.h gtk/interface.hh gtk/gui.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh gtk/messagebox.hh gtk/support.hh
gtk/jigdo.o: gtk/jigdo.cc util/unistd-jigdo.h util/debug.hh net/download.hh util/glibc-getopt.h net/glibwww.hh gtk/interface.hh gtk/gui.hh gtk/jobline.fh util/string-utf.hh util/string.hh util/log.hh gtk/joblist.hh util/nocopy.hh gtk/jobline.hh net/proxyguess.hh gtk/support.hh
gtk/support.o: gtk/support.cc util/unistd-jigdo.h gtk/support.hh
gtk/jobline.o: gtk/jobline.cc jigdoconfig.hh job/makeimagedl.fh util/log.hh util/status.hh util/progress.fh util/debug.hh job/makeimage.hh util/ilist.hh net/download.hh net/uri.hh util/unistd-jigdo.h gtk/joblist.hh util/nocopy.hh gtk/interface.hh job/datasource.hh gtk/gui.hh util/bstream.hh util/smartptr.hh gtk/jobline.hh job/url-mapping.fh job/jigdo-io.fh job/url-mapping.hh util/md5sum.fh util/progress.hh util/md5sum.hh gtk/jobline.fh job/makeimagedl.hh util/configfile.hh job/job.hh util/string-utf.hh job/single-url.hh util/bstream-counted.hh util/string.hh gtk/gtk-makeimage.hh gtk/messagebox.hh gtk/gtk-single-url.hh
gtk/joblist.o: gtk/joblist.cc util/unistd-jigdo.h gtk/jobline.fh util/string-utf.hh util/string.hh util/log.hh util/debug.hh net/download.hh gtk/joblist.hh util/nocopy.hh gtk/interface.hh gtk/gui.hh gtk/jobline.hh gtk/support.hh gtk/treeiter.hh
job/makeimagedl-info.o: job/makeimagedl-info.cc util/unistd-jigdo.h compat.hh util/debug.hh util/configfile.hh jigdoconfig.hh job/url-mapping.hh util/md5sum.hh job/job.hh util/bstream-counted.hh job/makeimagedl.fh net/download.hh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh util/smartptr.hh job/jigdo-io.fh util/progress.hh job/single-url.hh util/status.hh job/makeimage.hh net/uri.hh util/bstream.hh util/string.hh job/url-mapping.fh util/md5sum.fh util/log.hh job/makeimagedl.hh
job/jigdodownload.o: job/jigdodownload.cc util/log.hh jigdoconfig.hh util/bstream-counted.hh job/makeimagedl.hh util/status.hh job/makeimagedl.fh job/makeimage.hh net/download.hh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh util/debug.hh job/datasource.hh net/uri.hh util/smartptr.hh util/bstream.hh job/jigdo-io.fh util/string.hh util/progress.hh job/url-mapping.hh job/url-mapping.fh util/configfile.hh util/md5sum.hh util/md5sum.fh job/single-url.hh job/job.hh job/jigdodownload.hh
job/jigdo-io.o: job/jigdo-io.cc util/debug.hh util/configfile.hh job/url-mapping.fh util/smartptr.hh job/job.hh util/md5sum.fh util/progress.hh job/makeimagedl.fh job/makeimagedl.hh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh job/single-url.hh util/gunzip.hh jigdoconfig.hh job/jigdo-io.fh util/status.hh job/url-mapping.hh job/makeimage.hh util/md5sum.hh net/uri.hh util/bstream-counted.hh util/string.hh util/bstream.hh net/download.hh util/log.hh job/jigdo-io.hh util/mimestream.hh
job/makeimage.o: job/makeimage.cc util/nocopy.hh util/debug.hh util/configfile.hh jigdoconfig.hh job/makeimage.hh
job/url-mapping.o: job/url-mapping.cc util/unistd-jigdo.h compat.hh util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh net/uri.hh util/md5sum.hh util/smartptr.hh util/status.hh job/url-mapping.fh util/bstream.hh util/md5sum.fh job/url-mapping.hh
job/url-mapping-test.o: job/url-mapping-test.cc util/debug.hh util/configfile.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh net/uri.hh util/md5sum.hh util/smartptr.hh util/status.hh job/url-mapping.fh util/bstream.hh util/md5sum.fh job/url-mapping.hh
job/makeimagedl-info-test.o: job/makeimagedl-info-test.cc util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh job/url-mapping.hh util/md5sum.hh job/job.hh util/bstream-counted.hh job/makeimagedl.fh net/download.hh util/progress.fh util/ilist.hh job/datasource.hh util/smartptr.hh job/jigdo-io.fh util/progress.hh util/configfile.hh job/single-url.hh jigdoconfig.hh util/status.hh job/makeimage.hh net/uri.hh util/bstream.hh job/url-mapping.fh util/md5sum.fh job/makeimagedl.hh
job/datasource.o: job/datasource.cc util/debug.hh util/string.hh util/log.hh job/job.hh job/makeimagedl.fh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh
job/makeimagedl.o: job/makeimagedl.cc util/unistd-jigdo.h util/log.hh job/job.hh job/makeimagedl.fh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh util/progress.hh util/debug.hh util/bstream.hh util/string.hh job/cached-url.hh compat.hh job/url-mapping.fh util/smartptr.hh util/md5sum.fh job/makeimagedl.hh util/configfile.hh job/single-url.hh util/gunzip.hh jigdoconfig.hh job/jigdo-io.fh util/status.hh job/url-mapping.hh job/makeimage.hh util/md5sum.hh net/uri.hh util/bstream-counted.hh net/download.hh job/jigdo-io.hh util/mimestream.hh
job/cached-url.o: job/cached-url.cc util/unistd-jigdo.h util/autoptr.hh util/log.hh job/job.hh job/makeimagedl.fh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh util/progress.hh util/debug.hh util/bstream.hh util/string.hh job/cached-url.hh
job/jigdo-io-test.o: job/jigdo-io-test.cc job/url-mapping.hh util/md5sum.hh job/job.hh util/bstream-counted.hh job/makeimagedl.fh net/download.hh util/progress.fh util/string-utf.hh util/nocopy.hh util/ilist.hh job/datasource.hh util/smartptr.hh job/jigdo-io.fh util/progress.hh util/configfile.hh job/single-url.hh jigdoconfig.hh util/status.hh job/makeimage.hh util/debug.hh net/uri.hh util/bstream.hh util/string.hh job/url-mapping.fh util/md5sum.fh util/log.hh job/makeimagedl.hh util/gunzip.hh job/jigdo-io.hh util/mimestream.hh job/jigdo-io.cc
job/single-url.o: job/single-url.cc util/autoptr.hh util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/bstream-counted.hh job/job.hh job/makeimagedl.fh util/progress.fh util/ilist.hh job/datasource.hh net/download.hh util/smartptr.hh util/progress.hh util/bstream.hh job/single-url.hh
net/download-test.o: net/download-test.cc 
net/libwww-HTFTP.o: net/libwww-HTFTP.c 
net/libwww-HTHost.o: net/libwww-HTHost.c 
net/glibwww-init.o: net/glibwww-init.cc net/glibwww.hh
net/glibwww-callbacks.o: net/glibwww-callbacks.cc net/glibwww.hh
net/glibwww-trans.o: net/glibwww-trans.cc net/glibwww.hh
net/proxyguess-test.o: net/proxyguess-test.cc util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh glibcurl/glibcurl.h net/proxyguess.hh util/unistd-jigdo.h net/proxyguess.cc
net/uri.o: net/uri.cc net/uri.hh util/unistd-jigdo.h compat.hh util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
net/proxyguess.o: net/proxyguess.cc util/unistd-jigdo.h glibcurl/glibcurl.h util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh net/proxyguess.hh
net/download.o: net/download.cc util/debug.hh net/download.hh glibcurl/glibcurl.h util/configfile.hh jigdoconfig.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
net/uri-test.o: net/uri-test.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh net/uri.hh
util/string-utf-test.o: util/string-utf-test.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh
util/rsyncsum.o: util/rsyncsum.cc util/debug.hh util/bstream.hh serialize.hh util/rsyncsum.hh util/mimestream.hh util/rsyncsum.ih
util/gunzip-test.o: util/gunzip-test.cc util/debug.hh util/bstream.hh util/gunzip.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
util/string.o: util/string.cc util/debug.hh util/string.hh
util/mimestream-test.o: util/mimestream-test.cc util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/mimestream.hh
util/configfile.o: util/configfile.cc util/debug.hh util/configfile.hh util/string.hh
util/md5sum.o: util/md5sum.cc util/glibc-md5.hh util/debug.hh util/bstream.hh util/md5sum.fh util/md5sum.hh util/mimestream.hh util/md5sum.ih
util/glibc-getopt1.o: util/glibc-getopt1.c util/glibc-getopt.h
util/string-utf.o: util/string-utf.cc util/debug.hh util/string.hh util/string-utf.hh
util/progress.o: util/progress.cc util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/progress.fh util/progress.hh
util/autonullptr-test.o: util/autonullptr-test.cc util/string-utf.hh util/nocopy.hh util/ilist.hh util/string.hh util/log.hh util/debug.hh util/autonullptr.hh
util/gunzip.o: util/gunzip.cc util/debug.hh util/gunzip.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
util/random.o: util/random.cc util/debug.hh util/bstream.hh util/md5sum.fh util/md5sum.hh
util/log-test.o: util/log-test.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh
util/debug.o: util/debug.cc util/unistd-jigdo.h util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
util/log.o: util/log.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh
util/md5sum-test.o: util/md5sum-test.cc util/debug.hh util/bstream.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/md5sum.fh util/md5sum.hh util/mimestream.hh
util/rsyncsum-test.o: util/rsyncsum-test.cc util/debug.hh util/bstream.hh serialize.hh util/rsyncsum.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
util/glibc-getopt.o: util/glibc-getopt.c util/glibc-getopt.h util/unistd-jigdo.h
util/configfile-test.o: util/configfile-test.cc util/debug.hh util/configfile.hh
util/glibc-md5.o: util/glibc-md5.cc util/debug.hh util/bstream.hh util/md5sum.fh util/md5sum.hh util/glibc-md5.hh
util/checkpoint.o: util/checkpoint.cc util/checkpoint.hh
util/bstream.o: util/bstream.cc util/debug.hh util/bstream.hh
compat.o: compat.cc util/unistd-jigdo.h compat.hh util/debug.hh
scan.o: scan.cc util/unistd-jigdo.h util/debug.hh util/bstream.hh compat.hh util/configfile.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/md5sum.fh util/md5sum.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/status.hh cachefile.hh scan.hh
glibcurl/glibcurl.o: glibcurl/glibcurl.c glibcurl/glibcurl.h
glibcurl/glibcurl-example.o: glibcurl/glibcurl-example.c glibcurl/glibcurl.h
zstream-bz.o: zstream-bz.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh util/bstream.hh util/md5sum.fh util/md5sum.hh serialize.hh zstream.fh zstream.hh zstream-bz.hh
zstream-gz.o: zstream-gz.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh util/bstream.hh util/md5sum.fh util/md5sum.hh serialize.hh zstream.fh zstream.hh zstream-gz.hh
torture.o: torture.cc util/unistd-jigdo.h util/debug.hh util/bstream.hh util/configfile.hh jigdoconfig.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/md5sum.fh util/md5sum.hh util/mimestream.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/status.hh cachefile.hh scan.hh mkimage.hh partialmatch.hh compat.hh jigdoconfig.fh zstream.fh mktemplate.hh dirent.hh recursedir.hh
jigdoconfig-test.o: jigdoconfig-test.cc util/debug.hh util/configfile.hh jigdoconfig.hh
partialmatch.o: partialmatch.cc partialmatch.hh compat.hh util/configfile.hh jigdoconfig.fh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/md5sum.fh util/md5sum.hh serialize.hh util/rsyncsum.hh util/debug.hh scan.fh util/bstream.hh zstream.fh util/unistd-jigdo.h mktemplate.hh recursedir.fh util/status.hh cachefile.hh scan.hh partialmatch.ih
jigdoconfig.o: jigdoconfig.cc util/debug.hh util/configfile.hh jigdoconfig.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh
recursedir-test.o: recursedir-test.cc util/debug.hh recursedir.hh util/string.hh recursedir.fh dirent.hh util/unistd-jigdo.h recursedir.cc util/nocopy.hh util/log.hh util/string-utf.hh util/debug.cc
mkimage.o: mkimage.cc util/unistd-jigdo.h compat.hh util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh util/md5sum.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/status.hh cachefile.hh scan.hh util/bstream.hh util/md5sum.fh mkimage.hh zstream.fh zstream.hh zstream-gz.hh
recursedir.o: recursedir.cc recursedir.fh dirent.hh util/unistd-jigdo.h util/debug.hh recursedir.hh util/string.hh
mkjigdo.o: mkjigdo.cc util/debug.hh util/configfile.hh jigdoconfig.hh util/bstream.hh util/md5sum.fh util/md5sum.hh util/mimestream.hh partialmatch.hh compat.hh jigdoconfig.fh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh serialize.hh util/rsyncsum.hh scan.fh zstream.fh util/unistd-jigdo.h mktemplate.hh recursedir.fh util/status.hh cachefile.hh scan.hh
cachefile.o: cachefile.cc util/debug.hh util/status.hh cachefile.hh util/unistd-jigdo.h compat.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/bstream.hh serialize.hh
mktemplate.o: mktemplate.cc util/autoptr.hh util/unistd-jigdo.h compat.hh util/debug.hh util/string-utf.hh util/nocopy.hh util/string.hh util/log.hh util/mimestream.hh util/md5sum.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/status.hh cachefile.hh scan.hh util/bstream.hh util/md5sum.fh mkimage.hh partialmatch.hh util/configfile.hh jigdoconfig.fh zstream.fh mktemplate.hh zstream.hh zstream-gz.hh zstream-bz.hh
jigdo-file-cmd.o: jigdo-file-cmd.cc util/unistd-jigdo.h compat.hh util/debug.hh util/bstream.hh jigdoconfig.fh util/md5sum.fh util/string-utf.hh util/md5sum.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/nocopy.hh util/string.hh util/log.hh util/status.hh zstream.fh cachefile.hh scan.hh mktemplate.hh mkimage.hh util/configfile.hh partialmatch.hh jigdoconfig.hh jigdo-file-cmd.hh util/mimestream.hh dirent.hh recursedir.hh
jigdo-file.o: jigdo-file.cc util/glibc-getopt.h util/unistd-jigdo.h compat.hh util/debug.hh util/configfile.hh util/bstream.hh jigdoconfig.fh util/md5sum.fh util/string-utf.hh util/md5sum.hh recursedir.fh serialize.hh util/rsyncsum.hh scan.fh util/nocopy.hh util/string.hh util/log.hh util/status.hh zstream.fh cachefile.hh scan.hh mktemplate.hh mkimage.hh partialmatch.hh jigdoconfig.hh jigdo-file-cmd.hh dirent.hh recursedir.hh
zstream.o: zstream.cc util/string-utf.hh util/debug.hh util/nocopy.hh util/string.hh util/log.hh util/bstream.hh util/md5sum.fh util/md5sum.hh serialize.hh zstream.fh zstream.hh zstream-gz.hh zstream-bz.hh
jigdo-0.7.3/src/Makefile.in0000644000175000017500000002237010262777000015270 0ustar  richardrichard# Project: Jigdo (Jigsaw download)
#  __   _
#  |_) /|  Copyright (C) 2001-2004  |  richard@
#  | \/¯|  Richard Atterer          |  atterer.net
#  ¯ '` ¯
srcdir =	@srcdir@
VPATH =		@srcdir@

prefix =	@prefix@
datadir =	@datadir@

SUBDIRS =	glibcurl gtk job net util
SUBINCLUDE =	-I"$(srcdir)/glibcurl" -I"$(srcdir)/gtk" -I"$(srcdir)/job" \
		-I"$(srcdir)/net" -I"$(srcdir)/util"

# Prevent these variables from being taken from the environment
X =
Y =

# -I. to pick up config.h, _FILE_OFFSET_BITS for big file support under Linux
CPPFLAGS =	@CPPFLAGS@ -I. -I"$(srcdir)" $(SUBINCLUDE) \
		-D_FILE_OFFSET_BITS=64 @DEFS@ \
		-DPACKAGE_DATA_DIR="\"$(datadir)/jigdo/\"" \
		-DPACKAGE_LOCALE_DIR="\"$(datadir)/locale\"" \
		$(GTKCFLAGS) $(CURLCFLAGS) # $(LIBWWWCFLAGS)
CC =		@CC@
CFLAGS =	@CFLAGS@ $(X)
CXX =		@CXX@
CXXFLAGS =	@CXXFLAGS@ $(X)
LD =		@CXX@
LD_C =		@CC@
LDFLAGS =	@LIBS@ @LDFLAGS@ $(Y)
LIBS =		@LIBS@
AWK =		@AWK@
GLADECODE =	glade-2 -w # Create code from .glade file
DOC =		doxygen
WINDRES =	windres
GTKCFLAGS =	@GTKCFLAGS@
GTKLIBS =	@GTKLIBS@
GLIBLIBS =	@GLIBLIBS@
#LIBWWWCFLAGS =	@LIBWWWCFLAGS@
#LIBWWWLIBS =	@LIBWWWLIBS@
CURLCFLAGS =	@CURLCFLAGS@
CURLLIBS =	@CURLLIBS@

programs =	jigdo-file@exe@ @IF_GUI@ jigdo@exe@
debug-programs = torture@exe@ util/random@exe@ \
		@IF_GUI@ glibcurl/glibcurl-example@exe@
#libwww-hacks =	@IF_LIBWWW_HACKS@ net/libwww-HTFTP.o net/libwww-HTHost.o
windows-res =	@IF_WINDOWS@ jigdo.res
test-programs =	job/jigdo-io-test@exe@ \
		job/makeimagedl-info-test@exe@ job/url-mapping-test@exe@ \
		net/proxyguess-test@exe@ net/uri-test@exe@ \
		util/autonullptr-test@exe@ util/rsyncsum-test@exe@ \
		util/gunzip-test@exe@ util/log-test@exe@ \
		util/md5sum-test@exe@ util/mimestream-test@exe@ \
		util/string-utf-test@exe@

# fmt -s -w1|sed 's%[^a-zA-Z0-9./-]\+%%g'|sort|fmt -w60|sed 's%$% \\%'
objects-jigdo =	compat.o glibcurl/glibcurl.o gtk/gtk-makeimage.o \
		gtk/gtk-single-url.o gtk/gui.o gtk/interface.o gtk/jigdo.o \
		gtk/jobline.o gtk/joblist.o gtk/messagebox.o gtk/support.o \
		gtk/treeiter.o jigdoconfig.o job/cached-url.o \
		job/datasource.o job/jigdo-io.o job/makeimage.o \
		job/makeimagedl-info.o \
		job/makeimagedl.o job/single-url.o \
		job/url-mapping.o net/download.o net/uri.o net/proxyguess.o \
		util/bstream.o util/configfile.o util/glibc-getopt.o \
		util/glibc-getopt1.o util/glibc-md5.o util/gunzip.o \
		util/log.o util/md5sum.o util/progress.o util/string-utf.o \
		$(windows-res) \
		util/debug.o # this must come last!
#^ net/glibwww-callbacks.o net/glibwww-init.o
objects-jigdo-file = cachefile.o compat.o jigdo-file-cmd.o jigdo-file.o \
		jigdoconfig.o mkimage.o mkjigdo.o mktemplate.o \
		partialmatch.o recursedir.o scan.o util/bstream.o \
		util/configfile.o util/glibc-getopt.o util/glibc-getopt1.o \
		util/glibc-md5.o util/log.o util/md5sum.o util/rsyncsum.o \
		util/string.o zstream.o zstream-bz.o zstream-gz.o \
		util/debug.o # this must come last!
objects-torture = cachefile.o compat.o jigdoconfig.o mkimage.o mkjigdo.o \
		mktemplate.o partialmatch.o recursedir.o scan.o torture.o \
		util/bstream.o util/configfile.o util/glibc-md5.o \
		util/log.o util/md5sum.o util/rsyncsum.o util/string.o \
		zstream.o zstream-bz.o zstream-gz.o \
		util/debug.o # this must come last!
objects-random = util/glibc-md5.o util/log.o util/md5sum.o util/random.o \
		util/string.o \
		util/debug.o # this must come last!
#______________________________

.SUFFIXES:
.SUFFIXES: .c .cc .h .o
.cc.o:
		-@echo $(CXX) '$$cxx' -c $< -o $@
		@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c "$<" -o "$@"
.c.o:
		-@echo $(CC) '$$c  ' -c $< -o $@
		@$(CC) $(CPPFLAGS) $(CFLAGS) -c "$<" -o "$@"
#______________________________________________________________________

.PHONY:         all all-msg clean distclean mostlyclean maintainer-clean \
                dep depend doc strip test test-v test-c
all:		all-msg Makefile $(programs) @IF_DEBUG@ $(debug-programs) \
		@IFNOT_GXX2@ test-c @IFNOT_CROSSCOMPILING@ test
all-msg:
		-@if test "$$EMACS" != t; then \
		    echo "export cpp='$(CPPFLAGS)'"; \
		    echo 'export cxx="$$cpp $(CXXFLAGS)"'; \
		    echo 'export c="$$cpp $(CFLAGS)"'; \
		fi
check:		$(programs) torture@exe@
		@echo 
		@echo '*** Warning - this will take a few minutes and ~100MB'
		@echo '*** disk space, and will bash the disc quite a bit...'
		@echo && sleep 3
		mkdir ironmaiden || true
		./torture 0 4
		./torture 128 132
		./torture 256 260
clean mostlyclean:
		for d in . $(SUBDIRS); do \
		    rm -f $$d/*.o $$d/core; \
		    for p in $$d/*test; do if test -x "$$p" -a -f \
		        "$(srcdir)/$$d/$$p.cc"; then rm -f "$$p"; fi; done; \
		done
		rm -f gtk/interface.hh.tmp gtk/gui.cc.tmp gtk/gui.hh.tmp
		rm -f $(programs) $(debug-programs) $(test-programs)
		rm -rf apidoc mktemplate-testdir
distclean:	clean
		for d in . $(SUBDIRS); do \
		    rm -f $$d/TAGS $$d/*~ $$d/\#*\# $$d/*.bak; \
		    rm -f $$d/Makefile $$d/config.h; \
		done
		rm -rf html
		if test ! -h ironmaiden; then rm -rf ironmaiden; fi
doc:
		$(DOC)
maintainer-clean: distclean
		rm -f gtk/interface.cc gtk/interface.hh
		rm -f Makedeps


make-makedeps =	(cd "$(srcdir)" && find . -type f \
		    '(' -name '*.cc' -o -name '*.c' ')') \
		| xargs $(AWK) -f "$(srcdir)/../scripts/depend.awk" \
		    "$(srcdir)" $(SUBDIRS) -
# No dependency - don't normally remake Makedeps on gtk/interface.cc remake
# (Touching Makedeps/Makefile necessary, or circ. dep. Makefile->Makedeps.)
Makedeps:
		if test ! -r gtk/interface.cc; then \
		    touch "$(srcdir)/Makedeps" Makefile; \
		    $(MAKE) gtk/interface.cc; \
		fi
		$(make-makedeps)
# Dependency ensures auto-generated files are there & appear in depend output
dep depend:	gtk/interface.cc
		$(make-makedeps)
		@if test "$(srcdir)/Makedeps" -nt Makefile; then \
		    echo "cd .. && sh config.status"; \
		    cd .. && sh config.status; \
		fi
strip:
		for p in $(programs); do if test -f "$$p"; then \
		    strip -R .comment -R .note "$$p"; fi; done

TEST-DEFAULTOBJS = util/log.o util/string-utf.o util/debug.o
TEST-LDFLAGS =	@LDFLAGS@ $(GLIBLIBS)
EXE =@exe@
# Compile only
test-c:		$(test-programs)
# Compile and run
test:		$(test-programs) jigdo-file@exe@ util/random@exe@
		@echo "Running unit tests..."; \
		for p in $(test-programs); do \
		    if "$$p"; then continue; fi; \
		    echo "$$p failed, for details enter: $$p all"; exit 1; \
		done; \
		testscripts="`cd $(srcdir) && echo *-test*.sh`"; \
		export srcdir="$(srcdir)"; \
		for p in $$testscripts; do \
		    if sh "$(srcdir)/$$p"; then continue; fi; \
		    echo "$$p failed"; exit 1; \
		done; \
		set $(test-programs) $$testscripts; \
		    echo "All $$# tests succeeded"
# Compile and run, re-run in verbose mode after error
test-v:		$(test-programs) jigdo-file@exe@ util/random@exe@
		@echo "Running unit tests..."; \
		for p in $(test-programs); do \
		    if "$$p"; then echo "OK: $$p"; continue; fi; \
		    echo "$$p failed"; "$$p" all; exit 1; \
		done; \
		testscripts="`cd $(srcdir) && echo *-test*.sh`"; \
		export srcdir="$(srcdir)"; \
		for p in $$testscripts; do \
		    if sh "$(srcdir)/$$p"; then echo "OK: $$p"; continue; fi; \
		    echo "$$p failed"; sh "$(srcdir)/$$p" all; exit 1; \
		done; \
		set $(test-programs) $$testscripts; \
		    echo "All $$# tests succeeded"

config.h:	$(srcdir)/../jigdo.spec
		rm -f config.h
		@echo "jigdo.spec has changed - rerun the configure script!"; \
		exit 1
Makefile:	Makefile.in Makedeps config.h.in
		cd .. && sh config.status # update Makefile from Makefile.in
#______________________________

jigdo32.ico:	../gfx/jigdo32.ico
		ln -s "$<" . || cp "$<" .

jigdo.res:	jigdo.rc jigdo32.ico
		$(WINDRES) "$<" -O coff -o "$@"
#______________________________

jigdo@exe@:	$(objects-jigdo)
		$(LD) -o $@ $(objects-jigdo) $(LDFLAGS) $(GTKLIBS) \
		    $(CURLLIBS) @IF_WINDOWS@ -lws2_32 @IFNOT_DEBUG@ -mwindows
glibcurl/glibcurl-example@exe@:	glibcurl/glibcurl-example.o \
		    glibcurl/glibcurl.o
		$(LD_C) -o $@ glibcurl/glibcurl-example.o \
		    glibcurl/glibcurl.o $(LDFLAGS) $(GTKLIBS) $(CURLLIBS) \
		    @IF_WINDOWS@ -lws2_32
jigdo-file@exe@: $(objects-jigdo-file)
		$(LD) -o $@ $(objects-jigdo-file) $(LDFLAGS)
torture@exe@:	$(objects-torture)
		$(LD) -o $@ $(objects-torture) $(LDFLAGS)
util/random@exe@: $(objects-random)
		$(LD) -o $@ $(objects-random) $(LDFLAGS)
mimestreamtest@exe@: mimestreamtest.o util/debug.o
		$(LD) -o $@ mimestreamtest.o util/log.o util/string-utf.o \
		    util/debug.o $(LDFLAGS) $(GTKLIBS)
string-utftest@exe@: util/debug.o util/string-utf.o util/string-utftest.o
		$(LD) -o $@ util/string-utf.o util/string-utftest.o \
		    util/debug.o $(LDFLAGS) $(GTKLIBS)
gunziptest@exe@: util/gunzip.o util/gunziptest.o util/debug.o
		$(LD) -o $@ util/gunzip.o util/gunziptest.o util/debug.o \
		    $(LDFLAGS)
logtest@exe@:	util/debug.o util/log.o util/logtest.o util/string-utf.o
		$(LD) -o $@ util/log.o util/logtest.o util/string-utf.o \
		    util/debug.o $(LDFLAGS) $(GTKLIBS)

gtk/interface.cc gtk/interface.hh: $(srcdir)/../jigdo.glade \
			$(srcdir)/../scripts/glade-filter.awk
		cd $(srcdir)/.. \
		    && $(GLADECODE) "jigdo.glade"
		$(AWK) -f "$(srcdir)/../scripts/glade-filter.awk" \
		    "$(srcdir)/gtk/interface"
		-ln -sf "$(srcdir)/gtk/interface.cc" gtk/interface.cc
		cd $(srcdir) && rm -f gtk/gui.cc.tmp gtk/gui.hh.tmp \
		    gtk/interface.cc.tmp gtk/interface.hh.tmp
#______________________________________________________________________

LDFLAGS_WINSOCK = @IF_WINDOWS@ -lws2_32

@SRC_MAKEDEPS@
jigdo-0.7.3/src/cachefile.cc0000644000175000017500000001635210267703637015452 0ustar  richardrichard/* $Id: cachefile.cc,v 1.8 2005/07/21 11:31:43 atterer Exp $ -*- C++ -*-
  __   _
  |_) /|  Copyright (C) 2001-2003  |  richard@
  | \/¯|  Richard Atterer          |  atterer.net
  ¯ '` ¯
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License, version 2. See
  the file COPYING for details.

  Cache with MD5 sums of file contents - used by JigdoCache in scan.hh

*/

#include 

#include 
#include 
#if HAVE_LIBDB

#if DEBUG
#  include 
#endif
#include 
#include  /* time() */

#include 
#include 
#include 
//______________________________________________________________________

DEBUG_UNIT("cachefile")

CacheFile::CacheFile(const char* dbName) {
  memset(&data, 0, sizeof(DBT));

  int e = db_create(&db, 0, 0); // No env/flags
  if (e != 0) throw DbError(e);

  // Cache of 0GB+4MB, one contiguous chunk
  db->set_cachesize(db, 0, 4*1024*1024, 1);

  // Use a btree, create database file if not yet present
  e = compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE, DB_CREATE,
                    0666);
  if (e != 0) {
    // Re-close, in case it is necessary
    db->close(db, 0);
    if (e != DB_OLD_VERSION && e != DB_RUNRECOVERY)
      throw DbError(e);
    /* If the DB file is old or corrupted, just regenerate it from
       scratch, otherwise throw error. */
    debug("Cache file corrupt, recreating it");
    if (compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE,
                      DB_CREATE | DB_TRUNCATE, 0666) != 0)
      throw DbError(e);
  }

  data.flags |= DB_DBT_REALLOC;
}
//______________________________________________________________________

namespace {

  /** Local struct: Wrapper which calls close() for any DBC cursor at end of
      scope */
  struct AutoCursor {
    AutoCursor() : c(0) { }
    ~AutoCursor() { close(); }
    int close() {
      if (c == 0) return 0;
      int r = c->c_close(c);
      c = 0;
      return r;
    }
    int get(DBT *key, DBT *data, u_int32_t flags) {
      return c->c_get(c, key, data, flags);
    }
    int put(DBT *key, DBT *data, u_int32_t flags) {
      return c->c_put(c, key, data, flags);
    }
    int del(u_int32_t flags) {
      return c->c_del(c, flags);
    }
    DBC* c;
  };

}
//________________________________________

Status CacheFile::find(const byte*& resultData, size_t& resultSize,
                       const string& fileName, uint64 fileSize, time_t mtime) {
  DBT key; memset(&key, 0, sizeof(DBT));
  key.data = const_cast(fileName.c_str());
  key.size = fileName.size();

  AutoCursor cursor;
  // Cursor with no transaction id, no flags
  if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;

  if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
      || data.data == 0) return FAILED;

  // Check whether mtime and size matches
  Paranoid(data.size >= USER_DATA);
  byte* d = static_cast(data.data);
  Paranoid(d != 0);
  time_t cacheMtime;
  unserialize4(cacheMtime, d + MTIME);
  if (cacheMtime != mtime) return FAILED;
  uint64 cacheFileSize;
  unserialize6(cacheFileSize, d + SIZE);
  if (cacheFileSize != fileSize) return FAILED;

  // Match - update access time
  time_t now = time(0);
  Paranoid(now != static_cast(-1));
  serialize4(now, d + ACCESS);
  DBT partial; memset(&partial, 0, sizeof(DBT));
  partial.data = d + ACCESS;
  partial.size = 4;
  partial.flags |= DB_DBT_PARTIAL;
  partial.doff = ACCESS;
  partial.dlen = 4;
  //cerr << "CacheFile lookup successfull for "<(fileName.c_str());
  key.size = fileName.size();

  AutoCursor cursor;
  // Cursor with no transaction id, no flags
  if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;

  if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
      || data.data == 0) return FAILED;

  // get mtime and size
  Paranoid(data.size >= USER_DATA);
  byte* d = static_cast(data.data);
  Paranoid(d != 0);
  time_t cacheMtime;
  unserialize4(cacheMtime, d + MTIME);
  resultMtime = cacheMtime;
  uint64 cacheFileSize;
  unserialize6(cacheFileSize, d + SIZE);
  resultFileSize = cacheFileSize;

  // Match - update access time
  time_t now = time(0);
  Paranoid(now != static_cast(-1));
  serialize4(now, d + ACCESS);
  DBT partial; memset(&partial, 0, sizeof(DBT));
  partial.data = d + ACCESS;
  partial.size = 4;
  partial.flags |= DB_DBT_PARTIAL;
  partial.doff = ACCESS;
  partial.dlen = 4;
  //cerr << "CacheFile lookup successfull for "<cursor(db, 0, &cursor.c, 0) != 0) return;

  int status;
  while ((status = cursor.get(&key, &data, DB_NEXT)) == 0) {
    time_t lastAccess = 0;
    // If data.data == 0, expire entry by leaving lastAccess at 0
    if (data.data != 0)
      unserialize4(lastAccess, static_cast(data.data) + ACCESS);
    // Same as 'if (lastAccess(t - lastAccess) > 0) {
      debug("Cache: expiring %1",
            string(static_cast(key.data), key.size));
      cursor.del(0);
    }
  }
  if (status != DB_NOTFOUND)
    throw DbError(status);
}
//______________________________________________________________________

/* Prepare for an insertion of data, by allocating a sufficient amount
   of memory and returning a pointer to it. */
byte* CacheFile::insert_prepare(size_t inSize) {
  // Allocate enough memory for the new entry
  void* tmp = realloc(data.data, USER_DATA + inSize);
  if (tmp == 0) throw bad_alloc();
  data.data = tmp;
  data.size = USER_DATA + inSize;
  return static_cast(tmp) + USER_DATA;
}

/* ASSUMES THAT insert_prepare() HAS JUST BEEN CALLED and that the
   data had been copied to the memory region it returned. This
   function commits the data to the db. */
void CacheFile::insert_perform(const string& fileName, time_t mtime,
                               uint64 fileSize) {
  byte* buf = static_cast(data.data);

  // Write our data members
  time_t now = time(0);
  serialize4(now, buf + ACCESS);
  serialize4(mtime, buf + MTIME);
  serialize6(fileSize, buf + SIZE);

  // Insert in database
  DBT key; memset(&key, 0, sizeof(DBT));
  key.data = const_cast(fileName.c_str());
  key.size = fileName.size();
  db->put(db, 0, &key, &data, 0); // No transaction, overwrite

//   cerr << "CacheFile write `"<
  Size Meaning
   4   lastAccess - timestamp of last read or write access to this data
   4   fileMtime - timestamp to detect modifications, and for entry expiry
   6   fileSize - for calculation of nr of blocks, and for entry expiry
This is not handled by CacheFile; it is passed as an opaque string of bytes to scan.hh classes:
   4   blockLength (of rsync sum)
   4   md5BlockLength
   4   blocks (number of valid md5 blocks in this entry)
   8   rsyncSum of file start (only valid if blocks > 0)
  16   fileMD5Sum (only valid if
                   blocks == (fileSize+md5BlockLength-1)/md5BlockLength )
  followed by n entries:
  16   md5sum of block of size md5BlockLength
Why is mtime and size not part of the key? Because we only want to store one entry per file, not an additional entry whenever the file is changed. */ #ifndef CACHEFILE_HH #define CACHEFILE_HH #include #include #include /* for time_t */ #if HAVE_LIBDB #include #include /* free() */ #include /* memcpy(), memset() */ #include #include //______________________________________________________________________ /** libdb errors */ struct DbError : public Error { explicit DbError(int c) : Error(db_strerror(c)), code(c) { } DbError(int c, const string& m) : Error(m), code(c) { } DbError(int c, const char* m) : Error(m), code(c) { } int code; }; //______________________________________________________________________ /** Cache with MD5 sums of file contents */ class CacheFile { public: /** Create new database or open existing database */ CacheFile(const char* dbName); inline ~CacheFile(); /** Look for an entry in the database which matches the specified filename (which must be absolute), file modification time and file size. If no entry is found, return FAILED. Otherwise, return OK and overwrite resultData/resultSize with ptr/len of the binary string associated with this file. The first byte of resultData is the first byte of the "blockLength" entry (see start of this file). The result pointer is only valid until the next database operation. */ Status find(const byte*& resultData, size_t& resultSize, const string& fileName, uint64 fileSize, time_t mtime); /** Look for an entry in the database which matches the specified filename (which must be absolute). If no entry is found, return FAILED. Otherwise, return OK and overwrite resultData/resultSize with ptr/len of the binary string associated with this file. The first byte of resultData is the first byte of the "blockLength" entry (see start of this file). The result pointer is only valid until the next database operation. */ Status findName(const byte*& resultData, size_t& resultSize, const string& fileName, off_t& resultFileSize, time_t& resultMtime); /** Insert/overwrite entry for the given file (name must be absolute, file must have the supplied mtime and size). The data for the entry is supplied in inData. */ inline void insert(const byte* inData, size_t inSize, const string& fileName, time_t mtime, uint64 fileSize); /** As above, but data is created by the supplied functor object, which must have the method 'void operator()(byte* x)' defined, which when called must write inSize bytes to the memory at x. */ template inline void insert(Functor f, size_t inSize, const string& fileName, time_t mtime, uint64 fileSize); /** Remove all entries from the database that have a "last access" time that is older than the given time. */ void expire(time_t t); private: // Don't copy explicit inline CacheFile(const CacheFile&); inline CacheFile& operator=(const CacheFile&); byte* insert_prepare(size_t inSize); void insert_perform(const string& fileName, time_t mtime, uint64 fileSize); /* Byte offsets of first members of a cache entry (see start of this file). Defining a struct with byte members would be more convenient, but would also be a recipe for disaster because of alignment problems. (E.g., a RISC machine might pad to the next multiple of 4 bytes after every byte member.) */ enum { ACCESS = 0, MTIME = 4, SIZE = 8, USER_DATA = 14 }; DB* db; // Database object DBT data; // Object for result data }; //______________________________________________________________________ CacheFile::~CacheFile() { free(data.data); Paranoid(db != 0); db->close(db, 0); // no flags, ignore any errors } //______________________________________________________________________ void CacheFile::insert(const byte* inData, size_t inSize, const string& fileName, time_t mtime, uint64 fileSize) { memcpy(insert_prepare(inSize), inData, inSize); insert_perform(fileName, mtime, fileSize); } template void CacheFile::insert(Functor f, size_t inSize, const string& fileName, time_t mtime, uint64 fileSize) { f(insert_prepare(inSize)); insert_perform(fileName, mtime, fileSize); } //====================================================================== #else // !HAVE_LIBDB - provide a dummy implementation which does nothing class CacheFile { public: CacheFile(const char*) { } ~CacheFile() { } bool find(const byte*&, size_t&, const string&, time_t, uint64) { return false; } bool find_name(const byte*&, size_t&, const string&, long long int&, time_t&) { return false; } template void insert(Functor, size_t, const string&, time_t, uint64) { } void insert(const byte*, size_t, const string&, time_t, uint64) { } void expire(time_t) { } }; #endif #endif jigdo-0.7.3/src/compat.cc0000644000175000017500000001227410433366324015022 0ustar richardrichard/* $Id: compat.cc,v 1.6 2006/05/19 16:05:40 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Cross-platform compatibility */ #include #include #include #include #include #include #include #if WINDOWS # include # include #endif //______________________________________________________________________ #if HAVE_TRUNCATE // No additional code required //______________________________________________________________________ #elif WINDOWS // Truncate using native Windows API int compat_truncate(const char* path, uint64 length) { // TODO error handling: GetLastError(), FormatMessage() HANDLE handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (handle == INVALID_HANDLE_VALUE) return -1; LONG lengthHi = length >> 32; DWORD setPointerRet = SetFilePointer(handle, length, &lengthHi, FILE_BEGIN); if ((setPointerRet == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) || SetEndOfFile(handle) == 0) { CloseHandle(handle); return -1; } if (CloseHandle(handle) == 0) return -1; return 0; } //______________________________________________________________________ #elif !HAVE_TRUNCATE && HAVE_FTRUNCATE // Truncate using POSIX ftruncate() #include #include #include #include #ifndef O_LARGEFILE # define O_LARGEFILE 0 // Linux: Allow 64-bit file sizes on 32-bit arches #endif int compat_truncate(const char* path, uint64 length) { int fd = open(path, O_RDWR | O_LARGEFILE); if (fd == -1) return -1; if (ftruncate(fd, length) != 0) { close(fd); return -1; } return close(fd); } //______________________________________________________________________ #else # error "No implementation for truncating files!" #endif //==================================================================== #if WINDOWS // Delete destination before renaming int compat_rename(const char* src, const char* dst) { remove(dst); // Ignore errors return rename(src, dst); } #endif //==================================================================== #if !WINDOWS && !HAVE_SETENV namespace { struct CmpTilEq { /* Like less<>, but only compares up to the first '=' in the string. All *strings must* contain a '='. */ bool operator()(const string& x, const string& y) const { string::const_iterator xx = x.begin(), yy = y.begin(); char a, b; do { a = *xx; ++xx; b = *yy; ++yy; if (a == '=') return b != '='; } while (a == b); if (b == '=') return false; return a < b; } }; } /* This is probably a candidate for the least efficient setenv-via-putenv implementation ever. */ bool compat_setenv(const char* name, const char* value) { typedef set VarSet; static VarSet vars; string var = name; var += '='; var += value; VarSet::iterator old = vars.find(var); if (old != vars.end()) vars.erase(old); pair ins = vars.insert(var); return putenv(const_cast(ins.first->c_str())) == 0 ? SUCCESS : FAILURE; } #endif //==================================================================== #if !WINDOWS && HAVE_IOCTL_WINSZ && HAVE_FILENO // Solaris 10 includes: #include #include // Linux includes: #include #include int ttyWidth() { struct winsize w; if (ioctl(fileno(stderr), TIOCGWINSZ, &w) == -1 || w.ws_col == 0) return 0; return w.ws_col; } //==================================================================== #if !HAVE_STRINGCMP int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const string& s2, string::size_type pos2 = 0, string::size_type n2 = string::npos) { string::size_type r1 = s1.length() - pos1; if (r1 > n1) r1 = n1; string::size_type r2 = s2.length() - pos2; if (r2 > n2) r2 = n2; string::size_type r = r2; int rdiff = r1 - r2; if (rdiff < 0) r = r1; string::const_iterator i1 = s1.begin() + pos1; string::const_iterator i2 = s2.begin() + pos2; while (r > 0) { if (*i1 < *i2) return -1; if (*i1 > *i2) return 1; ++i1; ++i2; --r; } return rdiff; } #endif //==================================================================== #if !HAVE_STRINGSTRCMP int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const char* s2, string::size_type n2 = string::npos) { string::size_type r1 = s1.length() - pos1; if (r1 > n1) r1 = n1; string::size_type r2 = strlen(s2); if (r2 > n2) r2 = n2; string::size_type r = r2; int rdiff = r1 - r2; if (rdiff < 0) r = r1; string::const_iterator i1 = s1.begin() + pos1; const char* i2 = s2; while (r > 0) { if (*i1 < *i2) return -1; if (*i1 > *i2) return 1; ++i1; ++i2; --r; } return rdiff; } #endif #endif jigdo-0.7.3/src/compat.hh0000644000175000017500000001311010261546437015026 0ustar richardrichard/* $Id: compat.hh,v 1.6 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Cross-platform compatibility support */ #ifndef COMPAT_HH #define COMPAT_HH #include #include #include #include #include #include #if WINDOWS # include #endif //______________________________________________________________________ // No operator<< for uint64, so define our own #if !HAVE_OUTUINT64 #include inline ostream& operator<<(ostream& s, const uint64 x) { s << static_cast(x / 1000000000) << static_cast(x % 1000000000); return s; } #endif //______________________________________________________________________ /** Truncate a file to a given length. Behaviour undefined if given length is bigger than current file size */ #if HAVE_TRUNCATE inline int compat_truncate(const char* path, uint64 length) { return truncate(path, length); } #else int compat_truncate(const char* path, uint64 length); #endif //______________________________________________________________________ /** Rename a file. Mingw does provide rename(), but gives an error if the destination name already exists. This one doesn't. */ #if WINDOWS int compat_rename(const char* src, const char* dst); #else inline int compat_rename(const char* src, const char* dst) { return rename(src, dst); } #endif //______________________________________________________________________ /** Create a directory */ #if WINDOWS inline int compat_mkdir(const char* newDir) { return mkdir(newDir); } #else inline int compat_mkdir(const char* newDir) { return mkdir(newDir, 0777); } #endif //______________________________________________________________________ /** Set/overwrite environment variable, return SUCCESS or FAILURE */ #if WINDOWS inline bool compat_setenv(const char* name, const char* value) { return (SetEnvironmentVariable(name, value) != 0) ? SUCCESS : FAILURE; } #elif HAVE_SETENV /* Linux, BSD */ inline bool compat_setenv(const char* name, const char* value) { return (setenv(name, value, 1) == 0) ? SUCCESS : FAILURE; } #else /* Solaris and other Unices without setenv() */ bool compat_setenv(const char* name, const char* value); #endif //______________________________________________________________________ /** Width in characters of the tty (for progress display), or 0 if not a tty or functions not present on system. */ #if WINDOWS inline int ttyWidth() { return 80; } #elif !HAVE_IOCTL_WINSZ || !HAVE_FILENO inline int ttyWidth() { return 0; } #else extern int ttyWidth(); #endif //______________________________________________________________________ /** (For "file:" URI handling) If directory separator on this system is not '/', exchange the actual separator for '/' and vice versa in the supplied string. Do the same for any non-'.' file extension separator. We trust in the optimizer to remove unnecessary code. */ inline void compat_swapFileUriChars(string& s) { // Need this "if" because gcc cannot optimize away loops if (DIRSEP != '/' || EXTSEP != '.') { for (string::iterator i = s.begin(), e = s.end(); i != e; ++i) { if (DIRSEP != '/' && *i == DIRSEP) *i = '/'; else if (DIRSEP != '/' && *i == '/') *i = DIRSEP; else if (EXTSEP != '.' && *i == EXTSEP) *i = '.'; else if (EXTSEP != '.' && *i == '.') *i = EXTSEP; } } } //______________________________________________________________________ /** Three-way comparison between arbitrary substrings */ #if HAVE_STRINGCMP inline int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const string& s2, string::size_type pos2 = 0, string::size_type n2 = string::npos) { return s1.compare(pos1, n1, s2, pos2, n2); } #else int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const string& s2, string::size_type pos2 = 0, string::size_type n2 = string::npos); #endif //______________________________________________________________________ /** Three-way comparison between arbitrary substrings */ #if HAVE_STRINGSTRCMP inline int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const char* s2, string::size_type n2 = string::npos) { return s1.compare(pos1, n1, s2, n2); } #else int compat_compare(const string& s1, string::size_type pos1, string::size_type n1, const char* s2, string::size_type n2 = string::npos); #endif //______________________________________________________________________ #if HAVE_LIBDB # include // v3, v4.0: // int (*open) __P((DB *, // const char *, const char *, DBTYPE, u_int32_t, int)); // v4.1 onwards: // int (*open) __P((DB *, DB_TXN *, // const char *, const char *, DBTYPE, u_int32_t, int)); inline int compat_dbOpen(DB* db, const char* file, const char* database, DBTYPE type, u_int32_t flags, int mode) { return db->open(db, # if (DB_VERSION_MAJOR > 4 || \ (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 0)) NULL, # endif file, database, type, flags, mode); } #endif //______________________________________________________________________ #endif jigdo-0.7.3/src/config.h.in0000644000175000017500000001743510262207206015250 0ustar richardrichard// -*- C++ -*- /** @file Auto-generated from config.h.in */ // System-dependent configuration #ifndef CONFIG_H #define CONFIG_H /** @name What OS do we compile for? Exactly one of these must be 1. Assumes a native Windows environment like mingw32, not Cygwin. */ //@{ #define WINDOWS 0 #define UNIX 0 //@} /** Define if target architecture is big-endian */ #undef WORDS_BIGENDIAN /** An unsigned integer type with at least 64, possibly more bits */ #undef TYPE_UINT64 /** Define to 1 if the type "unsigned long long" is supported */ #define HAVE_UNSIGNED_LONG_LONG 1 /** Define to 1 if "uint64 x; cout << x;" works */ #define HAVE_OUTUINT64 1 /** Is "#define debug(format, ...)" possible in C++? The C99 standard has it. */ #define HAVE_VARMACRO 0 /** Define to 1 if header is available on the system */ #define HAVE_STDDEF_H 0 #if HAVE_STDDEF_H # include /* to get size_t */ #endif /** Define to 1 if header is available on the system */ #define HAVE_UNISTD_H 0 /** Define to 1 if header is available on the system */ #define HAVE_LIMITS_H 0 /** Define to 1 if header is available on the system */ #define HAVE_STRING_H 1 /** Define to `unsigned' if doesn't define. */ #undef size_t /** Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /** Define to 1 if gettext is installed, if you want support for translation of program messages. PACKAGE_LOCALE_DIR is a string like "/usr/local/share/locale" under Unix - gettext will load e.g. "/usr/local/share/locale/de/LC_MESSAGES/jigdo.mo" for German translations */ #define ENABLE_NLS 0 /** Define if your system provides getopt_long in (will compile in own version if not) */ #define HAVE_GETOPT_LONG 0 /** Define if your system provides uname in */ #define HAVE_UNAME 0 /** Define to 1 if libdb is present on the system. If set to 0, some functionality (jigdo-file's --cache option) will not be available. */ #define HAVE_LIBDB 0 /** Define to 1 if "int lstat(const char *file_name, struct stat *buf)" is available, i.e. symbolic links are supported. If defined to 0, stat() is used instead. */ #define HAVE_LSTAT 0 /** Preferably, we want to use "int truncate(const char *path, off_t length)" to truncate a file to a given length. Alternatively, if "int ftruncate(int fd, off_t length)" is available, compat.cc truncates using that. */ #define HAVE_TRUNCATE 0 #define HAVE_FTRUNCATE 0 /** Define to 1 if "void * mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)" and "int munmap(void *start, size_t length)" are present. Only used in torture. */ #define HAVE_MMAP 0 /** Define to 1 if memcpy is is present */ #define HAVE_MEMCPY 1 /** Define to 1 if "int fileno( FILE *stream)" for getting the Unix file descriptor for an ANSI C FILE* is present. */ #define HAVE_FILENO 0 /** Define to 1 if "ioctl(fileno(stdout), TIOCGWINSZ, &winsize)" can be used to read the width in characters of the TTY, for formatting of progress reports. This probably only works on Linux. */ #define HAVE_IOCTL_WINSZ 0 /** Both MinGW32 and glib declare 'struct dirent' and 'struct DIR'. If there are problems using the relevant headers together, define to 1 to do some really dirty things with the preprocessor to disable the glib declaration. */ #define DIRENT_HACK 0 /** Define to 1 if the following function is supported by the C++ library: int string::compare(size_type pos, size_type n, const string& s, size_type pos2, size_type n2) const; */ #define HAVE_STRINGCMP 1 /** Define to 1 if the following function is supported by the C++ library: int string::compare(size_type pos, size_type n, const char* s, size_type len = npos) const; */ #define HAVE_STRINGSTRCMP 1 /** Define to 1 if setenv(const char *name, const char *value, int overwrite); is available. If this is not the case (e.g. on Solaris), a kludge of a workaround using putenv() is used. */ #define HAVE_SETENV 0 /** On native MinGW32, redefine snprintf to _snprintf */ #undef snprintf /** On native MinGW32, redefine stat to _stati64 for big file support */ #undef stat /** Big file support is broken in the libstdc++ on GCC 3.x, x<4. There's a rather gruesome workaround in bstream.hh for that case. */ #if __GNUC__ == 3 && __GNUC_MINOR__ < 4 && !defined __MINGW32__ # define HAVE_WORKING_FSTREAM 0 #else # define HAVE_WORKING_FSTREAM 1 #endif /** Define to 1 to compile in lots of additional run-time checks */ #ifndef DEBUG #define DEBUG 0 #endif /** Program version */ #undef JIGDO_VERSION #endif /* CONFIG_H */ //______________________________________________________________________ #ifndef CONFIG_CONSTANTS #ifdef __cplusplus #define CONFIG_CONSTANTS // Do not change this #define PACKAGE "jigdo" // For copyright messages, the year of the last change to the program code #define CURRENT_YEAR 2005 // __attribute__ only present with GCC #ifndef __GNUC__ # define __attribute__(x) #endif #if DEBUG # define NOINLINE /* to reduce amount of recompilation */ # define GTK_DISABLE_DEPRECATED # define GNOME_DISABLE_DEPRECATED # define G_DISABLE_DEPRECATED #else /* !DEBUG */ // Disable runtime checks of glib - if you #include config.h before glib.h!-) # define G_DISABLE_CHECKS # define G_DISABLE_ASSERT #endif /** Character (as char and string constant) for directory separator First character of SPLITSEP must be same as DIRSEP */ #if WINDOWS # define DIRSEP '\\' # define DIRSEPS "\\" # define SPLITSEP "\\.\\" #else # define DIRSEP '/' # define DIRSEPS "/" # define SPLITSEP "//" #endif /// Character for filename extensions #define EXTSEP '.' #define EXTSEPS "." /* contains #defines of min and max which get on everybody's nerves by colliding with the STL's min() and max() functions. With mingw, defining NOMINMAX prevents min and max from being defined as macros. */ #if (WINDOWS && !defined(NOMINMAX)) # define NOMINMAX #endif #if DIRENT_HACK # include #endif // libwww.hh also contains some of these #defines #if ENABLE_NLS # include # undef _ # define _(String) dgettext (PACKAGE, String) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define _(String) (String) # define N_(String) (String) #endif #include // Compatibility for compilers which do/don't put the STL into std:: namespace std { } using namespace std; namespace { /** This may seem to be defined the wrong way around, but it allows you to write 'if (someFunction()) { handleError(); }' */ const bool SUCCESS = false; /// const bool FAILURE = true; /** Version of file format */ const unsigned FILEFORMAT_MAJOR = 1; const unsigned FILEFORMAT_MINOR = 2; /** String at start of .template files */ const char TEMPLATE_HDR[] = "JigsawDownload template "; /** Info URL written in .template/.jigdo file header */ const char* const URL = "http://atterer.net/jigdo/"; /** Approximate size of chunks into which unmatched data is chopped up when included in template file. Measured in bytes of *uncompressed* data. Only applicable to gzip; when using bzip2, it is ignored. */ const size_t ZIPCHUNK_SIZE = 256*1024; /** Number of bytes to write at a time before outputting a progress report */ const size_t REPORT_INTERVAL = 256U*1024; } /** unsigned int with at least 64, but possibly more bits */ typedef TYPE_UINT64 uint64; /** unsigned int with at least 32, but possibly more bits */ typedef unsigned int uint32; /** exactly 8 bits, unsigned */ typedef unsigned char byte; /** Bjarne's implicit cast - useful at times */ template inline T implicit_cast(U u) { return u; } #endif #endif jigdo-0.7.3/src/dirent.hh0000644000175000017500000000262310121135314015016 0ustar richardrichard/* $Id: dirent.hh,v 1.2 2004/09/12 21:08:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Compatibility header for glib+mingw. Probably no longer an issue.y */ #ifndef DIRENT_HH #define DIRENT_HH /* Work around clash of definitions of 'struct dirent' and 'struct DIR'; both MinGW and glib define them. */ #include #if DIRENT_HACK # if DEBUG # warning "horrible preprocessor hack to fix glib/MinGW dirent conflict" # endif # include # include # define dirent glibHack_seeConfigH_dirent # define DIR glibHack_seeConfigH_DIR # define g_win32_opendir glibHack_seeConfigH_g_win32_opendir # define g_win32_readdir glibHack_seeConfigH_g_win32_readdir # define g_win32_closedir glibHack_seeConfigH_g_win32_closedir # define g_win32_rewinddir glibHack_seeConfigH_g_win32_rewinddir # include # undef dirent # undef DIR # undef g_win32_opendir # undef g_win32_readdir # undef g_win32_closedir # undef g_win32_rewinddir # undef ftruncate # undef opendir # undef readdir # undef rewinddir # undef closedir # else # include #endif #endif jigdo-0.7.3/src/glibcurl/Makefile0000644000175000017500000000124710076616605016474 0ustar richardrichard# Project: glibcurl (integration of glib event loop with libcurl) # __ _ # |_) /| Copyright (C) 2004 | richard@ # | \/¯| Richard Atterer | atterer.net # ¯ '` ¯ CFLAGS = -Wall -O2 CPPFLAGS = -I. \ $(shell pkg-config gthread-2.0 --cflags) \ $(shell curl-config --cflags) LIBS = $(shell pkg-config gthread-2.0 --libs) \ $(shell curl-config --libs) all: glibcurl-example run: all ./glibcurl-example http://localhost:8000/~richard/ironmaiden/image clean: rm -f glibcurl-example glibcurl-example.o glibcurl.o glibcurl-example: glibcurl-example.o glibcurl.o $(CC) -o $@ glibcurl-example.o glibcurl.o $(LIBS) $(LDFLAGS) glibcurl-example.o: glibcurl.o jigdo-0.7.3/src/glibcurl/glibcurl-example.c0000644000175000017500000000615710154341062020426 0ustar richardrichard/* $Id: glibcurl-example.c,v 1.5 2004/12/04 13:57:06 atterer Exp $ -*- C -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 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 OF THIRD PARTY RIGHTS. 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. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. */ #include #include #include #define HANDLES 2 int numRunning = HANDLES; unsigned nBytes = 0; /* to accumulate total nr of bytes downloaded */ size_t curlWriter(void *ptr, size_t size, size_t nmemb, void *stream); void curlCallback(void*); int main(int argc, char** argv) { CURL* h[HANDLES]; GMainLoop* gloop; int i; if (argc < 2) { printf("Invoke this as \"%s http://URL\". %d concurrent transfers of " "the URL will be started, one printing AAA, the next BBB...\n", argv[0], HANDLES); return 1; } glibcurl_init(); gloop = g_main_loop_new(NULL, TRUE); glibcurl_set_callback(&curlCallback, gloop); for (i = 0; i < HANDLES; ++i) { h[i] = curl_easy_init(); curl_easy_setopt(h[i], CURLOPT_URL, argv[1]); curl_easy_setopt(h[i], CURLOPT_VERBOSE, 1); curl_easy_setopt(h[i], CURLOPT_BUFFERSIZE, 1024*10); curl_easy_setopt(h[i], CURLOPT_WRITEFUNCTION, curlWriter); curl_easy_setopt(h[i], CURLOPT_WRITEDATA, "ABCDEFGHIJKLMN" + i); glibcurl_add(h[i]); } /* Run main loop. Alternatively, you can use gtk_main() */ printf("Start\n"); g_main_loop_run(gloop); printf("\nFinished, fetched %u bytes\n", nBytes); /* Clean up */ for (i = 0; i < HANDLES; ++i) { glibcurl_remove(h[i]); curl_easy_cleanup(h[i]); } glibcurl_cleanup(); return 0; } size_t curlWriter(void* ptr, size_t size, size_t nmemb, void *stream) { if (ptr == 0) return 0; /* NOP, just to avoid "unused param" warning */ putchar(*(char*)stream); fflush(stdout); nBytes += size * nmemb; return size * nmemb; } void curlCallback(void* data) { CURLMsg* msg; int inQueue; /* putchar(' '); */ while (1) { msg = curl_multi_info_read(glibcurl_handle(), &inQueue); if (msg == 0) break; if (msg->msg != CURLMSG_DONE) continue; /* Cause the above call to g_main_loop_run() to terminate once all requests are finished. */ if (--numRunning == 0) g_main_loop_quit((GMainLoop*)data); } } jigdo-0.7.3/src/glibcurl/glibcurl.c0000644000175000017500000004434310154632020016771 0ustar richardrichard/* $Id: glibcurl.c,v 1.14 2004/12/05 16:15:12 atterer Exp $ -*- C -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 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 OF THIRD PARTY RIGHTS. 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. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. */ #include #include #include #include #include /* #define D(_args) fprintf _args; */ #define D(_args) /* #if 1 */ #ifdef G_OS_WIN32 /*______________________________________________________________________*/ /* Timeout for the fds passed to glib's poll() call, in millisecs. curl_multi_fdset(3) says we should call curl_multi_perform() at regular intervals. */ #define GLIBCURL_TIMEOUT 500 /* A structure which "derives" (in glib speak) from GSource */ typedef struct CurlGSource_ { GSource source; /* First: The type we're deriving from */ CURLM* multiHandle; GThread* selectThread; GCond* cond; /* To signal selectThread => main thread: call perform() */ GMutex* mutex; /* Not held by selectThread whenever it is waiting */ gboolean callPerform; /* TRUE => Call curl_multi_perform() Real Soon */ gint gtkBlockAndWait; gboolean selectRunning; /* FALSE => selectThread terminates */ /* For data returned by curl_multi_fdset */ fd_set fdRead; fd_set fdWrite; fd_set fdExc; int fdMax; } CurlGSource; /* Global state: Our CurlGSource object */ static CurlGSource* curlSrc = 0; /* The "methods" of CurlGSource */ static gboolean prepare(GSource* source, gint* timeout); static gboolean check(GSource* source); static gboolean dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void finalize(GSource* source); static GSourceFuncs curlFuncs = { &prepare, &check, &dispatch, &finalize, 0, 0 }; /*______________________________________________________________________*/ void glibcurl_init() { /* Create source object for curl file descriptors, and hook it into the default main context. */ GSource* src = g_source_new(&curlFuncs, sizeof(CurlGSource)); curlSrc = (CurlGSource*)src; g_source_attach(&curlSrc->source, NULL); if (!g_thread_supported()) g_thread_init(NULL); /* Init rest of our data */ curlSrc->callPerform = 0; curlSrc->selectThread = 0; curlSrc->cond = g_cond_new(); curlSrc->mutex = g_mutex_new(); curlSrc->gtkBlockAndWait = 0; /* Init libcurl */ curl_global_init(CURL_GLOBAL_ALL); curlSrc->multiHandle = curl_multi_init(); } /*______________________________________________________________________*/ void glibcurl_cleanup() { D((stderr, "glibcurl_cleanup\n")); /* You must call curl_multi_remove_handle() and curl_easy_cleanup() for all requests before calling this. */ /* assert(curlSrc->callPerform == 0); */ /* All easy handles must be finished */ /* Lock before accessing selectRunning/selectThread */ g_mutex_lock(curlSrc->mutex); curlSrc->selectRunning = FALSE; while (curlSrc->selectThread != NULL) { g_mutex_unlock(curlSrc->mutex); g_thread_yield(); g_cond_signal(curlSrc->cond); /* Make the select thread shut down */ g_thread_yield(); g_mutex_lock(curlSrc->mutex); /* Wait until it has shut down */ } g_mutex_unlock(curlSrc->mutex); assert(curlSrc->selectThread == NULL); g_cond_free(curlSrc->cond); g_mutex_free(curlSrc->mutex); curl_multi_cleanup(curlSrc->multiHandle); curlSrc->multiHandle = 0; curl_global_cleanup(); g_source_unref(&curlSrc->source); curlSrc = 0; } /*______________________________________________________________________*/ CURLM* glibcurl_handle() { return curlSrc->multiHandle; } /*______________________________________________________________________*/ CURLMcode glibcurl_add(CURL *easy_handle) { assert(curlSrc != 0); assert(curlSrc->multiHandle != 0); glibcurl_start(); return curl_multi_add_handle(curlSrc->multiHandle, easy_handle); } /*______________________________________________________________________*/ CURLMcode glibcurl_remove(CURL *easy_handle) { D((stderr, "glibcurl_remove %p\n", easy_handle)); assert(curlSrc != 0); assert(curlSrc->multiHandle != 0); return curl_multi_remove_handle(curlSrc->multiHandle, easy_handle); } /*______________________________________________________________________*/ /* Call this whenever you have added a request using curl_multi_add_handle(). */ void glibcurl_start() { D((stderr, "glibcurl_start\n")); curlSrc->callPerform = TRUE; } /*______________________________________________________________________*/ void glibcurl_set_callback(GlibcurlCallback function, void* data) { g_source_set_callback(&curlSrc->source, (GSourceFunc)function, data, NULL); } /*______________________________________________________________________*/ static gpointer selectThread(gpointer data) { int fdCount; struct timeval timeout; assert(data == 0); /* Just to get rid of unused param warning */ D((stderr, "selectThread\n")); g_mutex_lock(curlSrc->mutex); D((stderr, "selectThread: got lock\n")); curlSrc->selectRunning = TRUE; while (curlSrc->selectRunning) { FD_ZERO(&curlSrc->fdRead); FD_ZERO(&curlSrc->fdWrite); FD_ZERO(&curlSrc->fdExc); curlSrc->fdMax = -1; /* What fds does libcurl want us to poll? */ curl_multi_fdset(curlSrc->multiHandle, &curlSrc->fdRead, &curlSrc->fdWrite, &curlSrc->fdExc, &curlSrc->fdMax); timeout.tv_sec = GLIBCURL_TIMEOUT / 1000; timeout.tv_usec = (GLIBCURL_TIMEOUT % 1000) * 1000; fdCount = select(curlSrc->fdMax + 1, &curlSrc->fdRead, &curlSrc->fdWrite, &curlSrc->fdExc, &timeout); D((stderr, "selectThread: select() fdCount=%d\n", fdCount)); g_atomic_int_inc(&curlSrc->gtkBlockAndWait); /* "GTK thread, block!" */ D((stderr, "selectThread: waking up GTK thread %d\n", curlSrc->gtkBlockAndWait)); /* GTK thread will almost immediately block in prepare() */ g_main_context_wakeup(NULL); /* Now unblock GTK thread, continue after it signals us */ D((stderr, "selectThread: pre-wait\n")); g_cond_wait(curlSrc->cond, curlSrc->mutex); D((stderr, "selectThread: post-wait\n")); } curlSrc->selectThread = NULL; D((stderr, "selectThread: exit\n")); g_mutex_unlock(curlSrc->mutex); return NULL; } /*______________________________________________________________________*/ /* Returning FALSE may cause the main loop to block indefinitely, but that is not a problem, we use g_main_context_wakeup to wake it up */ /* Returns TRUE iff it holds the mutex lock */ gboolean prepare(GSource* source, gint* timeout) { assert(source == &curlSrc->source); D((stderr, "prepare: callPerform=%d, thread=%p\n", curlSrc->callPerform, curlSrc->selectThread)); *timeout = -1; if (g_atomic_int_dec_and_test(&curlSrc->gtkBlockAndWait)) { /* The select thread wants us to block */ D((stderr, "prepare: trying lock\n")); g_mutex_lock(curlSrc->mutex); D((stderr, "prepare: got lock\n")); return TRUE; } else { g_atomic_int_inc(&curlSrc->gtkBlockAndWait); } /* Usual behaviour: Nothing happened, so don't dispatch. */ if (!curlSrc->callPerform) return FALSE; /* Always dispatch if callPerform, i.e. 1st download just starting. */ D((stderr, "prepare: trying lock 2\n")); /* Problem: We can block up to GLIBCURL_TIMEOUT msecs here, until the select() call returns. However, under Win32 this does not appear to be a problem (don't know why) - it _does_ tend to block the GTK thread under Linux. */ g_mutex_lock(curlSrc->mutex); D((stderr, "prepare: got lock 2\n")); curlSrc->callPerform = FALSE; if (curlSrc->selectThread == NULL) { D((stderr, "prepare: starting select thread\n")); /* Note that the thread will stop soon because we hold mutex */ curlSrc->selectThread = g_thread_create(&selectThread, 0, FALSE, NULL); assert(curlSrc->selectThread != NULL); } return TRUE; } /*______________________________________________________________________*/ /* Called after all the file descriptors are polled by glib. */ gboolean check(GSource* source) { assert(source == &curlSrc->source); return FALSE; } /*______________________________________________________________________*/ gboolean dispatch(GSource* source, GSourceFunc callback, gpointer user_data) { CURLMcode x; int multiCount; assert(source == &curlSrc->source); do { x = curl_multi_perform(curlSrc->multiHandle, &multiCount); D((stderr, "dispatched: code=%d, reqs=%d\n", x, multiCount)); } while (x == CURLM_CALL_MULTI_PERFORM); if (multiCount == 0) curlSrc->selectRunning = FALSE; if (callback != 0) (*callback)(user_data); /* Let selectThread call select() again */ g_cond_signal(curlSrc->cond); g_mutex_unlock(curlSrc->mutex); return TRUE; /* "Do not destroy me" */ } /*______________________________________________________________________*/ void finalize(GSource* source) { assert(source == &curlSrc->source); } /*======================================================================*/ #else /* !G_OS_WIN32 */ /* Number of highest allowed fd */ #define GLIBCURL_FDMAX 127 /* Timeout for the fds passed to glib's poll() call, in millisecs. curl_multi_fdset(3) says we should call curl_multi_perform() at regular intervals. */ #define GLIBCURL_TIMEOUT 1000 /* GIOCondition event masks */ #define GLIBCURL_READ (G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP) #define GLIBCURL_WRITE (G_IO_OUT | G_IO_ERR | G_IO_HUP) #define GLIBCURL_EXC (G_IO_ERR | G_IO_HUP) /** A structure which "derives" (in glib speak) from GSource */ typedef struct CurlGSource_ { GSource source; /* First: The type we're deriving from */ CURLM* multiHandle; /* Previously seen FDs, for comparing with libcurl's current fd_sets */ GPollFD lastPollFd[GLIBCURL_FDMAX + 1]; int lastPollFdMax; /* Index of highest non-empty entry in lastPollFd */ int callPerform; /* Non-zero => curl_multi_perform() gets called */ /* For data returned by curl_multi_fdset */ fd_set fdRead; fd_set fdWrite; fd_set fdExc; int fdMax; } CurlGSource; /* Global state: Our CurlGSource object */ static CurlGSource* curlSrc = 0; /* The "methods" of CurlGSource */ static gboolean prepare(GSource* source, gint* timeout); static gboolean check(GSource* source); static gboolean dispatch(GSource* source, GSourceFunc callback, gpointer user_data); static void finalize(GSource* source); static GSourceFuncs curlFuncs = { &prepare, &check, &dispatch, &finalize, 0, 0 }; /*______________________________________________________________________*/ void glibcurl_init() { int fd; /* Create source object for curl file descriptors, and hook it into the default main context. */ curlSrc = (CurlGSource*)g_source_new(&curlFuncs, sizeof(CurlGSource)); g_source_attach(&curlSrc->source, NULL); /* Init rest of our data */ memset(&curlSrc->lastPollFd, 0, sizeof(curlSrc->lastPollFd)); for (fd = 1; fd <= GLIBCURL_FDMAX; ++fd) curlSrc->lastPollFd[fd].fd = fd; curlSrc->lastPollFdMax = 0; curlSrc->callPerform = 0; /* Init libcurl */ curl_global_init(CURL_GLOBAL_ALL); curlSrc->multiHandle = curl_multi_init(); D((stderr, "events: R=%x W=%x X=%x\n", GLIBCURL_READ, GLIBCURL_WRITE, GLIBCURL_EXC)); } /*______________________________________________________________________*/ CURLM* glibcurl_handle() { return curlSrc->multiHandle; } /*______________________________________________________________________*/ CURLMcode glibcurl_add(CURL *easy_handle) { assert(curlSrc->multiHandle != 0); curlSrc->callPerform = -1; return curl_multi_add_handle(curlSrc->multiHandle, easy_handle); } /*______________________________________________________________________*/ CURLMcode glibcurl_remove(CURL *easy_handle) { assert(curlSrc != 0); assert(curlSrc->multiHandle != 0); return curl_multi_remove_handle(curlSrc->multiHandle, easy_handle); } /*______________________________________________________________________*/ /* Call this whenever you have added a request using curl_multi_add_handle(). This is necessary to start new requests. It does so by triggering a call to curl_multi_perform() even in the case where no open fds cause that function to be called anyway. */ void glibcurl_start() { curlSrc->callPerform = -1; } /*______________________________________________________________________*/ void glibcurl_set_callback(GlibcurlCallback function, void* data) { g_source_set_callback(&curlSrc->source, (GSourceFunc)function, data, NULL); } /*______________________________________________________________________*/ void glibcurl_cleanup() { /* You must call curl_multi_remove_handle() and curl_easy_cleanup() for all requests before calling this. */ /* assert(curlSrc->callPerform == 0); */ curl_multi_cleanup(curlSrc->multiHandle); curlSrc->multiHandle = 0; curl_global_cleanup(); /* g_source_destroy(&curlSrc->source); */ g_source_unref(&curlSrc->source); curlSrc = 0; } /*______________________________________________________________________*/ static void registerUnregisterFds() { int fd, fdMax; FD_ZERO(&curlSrc->fdRead); FD_ZERO(&curlSrc->fdWrite); FD_ZERO(&curlSrc->fdExc); curlSrc->fdMax = -1; /* What fds does libcurl want us to poll? */ curl_multi_fdset(curlSrc->multiHandle, &curlSrc->fdRead, &curlSrc->fdWrite, &curlSrc->fdExc, &curlSrc->fdMax); /*fprintf(stderr, "registerUnregisterFds: fdMax=%d\n", curlSrc->fdMax);*/ assert(curlSrc->fdMax >= -1 && curlSrc->fdMax <= GLIBCURL_FDMAX); fdMax = curlSrc->fdMax; if (fdMax < curlSrc->lastPollFdMax) fdMax = curlSrc->lastPollFdMax; /* Has the list of required events for any of the fds changed? */ for (fd = 0; fd <= fdMax; ++fd) { gushort events = 0; if (FD_ISSET(fd, &curlSrc->fdRead)) events |= GLIBCURL_READ; if (FD_ISSET(fd, &curlSrc->fdWrite)) events |= GLIBCURL_WRITE; if (FD_ISSET(fd, &curlSrc->fdExc)) events |= GLIBCURL_EXC; /* List of events unchanged => no (de)registering */ if (events == curlSrc->lastPollFd[fd].events) continue; D((stderr, "registerUnregisterFds: fd %d: old events %x, " "new events %x\n", fd, curlSrc->lastPollFd[fd].events, events)); /* fd is already a lastPollFd, but event type has changed => do nothing. Due to the implementation of g_main_context_query(), the new event flags will be picked up automatically. */ if (events != 0 && curlSrc->lastPollFd[fd].events != 0) { curlSrc->lastPollFd[fd].events = events; continue; } curlSrc->lastPollFd[fd].events = events; /* Otherwise, (de)register as appropriate */ if (events == 0) { g_source_remove_poll(&curlSrc->source, &curlSrc->lastPollFd[fd]); curlSrc->lastPollFd[fd].revents = 0; D((stderr, "unregister fd %d\n", fd)); } else { g_source_add_poll(&curlSrc->source, &curlSrc->lastPollFd[fd]); D((stderr, "register fd %d\n", fd)); } } curlSrc->lastPollFdMax = curlSrc->fdMax; } /* Called before all the file descriptors are polled by the glib main loop. We must have a look at all fds that libcurl wants polled. If any of them are new/no longer needed, we have to (de)register them with glib. */ gboolean prepare(GSource* source, gint* timeout) { D((stderr, "prepare\n")); assert(source == &curlSrc->source); if (curlSrc->multiHandle == 0) return FALSE; registerUnregisterFds(); *timeout = GLIBCURL_TIMEOUT; /* return FALSE; */ return curlSrc->callPerform == -1 ? TRUE : FALSE; } /*______________________________________________________________________*/ /* Called after all the file descriptors are polled by glib. g_main_context_check() has copied back the revents fields (set by glib's poll() call) to our GPollFD objects. How inefficient all that copying is... let's add some more and copy the results of these revents into libcurl's fd_sets! */ gboolean check(GSource* source) { int fd, somethingHappened = 0; if (curlSrc->multiHandle == 0) return FALSE; assert(source == &curlSrc->source); FD_ZERO(&curlSrc->fdRead); FD_ZERO(&curlSrc->fdWrite); FD_ZERO(&curlSrc->fdExc); for (fd = 0; fd <= curlSrc->fdMax; ++fd) { gushort revents = curlSrc->lastPollFd[fd].revents; if (revents == 0) continue; somethingHappened = 1; /* D((stderr, "[fd%d] ", fd)); */ if (revents & (G_IO_IN | G_IO_PRI)) FD_SET((unsigned)fd, &curlSrc->fdRead); if (revents & G_IO_OUT) FD_SET((unsigned)fd, &curlSrc->fdWrite); if (revents & (G_IO_ERR | G_IO_HUP)) FD_SET((unsigned)fd, &curlSrc->fdExc); } /* D((stderr, "check: fdMax %d\n", curlSrc->fdMax)); */ /* return TRUE; */ /* return FALSE; */ return curlSrc->callPerform == -1 || somethingHappened != 0 ? TRUE : FALSE; } /*______________________________________________________________________*/ gboolean dispatch(GSource* source, GSourceFunc callback, gpointer user_data) { CURLMcode x; assert(source == &curlSrc->source); assert(curlSrc->multiHandle != 0); do { x = curl_multi_perform(curlSrc->multiHandle, &curlSrc->callPerform); /* D((stderr, "dispatched %d\n", x)); */ } while (x == CURLM_CALL_MULTI_PERFORM); /* If no more calls to curl_multi_perform(), unregister left-over fds */ if (curlSrc->callPerform == 0) registerUnregisterFds(); if (callback != 0) (*callback)(user_data); return TRUE; /* "Do not destroy me" */ } /*______________________________________________________________________*/ void finalize(GSource* source) { assert(source == &curlSrc->source); registerUnregisterFds(); } #endif jigdo-0.7.3/src/glibcurl/glibcurl.h0000644000175000017500000000567410154341205017004 0ustar richardrichard/* $Id: glibcurl.h,v 1.7 2004/12/04 13:58:29 atterer Exp $ -*- C -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 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 OF THIRD PARTY RIGHTS. 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. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. */ /** @file Use the libcurl multi interface from GTK+/glib programs without having to resort to multithreading */ #ifndef GLIBCURL_H #define GLIBCURL_H #include #ifdef __cplusplus extern "C" { #endif /** Initialize libcurl. Call this once at the beginning of your program. This function makes calls to curl_global_init() and curl_multi_init() */ void glibcurl_init(); /** Return global multi handle */ CURLM* glibcurl_handle(); /** Convenience function, just executes curl_multi_add_handle(glibcurl_handle(), easy_handle); glibcurl_start()*/ CURLMcode glibcurl_add(CURL* easy_handle); /** Convenience function, just executes curl_multi_remove_handle(glibcurl_handle(), easy_handle) */ CURLMcode glibcurl_remove(CURL* easy_handle); /** Call this whenever you have added a request using curl_multi_add_handle(). This is necessary to start new requests. It does so by triggering a call to curl_multi_perform() even in the case where no open fds cause that function to be called anyway. The call happens "later", i.e. during the next iteration of the glib main loop. glibcurl_start() only sets a flag to make it happen. */ void glibcurl_start(); /** Callback function for glibcurl_set_callback */ typedef void (*GlibcurlCallback)(void*); /** Set function to call after each invocation of curl_multi_perform(). Pass function==0 to unregister a previously set callback. The callback function will be called with the supplied data pointer as its first argument. */ void glibcurl_set_callback(GlibcurlCallback function, void* data); /** You must call glibcurl_remove() and curl_easy_cleanup() for all requests before calling this. This function makes calls to curl_multi_cleanup() and curl_global_cleanup(). */ void glibcurl_cleanup(); #ifdef __cplusplus } #endif #endif jigdo-0.7.3/src/gtk/.cvsignore0000644000175000017500000000003207701377734016015 0ustar richardrichardinterface.cc interface.hh jigdo-0.7.3/src/gtk/gtk-makeimage.cc0000644000175000017500000002117310226253017017017 0ustar richardrichard/* $Id: gtk-makeimage.cc,v 1.19 2005/04/10 16:36:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download and processing of .jigdo files - GTK+ frontend */ #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("gtk-makeimage") GtkMakeImage::GtkMakeImage(const string& uriStr, const string& destDir) : progress(), status(), treeViewStatus(), dest(), imageInfo(_("\nDownloading .jigdo data - please wait...")), imageShortInfo(), mid(uriStr, destDir) { mid.io.addListener(*this); // Remove all trailing '/' from dest dir, even if result empty unsigned destLen = destDir.length(); while (destLen > 0 && destDir[destLen - 1] == DIRSEP) --destLen; dest.assign(destDir, 0, destLen); } GtkMakeImage::~GtkMakeImage() { mid.killAllChildren(); /* Delete all children. A simpler frontend would always delete them immediately when makeImageDl_finished() is called, but with GTK+, we leave them instantiated a few seconds (if child was successful) or until now (if error). */ GtkTreeIter x; GtkTreeModel* model = GTK_TREE_MODEL(jobList()->store()); // if (gtk_tree_model_iter_children(model, &x, row()) == TRUE) { // do { // JobLine* child = jobList()->get(&x); // debug("~GtkMakeImage: Deleting child %1", child); // delete child; // } while (gtk_tree_model_iter_next(model, &x) == TRUE); // } while (gtk_tree_model_iter_children(model, &x, row()) == TRUE) { JobLine* child = jobList()->get(&x); debug("~GtkMakeImage: Deleting child %1", child); delete child; } } //______________________________________________________________________ bool GtkMakeImage::run() { // Show URL as object name unsigned lastSlash = mid.jigdoUri().rfind('/'); const char* object = ""; if (lastSlash != string::npos) object = mid.jigdoUri().c_str() + lastSlash + 1; treeViewStatus = _("Waiting"); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), JobList::COLUMN_OBJECT, object, -1); mid.run(); // By default, children of this object are visible GtkTreePath* path = gtk_tree_model_get_path( GTK_TREE_MODEL(jobList()->store()), row() ); gtk_tree_view_expand_row(jobList()->view(), path, TRUE); gtk_tree_path_free(path); return SUCCESS; } //______________________________________________________________________ /* User clicked on our line in the display of jobs. If the window is not already displaying our info, switch to it. Otherwise, cycle through the sub-notebook tabs of our info. */ void GtkMakeImage::selectRow() { if (jobList()->isWindowOwner(this)) { // Cycle through tabs GtkNotebook* notebook = GTK_NOTEBOOK(GUI::window.pageJigdo); int page = gtk_notebook_get_current_page(notebook); int npages = gtk_notebook_get_n_pages(notebook); // For convenience, some unused tabs are set to invisible; skip them! while (true) { ++page; if (page >= npages) page = 0; GtkWidget* entry = gtk_notebook_get_nth_page(notebook, page); if (GTK_WIDGET_VISIBLE(entry)) break; } //msg("selrowcallback %1 of %2", page, npages); gtk_notebook_set_current_page(notebook, page); } else { // Don't cycle through tabs, just switch to jigdo info in main window setNotebookPage(GUI::window.pageJigdo); jobList()->setWindowOwner(this); } updateWindow(); } //______________________________________________________________________ bool GtkMakeImage::paused() const { return false; } void GtkMakeImage::pause() { } void GtkMakeImage::cont() { } void GtkMakeImage::stop() { } void GtkMakeImage::percentDone(uint64* cur, uint64* total) { *cur = 0; *total = 0; } //______________________________________________________________________ void GtkMakeImage::updateWindow() { if (!jobList()->isWindowOwner(this)) return; // Image description gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_ShortInfo), imageShortInfo.c_str()); gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_Info), imageInfo.c_str()); // URL and destination lines gtk_label_set_text(GTK_LABEL(GUI::window.jigdo_URL), mid.jigdoUri().c_str()); gtk_label_set_text(GTK_LABEL(GUI::window.jigdo_dest), dest.c_str()); // Progress and status lines // if (!mid.paused() && !mid.failed()) { // progress.erase(); // job->progress()->appendProgress(&progress); // } gtk_label_set_text(GTK_LABEL(GUI::window.jigdo_progress), progress.c_str()); gtk_label_set_text(GTK_LABEL(GUI::window.jigdo_status), status.c_str()); # if 0 // Buttons (in)sensitive gtk_widget_set_sensitive(GUI::window.jigdo_startButton, (job != 0 && (paused() || job->succeeded() || job->failed() && job->resumePossible()) ? TRUE : FALSE)); gtk_widget_set_sensitive(GUI::window.jigdo_pauseButton, (job != 0 && !job->failed() && !job->succeeded() && !paused() ? TRUE : FALSE)); gtk_widget_set_sensitive(GUI::window.jigdo_stopButton, (job != 0 && !job->failed() && !job->succeeded() ? TRUE : FALSE)); # endif } //______________________________________________________________________ void GtkMakeImage::job_deleted() { } void GtkMakeImage::job_succeeded() { } void GtkMakeImage::job_failed(const string& message) { debug("job_failed: %1", message); treeViewStatus = subst(_("%E1"), message); status = message; progress = _("Failed:"); updateWindow(); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); } void GtkMakeImage::job_message(const string& message) { debug("job_message: %1", message); treeViewStatus = message; status = message; gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); } void GtkMakeImage::makeImageDl_new( Job::DataSource* childDownload, const string& uri, const string& destDesc) { debug("makeImageDl_new: %1", uri); GtkSingleUrl* child = new GtkSingleUrl(uri, destDesc, childDownload); GUI::jobList.prepend(child, this); // New child of "this" is "child" bool status = child->run(); /* NB run() cannot result in "delete child;" for child mode, so we always return a valid pointer here. */ Assert(status == SUCCESS); childDownload->io.addListener(*child); } void GtkMakeImage::makeImageDl_finished(Job::DataSource* src) { // mid.io.listeners() for (IList::iterator i = src->io.listeners().begin(), e = src->io.listeners().end(); i != e; ++i) { GtkSingleUrl* child = dynamic_cast(&*i); if (child != 0) { debug("makeImageDl_finished: %1", src->location()); child->childIsFinished(); } } } void GtkMakeImage::makeImageDl_haveImageSection() { dest += DIRSEP; dest += mid.imageName(); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_OBJECT, mid.imageName().c_str(), -1); imageInfo.erase(); const char* gtk[] = { "", "", // , "", "", // , "", "", // , "", "", // , "", "", // , "", "", // , "\n" //
}; mid.imageInfo(&imageInfo, true, gtk); /* Problem: GtkLabels have a certain default width which cannot be influenced AFAICT. The only way to make them wider is to include a word whose length exceeds the default width */ // imageInfo += "\n\xa0\x62"; const char* format = (mid.imageShortInfo().empty() ? "%EF2" : _("%EF1 (%EF2)")); imageShortInfo = subst(format, mid.imageShortInfo(), mid.imageName()); updateWindow(); } //______________________________________________________________________ void GtkMakeImage::on_startButton_clicked() { debug("unimplemented"); } void GtkMakeImage::on_pauseButton_clicked() { debug("unimplemented"); } void GtkMakeImage::on_stopButton_clicked() { debug("unimplemented"); } void GtkMakeImage::on_restartButton_clicked() { debug("unimplemented"); } void GtkMakeImage::on_closeButton_clicked() { if (jobList()->isWindowOwner(this)) setNotebookPage(GUI::window.pageOpen); delete this; } jigdo-0.7.3/src/gtk/gtk-makeimage.hh0000644000175000017500000000626010226253017017031 0ustar richardrichard/* $Id: gtk-makeimage.hh,v 1.11 2005/04/10 16:36:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Download and processing of .jigdo files - GTK+ frontend Beware of the interesting ownership relations here: As the front-end, GtkMakeImage creates and owns a MakeImageDl. That MakeImageDl creates child downloads of its own which are owned by the MakeImageDl. GtkSingleUrls are attached to those child downloads. */ #ifndef GTK_MAKEIMAGE_HH #define GTK_MAKEIMAGE_HH #include #include #include //______________________________________________________________________ /** Frontend for Job::MakeImageDl */ class GtkMakeImage : public JobLine, private Job::MakeImageDl::IO { public: GtkMakeImage(const string& uriStr, const string& destDir); virtual ~GtkMakeImage(); // Virtual methods from JobLine virtual bool run(); virtual void selectRow(); virtual bool paused() const; virtual void pause(); virtual void cont(); virtual void stop(); virtual void percentDone(uint64* cur, uint64* total); typedef void (GtkMakeImage::*TickHandler)(); inline void callRegularly(TickHandler handler); inline void callRegularlyLater(const int milliSec, TickHandler handler); // Called from gui.cc void on_startButton_clicked(); void on_pauseButton_clicked(); void on_stopButton_clicked(); void on_restartButton_clicked(); void on_closeButton_clicked(); private: // Virtual methods from Job::MakeImageDl::IO: virtual void job_deleted(); virtual void job_succeeded(); virtual void job_failed(const string& message); virtual void job_message(const string& message); virtual void makeImageDl_new(Job::DataSource* childDownload, const string& uri, const string& destDesc); virtual void makeImageDl_finished(Job::DataSource* childDownload); virtual void makeImageDl_haveImageSection(); // Update info in main window void updateWindow(); string progress, status; // Lines to display in main window string treeViewStatus; // Status section in the list of jobs string dest; // Destination dirname (filename once mid.haveImageSection()) /* Same as mid.imageInfo(), except that
is replaced with \n and

is replaced with \n\n */ string imageInfo; string imageShortInfo; Job::MakeImageDl mid; }; //______________________________________________________________________ /* The static_cast from GtkMakeImage::* to JobLine::* (i.e. member fnc of base class) is OK because we know for certain that the handler will only be invoked on SingleUrl objects. */ void GtkMakeImage::callRegularly(TickHandler handler) { JobLine::callRegularly(static_cast(handler)); } void GtkMakeImage::callRegularlyLater(const int milliSec, TickHandler handler) { JobLine::callRegularlyLater(milliSec, static_cast(handler)); } #endif jigdo-0.7.3/src/gtk/gtk-single-url.cc0000644000175000017500000006232210324272113017156 0ustar richardrichard/* $Id: gtk-single-url.cc,v 1.20 2005/10/15 21:27:39 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. 'Simple' file download, i.e. download data and write it to a file. */ #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("gtk-single-url") // Non-child mode, will create our own SingleUrl GtkSingleUrl::GtkSingleUrl(const string& uriStr, const string& destFile) : childMode(false), uri(uriStr), dest(destFile), progress(), status(), treeViewStatus(), destStream(0), messageBox(), job(0), singleUrl(0), state(CREATED) { debug("GtkSingleUrl %1, not child", this); } // Child mode, using supplied DataSource GtkSingleUrl::GtkSingleUrl(const string& uriStr, const string& destDesc, Job::DataSource* download) : childMode(true), uri(uriStr), dest(destDesc), progress(), status(), treeViewStatus(), destStream(0), messageBox(), job(download), singleUrl(0), state(CREATED) { debug("GtkSingleUrl %1, is child", this); } GtkSingleUrl::~GtkSingleUrl() { debug("~GtkSingleUrl %1", childMode); callRegularly(0); if (jobList()->isWindowOwner(this)) setNotebookPage(GUI::window.pageOpen); if (!childMode) { // Only delete if job owned, i.e. if we're not a child of another job debug("Deleting job %1", job); delete job; Paranoid(job == 0); // Was set to 0 during above stmt by job_deleted() } debug("~GtkSingleUrl done"); } //______________________________________________________________________ // Regular file download (not .jigdo download) bool GtkSingleUrl::run() { state = RUNNING; // Show URL as object name progress.erase(); status = _("Waiting..."); treeViewStatus = _("Waiting"); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), JobList::COLUMN_OBJECT, uri.c_str(), -1); // Don't open output file and don't run() download if child mode if (childMode) return SUCCESS; struct stat fileInfo; int statResult = stat(dest.c_str(), &fileInfo); if (statResult == 0) { if (S_ISDIR(fileInfo.st_mode)) { MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing destination"), subst(_("Cannot save to `%LE1' because that already exists as a " "directory"), dest)); m->show(); state = ERROR; delete this; return FAILURE; } else if (S_ISREG(fileInfo.st_mode)) { // File exists - ask user resumeAsk(&fileInfo); return SUCCESS; } else { // Destination is device or link MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing destination"), subst(_("Cannot save to `%LE1' because that already exists as a " "device/link"), dest)); m->show(); state = ERROR; messageBox.set(m); //delete this; return FAILURE; } } openOutputAndRun(); return SUCCESS; } //______________________________________________________________________ void GtkSingleUrl::openOutputAndRun(/*bool pragmaNoCache*/) { // Open output file Paranoid(!childMode); Paranoid(destStream == 0); destStream = new BfstreamCounted(dest.c_str(), ios::binary|ios::in|ios::out|ios::trunc); if (!*destStream) { MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing destination"), subst(_("Could not open `%LE1' for output: %LE2"), dest, strerror(errno))); m->show(); state = ERROR; messageBox.set(m); return; } // Allocate job and run it status = _("Waiting..."); if (job == 0) job = singleUrl = new Job::SingleUrl(uri); debug("singleUrl=%1", singleUrl); singleUrl->io.addListener(*this); singleUrl->setDestination(destStream.get(), 0, 0); //singleUrl->setPragmaNoCache(pragmaNoCache); singleUrl->run(); } //________________________________________ void GtkSingleUrl::openOutputAndResume() { Paranoid(!childMode); Paranoid(destStream == 0); struct stat fileInfo; int statResult = stat(dest.c_str(), &fileInfo); if (statResult == 0) { //if (destStream != 0) delete destStream; destStream = new BfstreamCounted(dest.c_str(), ios::binary|ios::in|ios::out); if (*destStream) { // Start the resume download state = RUNNING; status = subst(_("Resuming download - overlap is %1kB"), Job::SingleUrl::RESUME_SIZE / 1024); updateWindow(); if (job == 0) job = singleUrl = new Job::SingleUrl(uri); debug("singleUrl=%1", singleUrl); iList_remove(); singleUrl->io.addListener(*this); singleUrl->setResumeOffset(fileInfo.st_size); singleUrl->setDestination(destStream.get(), 0, 0); singleUrl->run(); return; } } // An error occurred debug("openOutputAndResume: statResult=%1, %2", statResult, strerror(errno)); treeViewStatus = _("Open of output file failed"); string error = subst(_("Could not open output file: %LE1"), strerror(errno)); failedPermanently(&error); } //______________________________________________________________________ /* Auto-resume, called e.g. after the connection dropped unexpectedly, to resume the download. */ void GtkSingleUrl::startResume() { Paranoid(!childMode); Paranoid(job != 0); Paranoid(singleUrl->resumePossible()); // We already checked this earlier callRegularly(0); Assert(destStream == 0); # if DEBUG struct stat fileInfo; int statResult = stat(dest.c_str(), &fileInfo); Paranoid(statResult == 0); debug("startResume: Trying resume from %1, actual file size %2", job->progress()->currentSize(), uint64(fileInfo.st_size)); Paranoid(job->progress()->currentSize() == uint64(fileInfo.st_size)); # endif destStream = new BfstreamCounted(dest.c_str(), ios::binary|ios::in|ios::out); if (!*destStream) { // An error occurred treeViewStatus = _("Open of output file failed"); string error = subst(_("Could not open output file: %LE1"), strerror(errno)); failedPermanently(&error); return; } // Start the resume download state = RUNNING; status = subst(_("Resuming download - overlap is %1kB"), Job::SingleUrl::RESUME_SIZE / 1024); updateWindow(); iList_remove(); singleUrl->io.addListener(*this); singleUrl->setResumeOffset(job->progress()->currentSize()); singleUrl->setDestination(destStream.get(), 0, 0); singleUrl->run(); return; } //______________________________________________________________________ void GtkSingleUrl::selectRow() { setNotebookPage(GUI::window.pageDownload); jobList()->setWindowOwner(this); updateWindow(); } //______________________________________________________________________ bool GtkSingleUrl::paused() const { return job != 0 && state == PAUSED; } void GtkSingleUrl::pause() { if (paused()) return; if (job != 0) { job->pause(); state = PAUSED; } //progress.clear(); status = _("Download is paused"); updateWindow(); showProgress(); // Display "50kB of 100kB, paused" in GtkTreeView } void GtkSingleUrl::cont() { if (!paused()) return; if (job != 0) { job->cont(); state = RUNNING; } status.erase(); updateWindow(); progress.erase(); } void GtkSingleUrl::stop() { Paranoid(!childMode); debug("Stopping SingleUrl %1 at byte %2", singleUrl, singleUrl->progress()->currentSize()); destStream->sync(); destStream.clear(); if (job != 0) { singleUrl->setDestination(0, 0, 0); if (state == PAUSED) singleUrl->cont(); state = STOPPED; singleUrl->stop(); } //progress.erase(); status = _("Download was stopped manually"); updateWindow(); } //______________________________________________________________________ void GtkSingleUrl::percentDone(uint64* cur, uint64* total) { if (job == 0 || job->progress()->dataSize() == 0) { if (state == SUCCEEDED) *cur = *total = 1; else *cur = *total = 0; } else { *cur = job->progress()->currentSize(); *total = job->progress()->dataSize(); } } //______________________________________________________________________ void GtkSingleUrl::updateWindow() { if (!jobList()->isWindowOwner(this)) return; debug("updateWindow: state=%1 status=\"%2\"", int(state), status); // URL and destination lines gtk_label_set_text(GTK_LABEL(GUI::window.download_URL), uri.c_str()); gtk_label_set_text(GTK_LABEL(GUI::window.download_dest), dest.c_str()); // Progress and status lines if (job != 0 && (state == RUNNING || state == STOPPED)) { progress.erase(); job->progress()->appendProgress(&progress); } gtk_label_set_text(GTK_LABEL(GUI::window.download_progress), progress.c_str()); gtk_label_set_text(GTK_LABEL(GUI::window.download_status), status.c_str()); // Buttons (in)sensitive gboolean canStart = FALSE; if (job != 0) { if (state == PAUSED) canStart = TRUE; else if (!childMode && (/*state == SUCCEEDED ||*/ state == STOPPED ||state == ERROR && singleUrl->resumePossible())) canStart = TRUE; } gtk_widget_set_sensitive(GUI::window.download_startButton, canStart); gtk_widget_set_sensitive(GUI::window.download_pauseButton, FALSE); // once libcurl allows pausing: (job != 0 && state == RUNNING ? TRUE : FALSE)); gtk_widget_set_sensitive(GUI::window.download_stopButton, (job != 0 && !childMode && (state == RUNNING || state == PAUSED) ? TRUE : FALSE)); gtk_widget_set_sensitive(GUI::window.download_restartButton, (!childMode ? TRUE : FALSE)); } //______________________________________________________________________ // fileInfo is stat() result of destination filename void GtkSingleUrl::resumeAsk(struct stat* fileInfo) { // How old is the file in minutes? time_t fileAge = (time(0) - fileInfo->st_mtime + 30) / 60; int days = fileAge / (60*24); int hours = fileAge / 60 - days * 24; int minutes = fileAge - (days * 60*24) - (hours * 60); string age; if (days > 0) age = subst(_("%1 days and %2 hours"), days, hours); else if (hours > 0) age = subst(_("%1 hours and %2 minutes"), hours, minutes); else age = subst(_("%1 minutes"), minutes); MessageBox* m = new MessageBox(MessageBox::QUESTION, MessageBox::NONE, _("Output file exists - overwrite it or resume download?"), subst(_("The output file `%LE1' already exists with a size of %2 " "bytes, it is %3 old.\n" "Overwrite deletes the data in this file, whereas " "Resume can be used to continue an earlier, interrupted " "download of the same file."), dest, static_cast(fileInfo->st_size), age)); m->addStockButton("gtk-cancel", GTK_RESPONSE_CANCEL); m->addButton(_("_Overwrite"), 0); m->addButton(_("_Resume"), 1); m->onResponse(&resumeResponse, this); m->show(); messageBox.set(m); progress.erase(); status = _("Please answer the pop-up: Overwrite, resume or cancel?"); updateWindow(); messageBox.set(m); } void GtkSingleUrl::resumeResponse(GtkDialog*, int r, gpointer data) { GtkSingleUrl* self = static_cast(data); if (r == GTK_RESPONSE_CANCEL || r == GTK_RESPONSE_DELETE_EVENT) { delete self; return; } // Remove "please react to pop-up" message self->status.erase(); if (r == 0) { // Overwrite self->openOutputAndRun(); } else { // Resume Paranoid(r == 1); self->openOutputAndResume(); } self->updateWindow(); self->messageBox.set(0); /* Must call this now and not before the "if" above becase openOutputAndResume() makes state!=PAUSED, which is what should be displayed */ if (self->jobList()->isWindowOwner(self)) self->updateWindow(); } //______________________________________________________________________ /* The job whose io ptr references us is being deleted. */ void GtkSingleUrl::job_deleted() { debug("job_deleted job=singleUrl=0"); job = singleUrl = 0; return; } //______________________________________________________________________ // Called when download succeeds void GtkSingleUrl::job_succeeded() { debug("job_succeeded"); callRegularly(0); // Can't see the same problem as with job_failed(), but just to be safe... destStream.clear(); if (!childMode) singleUrl->setDestination(0, 0, 0); string s; Progress::appendSize(&s, job->progress()->currentSize()); progress.erase(); status = subst(_("Download is complete - fetched %1 (%2 bytes)"), s, job->progress()->currentSize()); if (state != STOPPED) state = SUCCEEDED; updateWindow(); s = subst(_("Finished - fetched %1"), s); job_message(s); } //______________________________________________________________________ /* Like job_failed() below, but don't pay attention whether job->resumePossible() - *never* auto-resume the download. */ void GtkSingleUrl::failedPermanently(string* message) { if (!childMode) singleUrl->setDestination(0, 0, 0); destStream.clear(); state = ERROR; treeViewStatus = subst(_("%1"), message); status.swap(*message); updateWindow(); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); } //______________________________________________________________________ void GtkSingleUrl::job_failed(const string& message) { /* Important: close the file here. Otherwise, the following can happen: 1) Download aborts with an error, old JobLine still displays error message, isn't deleted yet; its stream stays open. 2) User restarts download, a SECOND stream is created. It's created with ios::trunc, but that's ignored (under Linux) cos of the second open handle. 3) If the second JobLine doesn't overwrite all data from the first one, we end up with file contents like "data from 2nd download" + "up to a page full of null bytes" + "stale data from 1st download". */ Paranoid(job != 0); if (!childMode) singleUrl->setDestination(0, 0, 0); destStream.clear(); if (state != STOPPED) state = ERROR; bool resumePossible = (job != 0 && !childMode && state == ERROR && singleUrl->resumePossible()); debug("job_failed: %1 job=%2 state=%3 resumePossible=%4", message, job, state, resumePossible); if (resumePossible) { treeViewStatus = subst(_("Try %1 of %2 after %E3"), singleUrl->currentTry() + 1, Job::SingleUrl::MAX_TRIES, message); } else { treeViewStatus = subst(_("%E1"), message); } //debug("job_failed: %1", message); status = message; if (progress.empty()) progress = _("Failed:"); updateWindow(); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); if (resumePossible) callRegularlyLater(Job::SingleUrl::RESUME_DELAY, &GtkSingleUrl::startResume); } //______________________________________________________________________ void GtkSingleUrl::job_message(const string& message) { treeViewStatus = message; gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); } //______________________________________________________________________ // Don't need this info - ignore void GtkSingleUrl::dataSource_dataSize(uint64) { return; } //______________________________________________________________________ void GtkSingleUrl::dataSource_data(const byte* /*data*/, unsigned /*size*/, uint64 /*currentSize*/) { //debug("dataSource_data %1", job->progress()->currentSize()); if (!needTicks()) callRegularly(&GtkSingleUrl::showProgress); if (childMode) return; if (!*destStream) { /* According to Stroustrup, "all bets are off" WRT the state of the stream. We assume that this does *not* mean that junk has been written, only that no all 'size' bytes were written. */ treeViewStatus = _("Write to output file failed"); string error = subst(_("Could not write to output file: %L1"), strerror(errno)); failedPermanently(&error); } } //______________________________________________________________________ // Show progress info. void GtkSingleUrl::showProgress() { // bool isOwner = jobList()->isWindowOwner(this); // if (progress.currentSize() == 0) { // // No data received yet - set label blank // if (isOwner) { // gtk_label_set_text(GTK_LABEL(GUI::window.download_progress), ""); // gtk_label_set_text(GTK_LABEL(GUI::window.download_status), ""); // } // return; // } // debug("%1", *job->progress()); if (job == 0 || state == ERROR || state == SUCCEEDED || state == STOPPED) return; Job::SingleUrl* jobx = dynamic_cast(job); bool resuming = (jobx != 0 && jobx->resuming()); GTimeVal now; g_get_current_time(&now); string s; const Progress* jobProgress = job->progress(); int timeLeft = jobProgress->timeLeft(now); int speed = 0; treeViewStatus.erase(); // Append "50kB" if size not known, else "50kB of 10MB" Progress::appendSizes(&treeViewStatus, jobProgress->currentSize(), jobProgress->dataSize()); // Append "10kB/s" if (paused()) { // Switch off calls to updateProgress() until user clicks Continue callRegularly(0); treeViewStatus += _(", paused"); } else { speed = jobProgress->speed(now); if (speed == 0) { treeViewStatus += _(", stalled"); } else if (speed != -1) { treeViewStatus += _(", "); Progress::appendSize(&treeViewStatus, speed); treeViewStatus += _("/s"); } } if (!resuming) gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); //____________________ if (jobList()->isWindowOwner(this)) { // Percentage/kBytes/bytes done in main window progress.erase(); jobProgress->appendProgress(&progress); gtk_label_set_text(GTK_LABEL(GUI::window.download_progress), progress.c_str()); // Speed/ETA in main window if (!paused() && !resuming) { status.erase(); jobProgress->appendSpeed(&status, speed, timeLeft); gtk_label_set_text(GTK_LABEL(GUI::window.download_status), status.c_str()); } } } //______________________________________________________________________ void GtkSingleUrl::on_startButton_clicked() { if (paused()) { cont(); gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); return; } if (job == 0 || childMode || !(state == ERROR || state == SUCCEEDED || state == STOPPED)) { Assert(false); return; } // Resume download Paranoid(!childMode); struct stat fileInfo; int statResult = stat(dest.c_str(), &fileInfo); // Scream if file no longer there, or not a file if (statResult != 0 || !S_ISREG(fileInfo.st_mode)) { string error; if (statResult != 0) error = subst(_("Resuming `%LE1' is not possible: %LE2"), dest, strerror(errno)); else error = subst(_("Resuming `%LE1' is not possible: It is not a regular " "file"), dest); MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing output file"), error); m->show(); messageBox.set(m); return; } // Scream if file/file size changed since download was stopped const Progress* jobProgress = job->progress(); uint64 size = static_cast(fileInfo.st_size); if (jobProgress->currentSize() != size) { MessageBox* m = new MessageBox(MessageBox::WARNING, MessageBox::NONE, _("Size of output file changed"), subst(_("When the download was stopped, the length of the output file " "`%LE1' was %2 bytes, but now it is %3 bytes. Do you really " "want to resume the download (from byte %3)?"), dest, jobProgress->currentSize(), size)); m->addButton(_("_Resume"), 0); m->addStockButton("gtk-cancel", GTK_RESPONSE_CANCEL); m->onResponse(&afterStartButtonClickedResponse, this); m->show(); messageBox.set(m); return; } // All OK, resume openOutputAndResume(); state = RUNNING; updateWindow(); gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); } void GtkSingleUrl::afterStartButtonClickedResponse(GtkDialog*, int r, gpointer data) { GtkSingleUrl* self = static_cast(data); self->messageBox.set(0); if (r == GTK_RESPONSE_CANCEL || r == GTK_RESPONSE_DELETE_EVENT) return; self->openOutputAndResume(); self->updateWindow(); gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); } //______________________________________________________________________ void GtkSingleUrl::on_closeButton_clicked() { if (job == 0 || state == PAUSED || state == ERROR || state == SUCCEEDED || state == STOPPED || job->progress()->currentSize() == 0) { delete this; return; } const char* question; const char* buttonText; if (dynamic_cast(job) != 0) { question = _("Download in progress - really abort it?"); buttonText = _("_Abort download"); } else { question = _("Data stream active - really abort it?"); buttonText = _("_Abort data stream"); } MessageBox* m = new MessageBox(MessageBox::WARNING, MessageBox::NONE, question, 0); m->addStockButton("gtk-cancel", GTK_RESPONSE_CANCEL); m->addButton(buttonText, 0); m->onResponse(&afterCloseButtonClickedResponse, this); m->show(); messageBox.set(m); } void GtkSingleUrl::afterCloseButtonClickedResponse(GtkDialog*, int r, gpointer data) { if (r == GTK_RESPONSE_CANCEL || r == GTK_RESPONSE_DELETE_EVENT) return; GtkSingleUrl* self = static_cast(data); debug("afterCloseButtonClickedResponse: deleting %1", self); delete self; } //______________________________________________________________________ void GtkSingleUrl::on_restartButton_clicked() { messageBox.set(0); // Close "overwrite/resume/cancel" question, if any if (job == 0 || state == ERROR) { restart(); return; } /* Trying to be clever here (oh dear...): Only ask whether to discard existing data if downloading that data took longer than half a minute (RESTART_WARNING_THRESHOLD seconds). The idea is that a wrong click will at most cost you 30 secs to recover, which happens to be the limit recommended by the GNOME usability people. :) */ const Progress* jobProgress = job->progress(); unsigned speed; if (paused()) { speed = jobProgress->speed(pauseStart); // speed = bytes per sec } else { GTimeVal now; g_get_current_time(&now); speed = jobProgress->speed(now); // speed = bytes per sec } /* speed will be 0 after Pause+Stop, in that case just assume an arbitrary speed of 64kB/sec, which is better than nothing... */ if (speed == 0) speed = 65536; debug("on_restartButton_clicked: cur=%2 speed=%1 thresh=%3", speed, jobProgress->currentSize(), speed * RESTART_WARNING_THRESHOLD); if (speed == 0 || jobProgress->currentSize() < speed * RESTART_WARNING_THRESHOLD) { restart(); return; } MessageBox* m = new MessageBox(MessageBox::WARNING, MessageBox::NONE, _("Restarting will discard already downloaded data"), _("Some data has already been downloaded. Are you sure you want to " "delete this data and restart the download?")); m->addStockButton("gtk-cancel", GTK_RESPONSE_CANCEL); m->addButton(_("_Restart"), 0); m->onResponse(&afterRestartButtonClickedResponse, this); m->show(); messageBox.set(m); } void GtkSingleUrl::afterRestartButtonClickedResponse(GtkDialog*, int r, gpointer data) { if (r == GTK_RESPONSE_CANCEL || r == GTK_RESPONSE_DELETE_EVENT) return; GtkSingleUrl* self = static_cast(data); self->restart(); } void GtkSingleUrl::restart() { Assert(!childMode); // Kill the old download destStream.clear(); if (singleUrl != 0) { singleUrl->setDestination(0, 0, 0); if (state == PAUSED) singleUrl->cont(); singleUrl->stop(); } // Start the new download status = _("Download was restarted - waiting..."); treeViewStatus = _("Restarted - waiting"); openOutputAndRun(/*true*/); state = RUNNING; progress.erase(); updateWindow(); gtk_tree_store_set(jobList()->store(), row(), JobList::COLUMN_STATUS, treeViewStatus.c_str(), -1); } void GtkSingleUrl::deleteThis() { debug("deleteThis %1", this); delete this; } jigdo-0.7.3/src/gtk/gtk-single-url.hh0000644000175000017500000001615610261546437017211 0ustar richardrichard/* $Id: gtk-single-url.hh,v 1.17 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file 'Simple' file download, i.e. download data and write it to a file. */ #ifndef GTK_SINGLE_URL_HH #define GTK_SINGLE_URL_HH #include #include #include #include #include #include #include #include //______________________________________________________________________ /** This class is two things at once: 1) Normal GtkSingleUrl mode: The frontend for a single-file download. It is a JobLine, so can be paused etc. like all JobLines. It registers as a Job::SingleUrl::IO with the Job::SingleUrl _that_it_owns_, to be notified whenever the job has something to say. 2) GtkDataSource-which-just-isnt-called-like-that, aka "child mode": The frontend for a Job::DataSource object. This DataSource object is _not_ owned by the GtkSingleUrl object. This is used if a MakeImageDl starts new child downloads. The two modes share so much code that IMHO doing two classes would not be better. */ class GtkSingleUrl : public JobLine, public Job::DataSource::IO { public: /** Only in child mode, delay (millisec) between the MakeImageDl telling us that it has deleted its child and the moment we delete the corresponding line from the JobList. The delay allows the user to read the "finished" message. */ static const int CHILD_FINISHED_DELAY = 10000; /** Create a new GtkSingleUrl, and also create an internal Job::SingleUrl to do the actual download. Delete the internal SingleURl from ~GtkSingleUrl(). */ GtkSingleUrl(const string& uriStr, const string& destFile); /** Create a new GtkSingleUrl, but use it with an already existing Job::DataSource. The Job::DataSource will not be owned by the GtkSingleUrl and will not be deleted by its dtor. This mode is used to create "child downloads" during .jigdo processing: The IO object registered with the supplied Job::DataSource is set up to do with the data what it wants, and then to pass the call on to this GtkSingleUrl (more accurately, the methods that this GtkSingleUrl inherits from Job::DataSource::IO). See also GtkMakeImage::makeImageDl_new(). @param uriStr URL @param destDesc A descriptive string like "/foo/bar/image, offset 3453", NOT a filename! Supplied for information only, to be displayed to the user. @param download The download we are attached to */ GtkSingleUrl(const string& uriStr, const string& destDesc, Job::DataSource* download); virtual ~GtkSingleUrl(); // Virtual methods from JobLine virtual bool run(); virtual void selectRow(); virtual bool paused() const; virtual void pause(); virtual void cont(); virtual void stop(); virtual void percentDone(uint64* cur, uint64* total); typedef void (GtkSingleUrl::*TickHandler)(); inline void callRegularly(TickHandler handler); inline void callRegularlyLater(const int milliSec, TickHandler handler); // Called from gui.cc void on_startButton_clicked(); inline void on_pauseButton_clicked(); inline void on_stopButton_clicked(); void on_restartButton_clicked(); void on_closeButton_clicked(); /** Cause this child download to remove itself from the JobList after CHILD_FINISHED_DELAY */ inline void childIsFinished(); private: enum State { CREATED, RUNNING, PAUSED, STOPPED, ERROR, SUCCEEDED }; // From Job::DataSource:IO virtual void job_deleted(); virtual void job_succeeded(); virtual void job_failed(const string& message); virtual void job_message(const string& message); virtual void dataSource_dataSize(uint64 n); virtual void dataSource_data(const byte* data, unsigned size, uint64 currentSize); // Helper methods /* Registered to be called for each tick by run(), updates the "% done" progress info. */ void showProgress(); // Update info in main window void updateWindow(); void resumeAsk(struct stat* fileInfo); // Ask user "resume/overwrite?" static void resumeResponse(GtkDialog*, int r, gpointer data); void openOutputAndRun(/*bool pragmaNoCache = false*/); // Allocate download job void openOutputAndResume(); // Alloc job and read resume data from file void updateTreeView(); // Update our line in GtkTreeView void failedPermanently(string* message); void startResume(); // Reload with "Pragma: no-cache" header, discards previously fetched data void restart(); /** Just executes "delete this". Used to remove the job line with callRegularlyLater() if this is a child download. */ void deleteThis(); // Only ask user if discarded data not worth more seconds than this static const int RESTART_WARNING_THRESHOLD = 30; static void afterStartButtonClickedResponse(GtkDialog*, int r, gpointer); static void afterCloseButtonClickedResponse(GtkDialog*, int r, gpointer); static void afterRestartButtonClickedResponse(GtkDialog*, int r, gpointer); /* true if the object was created using the second ctor, i.e. with a pre-created Job::DataSource object. */ bool childMode; string uri; // Source URI string dest; // Destination filename string progress, status; // Lines to display in main window string treeViewStatus; // Status section in the list of jobs SmartPtr destStream; MessageBox::Ref messageBox; // If !childMode, the next two point to the same object! Job::DataSource* job; // Pointer to SingleUrl or to DataSource object Job::SingleUrl* singleUrl; // Pointer to SingleUrl object, or null GTimeVal pauseStart; // timestamp of last download pause, or uninitialized State state; }; //______________________________________________________________________ /* The static_cast from GtkSingleUrl::* to JobLine::* (i.e. member fnc of base class) is OK because we know for certain that the handler will only be invoked on SingleUrl objects. */ void GtkSingleUrl::callRegularly(TickHandler handler) { JobLine::callRegularly(static_cast(handler)); } void GtkSingleUrl::callRegularlyLater(const int milliSec, TickHandler handler) { JobLine::callRegularlyLater(milliSec, static_cast(handler)); } //________________________________________ void GtkSingleUrl::on_pauseButton_clicked() { if (paused()) return; g_get_current_time(&pauseStart); pause(); gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); } void GtkSingleUrl::on_stopButton_clicked() { stop(); gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); } //________________________________________ void GtkSingleUrl::childIsFinished() { if (state == SUCCEEDED) callRegularlyLater(CHILD_FINISHED_DELAY, &GtkSingleUrl::deleteThis); } #endif jigdo-0.7.3/src/gtk/gui.cc0000644000175000017500000003533610264301761015110 0ustar richardrichard/* $Id: gui.cc,v 1.16 2005/07/10 20:24:17 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Interface to GTK GUI. Sets up global variables pointing to the main GUI elements. "Glue code" between the interface as generated by glade (in gtk-interface.*, gtk-support.*) and jigdo's functionality. */ #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("gui") /* Not all of these are set up during create() - we rely on the vars being initialized to 0 in that case. */ GUI::Window GUI::window; GUI::Filesel GUI::filesel; GUI::License GUI::license; //______________________________________________________________________ // File selection dialogue - helper functions namespace { using namespace GUI; GtkEntry* filesel_state = 0; // Private function for filesel_open below void on_filesel_okButton(GtkWidget*, gpointer) { if (filesel_state != 0) { // Copy over filename to the right GtkEntry GtkFileSelection* fileselObj = GTK_FILE_SELECTION(filesel.filesel); gtk_entry_set_text(filesel_state, gtk_file_selection_get_filename(fileselObj)); } gtk_widget_hide(filesel.filesel); // Close file selection window } /* Called to open filesel on e.g. a button click. When filesel's OK button is clicked, the selected filename is copied to "entry". */ void filesel_open(GtkWidget*, gpointer entry) { gtk_widget_show(filesel.filesel); filesel_state = GTK_ENTRY(entry); } } //______________________________________________________________________ /* Initialisation of the GUI. Mostly does some post-processing of the glade-generated data structures, connecting button press events to the corresponding function etc. */ void GUI::create() { debug("create"); window.create(); filesel.create(); GUI::jobList.postGtkInit(); /* Remove tabs from main window notebook, since the user is only allowed to switch between the pages using clicks on buttons/list entries. */ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(window.invisibleNotebook),FALSE); // Set background white for jigdo welcome screen GdkColor white; gdk_color_parse("white", &white); gtk_widget_modify_bg(window.aboutBgnd, GTK_STATE_NORMAL, &white); // GtkRcStyle* aboutStyle = gtk_widget_get_modifier_style(window.aboutBgnd); // gdk_color_parse("white", &aboutStyle->bg[0]); // ... modify style - how? // gtk_widget_modify_style(window.aboutBgnd, aboutStyle); gtk_widget_set_name(window.aboutBgnd, "aboutBgnd"); gtk_rc_parse_string("style \"whitebg\" { bg[NORMAL] = \"white\" }"); // base[NORMAL] = \"white\" gtk_rc_parse_string("widget \"aboutBgnd\" style \"whitebg\""); // Set string "Jigsaw Download x.y.z" on welcome screen GtkLabel* aboutJigdoLabel = GTK_LABEL(GUI::window.aboutJigdoLabel); string banner = subst(_( "" "Jigsaw Download %F1\n" "Copyright 2001-%2 Richard Atterer\n" "http://atterer.net/jigdo"), JIGDO_VERSION, CURRENT_YEAR); gtk_label_set_markup(aboutJigdoLabel, banner.c_str()); gtk_label_set_justify(aboutJigdoLabel, GTK_JUSTIFY_CENTER); //gtk_label_set_justify(GTK_LABEL(GUI::window.aboutJigdoButtonLabel), // GTK_JUSTIFY_CENTER); gtk_label_set_markup(GTK_LABEL(GUI::window.aboutJigdoButtonLabel), _( "This is Free Software, distributable under the " "terms of the GNU GPL v2.")); // Set width of image_Info label to something bigger than the default GtkRequisition infoReq; gtk_widget_size_request(GUI::window.window, &infoReq); //gtk_widget_get_size_request(GUI::window.jigdo_InfoVbox, &infoReq.width, //&infoReq.height); debug("updateWindow width=%1", infoReq.width); int width = infoReq.width - 100; if (width < 200) width = 450; gtk_widget_set_size_request(GUI::window.jigdo_Info, width, -1); /* Handler to copy filename to right GtkEntry when OK is pressed in filesel */ g_signal_connect(G_OBJECT(filesel.okButton), "clicked", G_CALLBACK(on_filesel_okButton), NULL); /* Open filesel window when buttons are clicked, enter result in appropriate GtkEntry */ g_signal_connect(G_OBJECT(window.open_URLSel), "clicked", G_CALLBACK(filesel_open), G_OBJECT(window.open_URL)); g_signal_connect(G_OBJECT(window.open_destSel), "clicked", G_CALLBACK(filesel_open), G_OBJECT(window.open_dest)); gtk_widget_hide_all(window.toolbarReuse); # if WINDOWS gtk_entry_set_text(GTK_ENTRY(window.open_dest), g_get_home_dir()); # else char* cwd = g_get_current_dir(); gtk_entry_set_text(GTK_ENTRY(window.open_dest), cwd); g_free(cwd); # endif # if DEBUG gtk_entry_set_text(GTK_ENTRY(window.open_URL), //"ftp://localhost/image" //"http://localhost:8000/~richard/ironmaiden/part32" # if WINDOWS "http://192.168.1.5:8000/~richard/ironmaiden/image.jigdo" # else "http://127.0.0.1:8000/~richard/ironmaiden/image.jigdo" # endif ); # endif debug("create end"); } //______________________________________________________________________ // Callback handlers void on_toolbarExit_clicked(GtkButton*, gpointer) { gtk_main_quit(); } gboolean on_window_delete_event(GtkWidget*, GdkEvent*, gpointer) { gtk_main_quit(); return TRUE; } void setNotebookPage(GtkWidget* pageObject) { GUI::jobList.setWindowOwner(0); GtkNotebook* notebook = GTK_NOTEBOOK(pageObject->parent); gboolean sensitive = (pageObject == GUI::window.pageOpen ? FALSE : TRUE); gtk_widget_set_sensitive(GUI::window.toolbarOpen, sensitive); gtk_notebook_set_current_page(notebook, gtk_notebook_page_num(notebook, pageObject)); } void on_openButton_clicked(GtkButton*, gpointer) { /* Create either a JobLine to download a single file, or one to process a .jigdo file, and GUI::jobList.append() it. */ const char* uri = gtk_entry_get_text(GTK_ENTRY(window.open_URL)); const char* dest = gtk_entry_get_text(GTK_ENTRY(window.open_dest)); JobLine::create(uri, dest); } //________________________________________ namespace { // Set up a text buffer which displays the jigdo license void setLicenseText(GtkTextBuffer* textBuf) { GtkTextTag* large = gtk_text_buffer_create_tag( textBuf, NULL, "scale", 2.0, NULL); GtkTextTag* center = gtk_text_buffer_create_tag( textBuf, NULL, "justification", GTK_JUSTIFY_CENTER, NULL); GtkTextIter iter; gtk_text_buffer_get_end_iter(textBuf, &iter); gtk_text_buffer_insert_with_tags(textBuf, &iter, _("\nJigsaw Download License\n"), -1, large, center, NULL); string copy = subst(_("\n" "\tCopyright © 2001-%1 Richard Atterer \n" "\tJigsaw Download homepage: http://atterer.net/jigdo\n" "\n"), CURRENT_YEAR, '@'); gtk_text_buffer_insert_with_tags(textBuf, &iter, copy.c_str(), -1, center, NULL); gtk_text_buffer_insert(textBuf, &iter, _( "Jigsaw Download 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.\n" "\n" "In addition, as a special exception, the author gives permission " "to link the jigdo code with the OpenSSL project's \"OpenSSL\" library " "(or with modified versions of it that use the same license as the " "\"OpenSSL\" library), and to distribute the linked executables.\n"), -1); gtk_text_buffer_insert_with_tags(textBuf, &iter, "______________________________\n\n", -1, center, NULL); gtk_text_buffer_insert(textBuf, &iter, _( "Please note: The copyright notice below only applies to the text of " "the GNU General Public License; the copyright of the program is as " "specified above. Also note that jigdo is licensed under GPL version " "_2_ and no other version.\n" "\n\n"), -1); string copying = packageDataDir; copying += "COPYING"; static const int BUF_SIZE = 1024; char buf[BUF_SIZE]; ifstream file(copying.c_str()); while (file) { file.read(buf, BUF_SIZE); size_t n = file.gcount(); for (size_t i = 1; i < n - 2; ++i) { // Very simple reformatting if (buf[i] == '\f') buf[i] = '\n'; else if (buf[i] == '\n' && buf[i - 1] != '\n' && isgraph(buf[i + 1])) buf[i] = ' '; else if (buf[i] == '\n') { } // Whoa, we're supposed to be outputting UTF8 - just delete non-ASCII else if ((buf[i] < 32 || buf[i] >= 127) && buf[i] != '\t') buf[i] = '?'; } gtk_text_buffer_insert(textBuf, &iter, buf, file.gcount()); } if (!file.eof() && !file) { string err = subst(_("Error loading `%LE1': %LE2"), copying, strerror(errno)); gtk_text_buffer_insert(textBuf, &iter, err.c_str(), -1); } } } // namespace //________________________________________ // Display jigdo license void on_aboutJigdoButton_clicked(GtkButton*, gpointer) { if (GUI::license.license == 0) { GUI::license.create(); GtkTextView* view = GTK_TEXT_VIEW(GUI::license.licenseText); GtkTextBuffer* textBuf = gtk_text_view_get_buffer(view); setLicenseText(textBuf); GtkTextIter iter; gtk_text_buffer_get_start_iter(textBuf, &iter); gtk_text_buffer_place_cursor(textBuf, &iter); GdkGeometry geom; geom.min_width = geom.min_height = 1; gtk_window_set_geometry_hints(GTK_WINDOW(GUI::license.license), GUI::license.licenseText, &geom, GDK_HINT_MIN_SIZE); } gtk_window_present(GTK_WINDOW(GUI::license.license)); } //________________________________________ void on_download_startButton_clicked(GtkButton*, gpointer) { GtkSingleUrl* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_startButton_clicked(); } void on_download_pauseButton_clicked(GtkButton*, gpointer) { GtkSingleUrl* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_pauseButton_clicked(); } void on_download_stopButton_clicked(GtkButton*, gpointer) { GtkSingleUrl* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_stopButton_clicked(); } void on_download_restartButton_clicked(GtkButton*, gpointer) { GtkSingleUrl* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_restartButton_clicked(); } void on_download_closeButton_clicked(GtkButton*, gpointer) { GtkSingleUrl* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_closeButton_clicked(); } void on_download_startButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.download_buttonInfo), _("Continue download (after Pause) or " "Resume it (after Stop)")); } void on_download_pauseButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.download_buttonInfo), _("Pause download, but leave connection " "to server open")); } void on_download_stopButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.download_buttonInfo), _("Stop download by closing the connection")); } void on_download_restartButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.download_buttonInfo), _("Restart download - the data downloaded so " "far is discarded")); } void on_download_closeButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.download_buttonInfo), _("Close this download and remove it from the " "list below")); } void on_download_button_leave(GtkButton*, gpointer) { gtk_label_set_text(GTK_LABEL(GUI::window.download_buttonInfo), ""); } //________________________________________ void on_jigdo_startButton_clicked(GtkButton*, gpointer) { GtkMakeImage* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_startButton_clicked(); } void on_jigdo_pauseButton_clicked(GtkButton*, gpointer) { GtkMakeImage* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_pauseButton_clicked(); } void on_jigdo_stopButton_clicked(GtkButton*, gpointer) { GtkMakeImage* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_stopButton_clicked(); } void on_jigdo_restartButton_clicked(GtkButton*, gpointer) { GtkMakeImage* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_restartButton_clicked(); } void on_jigdo_closeButton_clicked(GtkButton*, gpointer) { GtkMakeImage* j = dynamic_cast(GUI::jobList.windowOwner()); if (j != 0) j->on_closeButton_clicked(); } void on_jigdo_startButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_buttonInfo), _("Continue all child downloads (after Pause) " "or Resume them (after Stop or error)")); } void on_jigdo_pauseButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_buttonInfo), _("Pause all child downloads, but leave " "connections to servers open")); } void on_jigdo_stopButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_buttonInfo), _("Stop download by closing connections of " "all children")); } void on_jigdo_restartButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_buttonInfo), _("Restart download and processing of " ".jigdo - the .jigdo data " "downloaded so far (and only it) is discarded")); } void on_jigdo_closeButton_enter(GtkButton*, gpointer) { gtk_label_set_markup(GTK_LABEL(GUI::window.jigdo_buttonInfo), _("Close this download and all its children, " "and remove them from the list below")); } void on_jigdo_button_leave(GtkButton*, gpointer) { gtk_label_set_text(GTK_LABEL(GUI::window.jigdo_buttonInfo), ""); } jigdo-0.7.3/src/gtk/gui.hh0000644000175000017500000000614710226060300015105 0ustar richardrichard#/* $Id: gui.hh,v 1.2 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file GTK event handlers. Global variables: Pointers to GUI elements. */ #ifndef GTK_GUI_HH #define GTK_GUI_HH #include #include //______________________________________________________________________ /* Under Unix, packageDataDir is a string constant like "/usr/local/share/jigdo/" and is determined at compile time. Under Windows, it is a string containing the name of the dir with the .exe file. Similar for packageLocaleDir. */ #if WINDOWS # include extern string packageDataDir; // defined in jigdo.cc # define packageLocaleDir (packageDataDir.c_str()) #else # define packageDataDir PACKAGE_DATA_DIR # define packageLocaleDir PACKAGE_LOCALE_DIR #endif //______________________________________________________________________ namespace GUI { // Are initialized by create() extern Window window; extern Filesel filesel; extern License license; // To be called by main() to set up the variables above void create(); } // namespace GUI //______________________________________________________________________ // Callback prototypes for gtk-interface.cc // Defined in gtk-gui.cc void on_toolbarExit_clicked(GtkButton*, gpointer); gboolean on_window_delete_event(GtkWidget*, GdkEvent*, gpointer); void setNotebookPage(GtkWidget* pageObject); void on_openButton_clicked(GtkButton*, gpointer); void on_toolbarExit_clicked(GtkButton*, gpointer); void on_aboutJigdoButton_clicked(GtkButton*, gpointer); void on_download_startButton_enter(GtkButton*, gpointer); void on_download_startButton_clicked(GtkButton*, gpointer); void on_download_pauseButton_enter(GtkButton*, gpointer); void on_download_pauseButton_clicked(GtkButton*, gpointer); void on_download_stopButton_enter(GtkButton*, gpointer); void on_download_stopButton_clicked(GtkButton*, gpointer); void on_download_restartButton_enter(GtkButton*, gpointer); void on_download_restartButton_clicked(GtkButton*, gpointer); void on_download_closeButton_enter(GtkButton*, gpointer); void on_download_closeButton_clicked(GtkButton*, gpointer); void on_download_button_leave(GtkButton*, gpointer); void on_jigdo_startButton_enter(GtkButton*, gpointer); void on_jigdo_startButton_clicked(GtkButton*, gpointer); void on_jigdo_pauseButton_enter(GtkButton*, gpointer); void on_jigdo_pauseButton_clicked(GtkButton*, gpointer); void on_jigdo_stopButton_enter(GtkButton*, gpointer); void on_jigdo_stopButton_clicked(GtkButton*, gpointer); void on_jigdo_restartButton_enter(GtkButton*, gpointer); void on_jigdo_restartButton_clicked(GtkButton*, gpointer); void on_jigdo_closeButton_enter(GtkButton*, gpointer); void on_jigdo_closeButton_clicked(GtkButton*, gpointer); void on_jigdo_button_leave(GtkButton*, gpointer); //______________________________________________________________________ #endif jigdo-0.7.3/src/gtk/interface.cc0000644000175000017500000022756010433432676016276 0ustar richardrichard// Automatically created from `interface.cc.tmp' by glade-filter.awk #include /* * DO NOT EDIT THIS FILE - it is generated by Glade. */ #ifdef HAVE_CONFIG_H # include #endif #ifdef GTK_DISABLE_DEPRECATED # undef GTK_DISABLE_DEPRECATED #endif #include #include #include #include #include #include #include #include #include #include #define GLADE_HOOKUP_OBJECT(component,widget,name) \ g_object_set_data_full (G_OBJECT (component), "", \ gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) #define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ g_object_set_data (G_OBJECT (component), "", widget) GtkWidget* GUI::Window::create() { GtkWidget *separatortoolitem1; GtkWidget *hbox27; GtkWidget *vbox9; GtkWidget *alignment11; GtkWidget *hbox24; GtkWidget *vbox8; GtkWidget *alignment12; GtkWidget *hbox95; GtkWidget *image27; GtkWidget *label247; GtkWidget *table3; GtkWidget *scrolledwindow24; GtkWidget *viewport6; GtkWidget *label156; GtkWidget *hbox53; GtkWidget *hbox58; GtkWidget *hbox59; GtkWidget *label161; GtkWidget *hbox96; GtkWidget *_2lineshighlabelforsettingminimumheight; GtkWidget *vbox42; GtkWidget *hbox83; GtkWidget *image14; GtkWidget *image15; GtkWidget *label211; GtkWidget *image10; GtkWidget *image11; GtkWidget *image13; GtkWidget *scrolledwindow23; GtkWidget *viewport5; GtkWidget *table5; GtkWidget *label151; GtkWidget *label131; GtkWidget *label133; GtkWidget *label149; GtkWidget *vbox34; GtkWidget *scrolledwindow20; GtkWidget *viewport2; GtkWidget *label222; GtkWidget *hseparator5; GtkWidget *hbuttonbox9; GtkWidget *button119; GtkWidget *alignment3; GtkWidget *hbox85; GtkWidget *image16; GtkWidget *label223; GtkWidget *label213; GtkWidget *vbox36; GtkWidget *vbox39; GtkWidget *label229; GtkWidget *label230; GtkWidget *hbox88; GtkWidget *entry23; GtkWidget *alignment6; GtkWidget *button127; GtkWidget *button128; GtkWidget *alignment5; GtkWidget *hbox89; GtkWidget *image23; GtkWidget *label227; GtkWidget *hbuttonbox10; GtkWidget *button120; GtkWidget *button121; GtkWidget *alignment4; GtkWidget *hbox86; GtkWidget *image17; GtkWidget *label224; GtkWidget *label217; GtkWidget *vbox40; GtkWidget *vbox41; GtkWidget *label239; GtkWidget *radiobutton1; radiobutton1_group = NULL; GtkWidget *hbox93; GtkWidget *radiobutton2; GtkWidget *combo1; GtkWidget *hbox94; GtkWidget *alignment10; GtkWidget *radiobutton3; GtkWidget *scrolledwindow21; GtkWidget *viewport3; GtkWidget *table8; GtkWidget *label243; GtkWidget *label244; GtkWidget *label245; GtkWidget *label246; GtkWidget *combo2; GtkWidget *combo3; GtkWidget *hbuttonbox11; GtkWidget *button131; GtkWidget *button132; GtkWidget *alignment9; GtkWidget *hbox92; GtkWidget *image25; GtkWidget *label242; GtkWidget *label218; GtkWidget *vbox37; GtkWidget *hbox97; GtkWidget *_2lineshighforsettingminheight; GtkWidget *vbox43; GtkWidget *hbox98; GtkWidget *image28; GtkWidget *image29; GtkWidget *label251; GtkWidget *image30; GtkWidget *image31; GtkWidget *image32; GtkWidget *scrolledwindow22; GtkWidget *viewport4; GtkWidget *table7; GtkWidget *label233; GtkWidget *label235; GtkWidget *label237; GtkWidget *label231; GtkWidget *label219; tooltips = gtk_tooltips_new (); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), _("Jigsaw Download")); gtk_window_set_default_size (GTK_WINDOW (window), 640, 480); windowVbox = gtk_vbox_new (FALSE, 2); gtk_widget_show (windowVbox); gtk_container_add (GTK_CONTAINER (window), windowVbox); toolbarHandle = gtk_handle_box_new (); gtk_widget_show (toolbarHandle); gtk_box_pack_start (GTK_BOX (windowVbox), toolbarHandle, FALSE, FALSE, 0); toolbar = gtk_toolbar_new (); gtk_widget_show (toolbar); gtk_container_add (GTK_CONTAINER (toolbarHandle), toolbar); gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH); tmp_toolbar_icon_size = gtk_toolbar_get_icon_size (GTK_TOOLBAR (toolbar)); toolbarOpen = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-new"); gtk_widget_show (toolbarOpen); gtk_container_add (GTK_CONTAINER (toolbar), toolbarOpen); gtk_container_set_border_width (GTK_CONTAINER (toolbarOpen), 1); gtk_widget_set_sensitive (toolbarOpen, FALSE); GTK_WIDGET_SET_FLAGS (toolbarOpen, GTK_CAN_FOCUS); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (toolbarOpen), tooltips, _("Open new .jigdo file or URL"), NULL); toolbarReuse = (GtkWidget*) gtk_tool_button_new (NULL, _("Reuse files")); gtk_container_add (GTK_CONTAINER (toolbar), toolbarReuse); gtk_container_set_border_width (GTK_CONTAINER (toolbarReuse), 1); gtk_widget_set_sensitive (toolbarReuse, FALSE); GTK_WIDGET_SET_FLAGS (toolbarReuse, GTK_CAN_FOCUS); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (toolbarReuse), tooltips, _("Scan local filesystems for data needed by jigsaw downloads - useful when \"upgrading\" e.g. a CD image to a newer version"), NULL); separatortoolitem1 = (GtkWidget*) gtk_separator_tool_item_new (); gtk_widget_show (separatortoolitem1); gtk_container_add (GTK_CONTAINER (toolbar), separatortoolitem1); toolbarSettings = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-properties"); gtk_container_add (GTK_CONTAINER (toolbar), toolbarSettings); gtk_container_set_border_width (GTK_CONTAINER (toolbarSettings), 1); gtk_widget_set_sensitive (toolbarSettings, FALSE); GTK_WIDGET_SET_FLAGS (toolbarSettings, GTK_CAN_FOCUS); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (toolbarSettings), tooltips, _("Change preferences"), NULL); toolbarExit = (GtkWidget*) gtk_tool_button_new_from_stock ("gtk-quit"); gtk_widget_show (toolbarExit); gtk_container_add (GTK_CONTAINER (toolbar), toolbarExit); gtk_container_set_border_width (GTK_CONTAINER (toolbarExit), 1); GTK_WIDGET_SET_FLAGS (toolbarExit, GTK_CAN_FOCUS); gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (toolbarExit), tooltips, _("Exit from Jigsaw Download"), NULL); windowPaned = gtk_vpaned_new (); gtk_widget_show (windowPaned); gtk_box_pack_start (GTK_BOX (windowVbox), windowPaned, TRUE, TRUE, 0); gtk_paned_set_position (GTK_PANED (windowPaned), 240); invisibleNotebook = gtk_notebook_new (); gtk_widget_show (invisibleNotebook); gtk_paned_pack1 (GTK_PANED (windowPaned), invisibleNotebook, FALSE, TRUE); GTK_WIDGET_SET_FLAGS (invisibleNotebook, GTK_CAN_DEFAULT); gtk_notebook_set_show_border (GTK_NOTEBOOK (invisibleNotebook), FALSE); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (invisibleNotebook), GTK_POS_BOTTOM); pageOpen = gtk_vbox_new (FALSE, 4); gtk_widget_show (pageOpen); gtk_container_add (GTK_CONTAINER (invisibleNotebook), pageOpen); gtk_container_set_border_width (GTK_CONTAINER (pageOpen), 6); hbox27 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox27); gtk_box_pack_start (GTK_BOX (pageOpen), hbox27, TRUE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox27), 16); aboutJigdoLogo = create_pixmap (window, "jigdo-logo.png"); gtk_widget_show (aboutJigdoLogo); gtk_box_pack_start (GTK_BOX (hbox27), aboutJigdoLogo, FALSE, FALSE, 0); aboutBgnd = gtk_event_box_new (); gtk_widget_show (aboutBgnd); gtk_box_pack_start (GTK_BOX (hbox27), aboutBgnd, TRUE, TRUE, 0); vbox9 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox9); gtk_container_add (GTK_CONTAINER (aboutBgnd), vbox9); gtk_container_set_border_width (GTK_CONTAINER (vbox9), 4); aboutJigdoLabel = gtk_label_new (_("#")); gtk_widget_show (aboutJigdoLabel); gtk_box_pack_start (GTK_BOX (vbox9), aboutJigdoLabel, TRUE, FALSE, 0); GTK_WIDGET_SET_FLAGS (aboutJigdoLabel, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (aboutJigdoLabel), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (aboutJigdoLabel), TRUE); aboutJigdoButton = gtk_button_new (); gtk_widget_show (aboutJigdoButton); gtk_box_pack_end (GTK_BOX (vbox9), aboutJigdoButton, FALSE, FALSE, 0); GTK_WIDGET_UNSET_FLAGS (aboutJigdoButton, GTK_CAN_FOCUS); gtk_button_set_relief (GTK_BUTTON (aboutJigdoButton), GTK_RELIEF_NONE); alignment11 = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_widget_show (alignment11); gtk_container_add (GTK_CONTAINER (aboutJigdoButton), alignment11); aboutJigdoButtonLabel = gtk_label_new_with_mnemonic (_("#")); gtk_widget_show (aboutJigdoButtonLabel); gtk_container_add (GTK_CONTAINER (alignment11), aboutJigdoButtonLabel); gtk_label_set_use_markup (GTK_LABEL (aboutJigdoButtonLabel), TRUE); gtk_label_set_justify (GTK_LABEL (aboutJigdoButtonLabel), GTK_JUSTIFY_CENTER); hbox24 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox24); gtk_box_pack_end (GTK_BOX (pageOpen), hbox24, FALSE, FALSE, 0); vbox8 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox8); gtk_box_pack_end (GTK_BOX (hbox24), vbox8, FALSE, FALSE, 0); openButton = gtk_button_new (); gtk_widget_show (openButton); gtk_box_pack_end (GTK_BOX (vbox8), openButton, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS (openButton, GTK_CAN_DEFAULT); alignment12 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment12); gtk_container_add (GTK_CONTAINER (openButton), alignment12); hbox95 = gtk_hbox_new (FALSE, 2); gtk_widget_show (hbox95); gtk_container_add (GTK_CONTAINER (alignment12), hbox95); gtk_container_set_border_width (GTK_CONTAINER (hbox95), 5); image27 = gtk_image_new_from_stock ("gtk-ok", GTK_ICON_SIZE_BUTTON); gtk_widget_show (image27); gtk_box_pack_start (GTK_BOX (hbox95), image27, FALSE, FALSE, 0); label247 = gtk_label_new_with_mnemonic (_("_Start")); gtk_widget_show (label247); gtk_box_pack_start (GTK_BOX (hbox95), label247, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label247), TRUE); table3 = gtk_table_new (2, 3, FALSE); gtk_widget_show (table3); gtk_box_pack_end (GTK_BOX (hbox24), table3, TRUE, TRUE, 4); gtk_table_set_row_spacings (GTK_TABLE (table3), 2); gtk_table_set_col_spacings (GTK_TABLE (table3), 4); open_sourceURLLabel = gtk_label_new (_("URL")); gtk_widget_show (open_sourceURLLabel); gtk_table_attach (GTK_TABLE (table3), open_sourceURLLabel, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (open_sourceURLLabel), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (open_sourceURLLabel), 1, 0.5); gtk_misc_set_padding (GTK_MISC (open_sourceURLLabel), 2, 0); open_destinationLabel = gtk_label_new (_("Save to")); gtk_widget_show (open_destinationLabel); gtk_table_attach (GTK_TABLE (table3), open_destinationLabel, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (open_destinationLabel), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (open_destinationLabel), 1, 0.5); gtk_misc_set_padding (GTK_MISC (open_destinationLabel), 2, 0); open_URL = gtk_entry_new (); gtk_widget_show (open_URL); gtk_table_attach (GTK_TABLE (table3), open_URL, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, open_URL, _("If the string you enter does not point to a .jigdo file, jigdo behaves just like a normal \"download manager\" and saves the file in the destination directory, nothing else."), NULL); open_dest = gtk_entry_new (); gtk_widget_show (open_dest); gtk_table_attach (GTK_TABLE (table3), open_dest, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, open_dest, _("If you entered a .jigdo URL above, this must be a directory name. For single-file downloads, it can also be a filename."), NULL); open_URLSel = gtk_button_new_with_mnemonic (_("Browse...")); gtk_widget_show (open_URLSel); gtk_table_attach (GTK_TABLE (table3), open_URLSel, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); open_destSel = gtk_button_new_with_mnemonic (_("Browse...")); gtk_widget_show (open_destSel); gtk_table_attach (GTK_TABLE (table3), open_destSel, 2, 3, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); enterUrlLabel = gtk_label_new (_("Enter location of .jigdo file, and destination directory to save files to.")); gtk_widget_show (enterUrlLabel); gtk_box_pack_end (GTK_BOX (pageOpen), enterUrlLabel, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (enterUrlLabel), TRUE); pageOpenLabel = gtk_label_new (_("#Open")); gtk_widget_show (pageOpenLabel); gtk_notebook_set_tab_label (GTK_NOTEBOOK (invisibleNotebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (invisibleNotebook), 0), pageOpenLabel); gtk_label_set_justify (GTK_LABEL (pageOpenLabel), GTK_JUSTIFY_CENTER); pageReuse = gtk_vbox_new (FALSE, 4); gtk_widget_show (pageReuse); gtk_container_add (GTK_CONTAINER (invisibleNotebook), pageReuse); gtk_container_set_border_width (GTK_CONTAINER (pageReuse), 6); scrolledwindow24 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow24); gtk_box_pack_start (GTK_BOX (pageReuse), scrolledwindow24, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow24), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); viewport6 = gtk_viewport_new (NULL, NULL); gtk_widget_show (viewport6); gtk_container_add (GTK_CONTAINER (scrolledwindow24), viewport6); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport6), GTK_SHADOW_NONE); label156 = gtk_label_new (_("Please specify files/directories on the local filesystem to scan. Any files needed by jigdo templates that are found this way will not have to be downloaded again.\nNote that any directory to be scanned must contain single files which would otherwise be downloaded - in the case of CD images, you must supply the directory where the CD is mounted, NOT the name of an .iso file.")); gtk_widget_show (label156); gtk_container_add (GTK_CONTAINER (viewport6), label156); gtk_label_set_justify (GTK_LABEL (label156), GTK_JUSTIFY_CENTER); gtk_label_set_line_wrap (GTK_LABEL (label156), TRUE); hbox53 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox53); gtk_box_pack_start (GTK_BOX (pageReuse), hbox53, FALSE, FALSE, 0); reuse_path = gtk_entry_new (); gtk_widget_show (reuse_path); gtk_box_pack_start (GTK_BOX (hbox53), reuse_path, TRUE, TRUE, 0); reuse_pathSel = gtk_button_new_with_mnemonic (_("Browse...")); gtk_widget_show (reuse_pathSel); gtk_box_pack_start (GTK_BOX (hbox53), reuse_pathSel, FALSE, FALSE, 0); hbox58 = gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox58); gtk_box_pack_start (GTK_BOX (pageReuse), hbox58, FALSE, FALSE, 0); reuse_scanButton = gtk_button_new (); gtk_widget_show (reuse_scanButton); gtk_box_pack_start (GTK_BOX (hbox58), reuse_scanButton, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS (reuse_scanButton, GTK_CAN_DEFAULT); hbox59 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox59); gtk_container_add (GTK_CONTAINER (reuse_scanButton), hbox59); gtk_container_set_border_width (GTK_CONTAINER (hbox59), 2); label161 = gtk_label_new (_("Start scan")); gtk_widget_show (label161); gtk_box_pack_start (GTK_BOX (hbox59), label161, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label161), GTK_JUSTIFY_CENTER); reuse_clearButton = gtk_button_new_with_mnemonic (_("Clear cache")); gtk_widget_show (reuse_clearButton); gtk_box_pack_start (GTK_BOX (hbox58), reuse_clearButton, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS (reuse_clearButton, GTK_CAN_DEFAULT); pageReuseLabel = gtk_label_new (_("#Reuse")); gtk_widget_show (pageReuseLabel); gtk_notebook_set_tab_label (GTK_NOTEBOOK (invisibleNotebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (invisibleNotebook), 1), pageReuseLabel); gtk_label_set_justify (GTK_LABEL (pageReuseLabel), GTK_JUSTIFY_CENTER); pageDownload = gtk_vbox_new (FALSE, 0); gtk_widget_show (pageDownload); gtk_container_add (GTK_CONTAINER (invisibleNotebook), pageDownload); gtk_container_set_border_width (GTK_CONTAINER (pageDownload), 6); hbox96 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox96); gtk_box_pack_end (GTK_BOX (pageDownload), hbox96, FALSE, FALSE, 0); _2lineshighlabelforsettingminimumheight = gtk_label_new (_("\n")); gtk_widget_show (_2lineshighlabelforsettingminimumheight); gtk_box_pack_start (GTK_BOX (hbox96), _2lineshighlabelforsettingminimumheight, FALSE, FALSE, 0); download_buttonInfo = gtk_label_new (""); gtk_widget_show (download_buttonInfo); gtk_box_pack_start (GTK_BOX (hbox96), download_buttonInfo, TRUE, TRUE, 0); gtk_label_set_use_markup (GTK_LABEL (download_buttonInfo), TRUE); gtk_label_set_justify (GTK_LABEL (download_buttonInfo), GTK_JUSTIFY_RIGHT); gtk_label_set_line_wrap (GTK_LABEL (download_buttonInfo), TRUE); gtk_misc_set_alignment (GTK_MISC (download_buttonInfo), 1, 0.5); gtk_misc_set_padding (GTK_MISC (download_buttonInfo), 12, 0); vbox42 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox42); gtk_box_pack_end (GTK_BOX (hbox96), vbox42, FALSE, FALSE, 0); hbox83 = gtk_hbox_new (FALSE, 6); gtk_widget_show (hbox83); gtk_box_pack_end (GTK_BOX (vbox42), hbox83, FALSE, FALSE, 0); download_closeButton = gtk_button_new (); gtk_widget_show (download_closeButton); gtk_box_pack_end (GTK_BOX (hbox83), download_closeButton, FALSE, FALSE, 0); image14 = create_pixmap (window, "close.png"); gtk_widget_show (image14); gtk_container_add (GTK_CONTAINER (download_closeButton), image14); download_restartButton = gtk_button_new (); gtk_widget_show (download_restartButton); gtk_box_pack_end (GTK_BOX (hbox83), download_restartButton, FALSE, FALSE, 0); image15 = create_pixmap (window, "restart.png"); gtk_widget_show (image15); gtk_container_add (GTK_CONTAINER (download_restartButton), image15); label211 = gtk_label_new (""); gtk_widget_show (label211); gtk_box_pack_end (GTK_BOX (hbox83), label211, FALSE, FALSE, 3); download_stopButton = gtk_button_new (); gtk_widget_show (download_stopButton); gtk_box_pack_end (GTK_BOX (hbox83), download_stopButton, FALSE, FALSE, 0); image10 = create_pixmap (window, "stop.png"); gtk_widget_show (image10); gtk_container_add (GTK_CONTAINER (download_stopButton), image10); download_pauseButton = gtk_button_new (); gtk_widget_show (download_pauseButton); gtk_box_pack_end (GTK_BOX (hbox83), download_pauseButton, FALSE, FALSE, 0); image11 = create_pixmap (window, "pause.png"); gtk_widget_show (image11); gtk_container_add (GTK_CONTAINER (download_pauseButton), image11); download_startButton = gtk_button_new (); gtk_widget_show (download_startButton); gtk_box_pack_end (GTK_BOX (hbox83), download_startButton, FALSE, FALSE, 0); image13 = create_pixmap (window, "start.png"); gtk_widget_show (image13); gtk_container_add (GTK_CONTAINER (download_startButton), image13); scrolledwindow23 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow23); gtk_box_pack_end (GTK_BOX (pageDownload), scrolledwindow23, FALSE, FALSE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow23), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport5 = gtk_viewport_new (NULL, NULL); gtk_widget_show (viewport5); gtk_container_add (GTK_CONTAINER (scrolledwindow23), viewport5); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport5), GTK_SHADOW_NONE); table5 = gtk_table_new (4, 2, FALSE); gtk_widget_show (table5); gtk_container_add (GTK_CONTAINER (viewport5), table5); gtk_container_set_border_width (GTK_CONTAINER (table5), 6); gtk_table_set_row_spacings (GTK_TABLE (table5), 4); gtk_table_set_col_spacings (GTK_TABLE (table5), 4); download_dest = gtk_label_new (_("#")); gtk_widget_show (download_dest); gtk_table_attach (GTK_TABLE (table5), download_dest, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (download_dest, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (download_dest), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (download_dest), TRUE); gtk_misc_set_alignment (GTK_MISC (download_dest), 0, 0.5); download_progress = gtk_label_new (_("#")); gtk_widget_show (download_progress); gtk_table_attach (GTK_TABLE (table5), download_progress, 1, 2, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (download_progress, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (download_progress), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (download_progress), TRUE); gtk_misc_set_alignment (GTK_MISC (download_progress), 0, 0.5); label151 = gtk_label_new (_("Status ")); gtk_widget_show (label151); gtk_table_attach (GTK_TABLE (table5), label151, 0, 1, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label151), TRUE); gtk_label_set_justify (GTK_LABEL (label151), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label151), 0, 0.5); download_status = gtk_label_new (_("#")); gtk_widget_show (download_status); gtk_table_attach (GTK_TABLE (table5), download_status, 1, 2, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (download_status, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (download_status), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (download_status), TRUE); gtk_misc_set_alignment (GTK_MISC (download_status), 0, 0.5); download_URL = gtk_label_new (_("#")); gtk_widget_show (download_URL); gtk_table_attach (GTK_TABLE (table5), download_URL, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (download_URL, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (download_URL), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (download_URL), TRUE); gtk_misc_set_alignment (GTK_MISC (download_URL), 0, 0.5); label131 = gtk_label_new (_("URL ")); gtk_widget_show (label131); gtk_table_attach (GTK_TABLE (table5), label131, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label131), TRUE); gtk_label_set_justify (GTK_LABEL (label131), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label131), 0, 0.5); label133 = gtk_label_new (_("Saving to ")); gtk_widget_show (label133); gtk_table_attach (GTK_TABLE (table5), label133, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label133), TRUE); gtk_label_set_justify (GTK_LABEL (label133), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label133), 0, 0.5); label149 = gtk_label_new (_("Progress ")); gtk_widget_show (label149); gtk_table_attach (GTK_TABLE (table5), label149, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label149), TRUE); gtk_label_set_justify (GTK_LABEL (label149), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label149), 0, 0.5); pageDownloadLabel = gtk_label_new (_("#Download")); gtk_widget_show (pageDownloadLabel); gtk_notebook_set_tab_label (GTK_NOTEBOOK (invisibleNotebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (invisibleNotebook), 2), pageDownloadLabel); gtk_label_set_justify (GTK_LABEL (pageDownloadLabel), GTK_JUSTIFY_CENTER); pageJigdo = gtk_notebook_new (); gtk_widget_show (pageJigdo); gtk_container_add (GTK_CONTAINER (invisibleNotebook), pageJigdo); vbox34 = gtk_vbox_new (FALSE, 4); gtk_widget_show (vbox34); gtk_container_add (GTK_CONTAINER (pageJigdo), vbox34); gtk_container_set_border_width (GTK_CONTAINER (vbox34), 4); scrolledwindow20 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow20); gtk_box_pack_start (GTK_BOX (vbox34), scrolledwindow20, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow20), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); viewport2 = gtk_viewport_new (NULL, NULL); gtk_widget_show (viewport2); gtk_container_add (GTK_CONTAINER (scrolledwindow20), viewport2); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport2), GTK_SHADOW_NONE); jigdo_InfoVbox = gtk_vbox_new (FALSE, 8); gtk_widget_show (jigdo_InfoVbox); gtk_container_add (GTK_CONTAINER (viewport2), jigdo_InfoVbox); label222 = gtk_label_new (_("Note: Even though some data is already being downloaded, you\nneed to complete step 2 before the actual file download can start!")); gtk_widget_show (label222); gtk_box_pack_start (GTK_BOX (jigdo_InfoVbox), label222, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label222), TRUE); gtk_label_set_justify (GTK_LABEL (label222), GTK_JUSTIFY_CENTER); hseparator5 = gtk_hseparator_new (); gtk_widget_show (hseparator5); gtk_box_pack_start (GTK_BOX (jigdo_InfoVbox), hseparator5, FALSE, TRUE, 0); jigdo_ShortInfo = gtk_label_new (_("#ShortInfo")); gtk_widget_show (jigdo_ShortInfo); gtk_box_pack_start (GTK_BOX (jigdo_InfoVbox), jigdo_ShortInfo, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS (jigdo_ShortInfo, GTK_CAN_FOCUS); gtk_label_set_use_markup (GTK_LABEL (jigdo_ShortInfo), TRUE); gtk_label_set_selectable (GTK_LABEL (jigdo_ShortInfo), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_ShortInfo), 7.45058e-09, 0.5); jigdo_Info = gtk_label_new (_("#Info\nx\nx\nx")); gtk_widget_show (jigdo_Info); gtk_box_pack_start (GTK_BOX (jigdo_InfoVbox), jigdo_Info, FALSE, TRUE, 0); GTK_WIDGET_SET_FLAGS (jigdo_Info, GTK_CAN_FOCUS); gtk_label_set_use_markup (GTK_LABEL (jigdo_Info), TRUE); gtk_label_set_line_wrap (GTK_LABEL (jigdo_Info), TRUE); gtk_label_set_selectable (GTK_LABEL (jigdo_Info), TRUE); hbuttonbox9 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox9); gtk_box_pack_start (GTK_BOX (vbox34), hbuttonbox9, FALSE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox9), GTK_BUTTONBOX_END); gtk_box_set_spacing (GTK_BOX (hbuttonbox9), 6); button119 = gtk_button_new (); gtk_widget_show (button119); gtk_container_add (GTK_CONTAINER (hbuttonbox9), button119); GTK_WIDGET_SET_FLAGS (button119, GTK_CAN_DEFAULT); alignment3 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment3); gtk_container_add (GTK_CONTAINER (button119), alignment3); hbox85 = gtk_hbox_new (FALSE, 2); gtk_widget_show (hbox85); gtk_container_add (GTK_CONTAINER (alignment3), hbox85); image16 = gtk_image_new_from_stock ("gtk-go-forward", GTK_ICON_SIZE_BUTTON); gtk_widget_show (image16); gtk_box_pack_end (GTK_BOX (hbox85), image16, TRUE, TRUE, 0); label223 = gtk_label_new_with_mnemonic (_("_Next")); gtk_widget_show (label223); gtk_box_pack_start (GTK_BOX (hbox85), label223, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label223), TRUE); label213 = gtk_label_new_with_mnemonic (_("_1: Information")); gtk_widget_show (label213); gtk_notebook_set_tab_label (GTK_NOTEBOOK (pageJigdo), gtk_notebook_get_nth_page (GTK_NOTEBOOK (pageJigdo), 0), label213); gtk_label_set_use_markup (GTK_LABEL (label213), TRUE); gtk_misc_set_alignment (GTK_MISC (label213), 0, 0.5); vbox36 = gtk_vbox_new (FALSE, 12); gtk_container_add (GTK_CONTAINER (pageJigdo), vbox36); gtk_container_set_border_width (GTK_CONTAINER (vbox36), 4); vbox39 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox39); gtk_box_pack_start (GTK_BOX (vbox36), vbox39, TRUE, TRUE, 0); label229 = gtk_label_new (_("Reuse local files for generation of %1:")); gtk_widget_show (label229); gtk_box_pack_start (GTK_BOX (vbox39), label229, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label229), TRUE); gtk_misc_set_alignment (GTK_MISC (label229), 7.45058e-09, 0.5); label230 = gtk_label_new (_("UNIMPLEMENTED\njust click Next...")); gtk_widget_show (label230); gtk_box_pack_start (GTK_BOX (vbox39), label230, TRUE, TRUE, 0); gtk_label_set_justify (GTK_LABEL (label230), GTK_JUSTIFY_CENTER); hbox88 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox88); gtk_box_pack_start (GTK_BOX (vbox36), hbox88, FALSE, TRUE, 0); entry23 = gtk_entry_new (); gtk_widget_show (entry23); gtk_box_pack_start (GTK_BOX (hbox88), entry23, TRUE, TRUE, 0); gtk_widget_set_sensitive (entry23, FALSE); alignment6 = gtk_alignment_new (0.5, 0.5, 1, 0); gtk_widget_show (alignment6); gtk_box_pack_start (GTK_BOX (hbox88), alignment6, FALSE, FALSE, 0); button127 = gtk_button_new_with_mnemonic (_("...")); gtk_widget_show (button127); gtk_container_add (GTK_CONTAINER (alignment6), button127); gtk_widget_set_sensitive (button127, FALSE); button128 = gtk_button_new (); gtk_widget_show (button128); gtk_box_pack_start (GTK_BOX (hbox88), button128, FALSE, FALSE, 0); gtk_widget_set_sensitive (button128, FALSE); GTK_WIDGET_SET_FLAGS (button128, GTK_CAN_DEFAULT); alignment5 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment5); gtk_container_add (GTK_CONTAINER (button128), alignment5); hbox89 = gtk_hbox_new (FALSE, 2); gtk_widget_show (hbox89); gtk_container_add (GTK_CONTAINER (alignment5), hbox89); image23 = gtk_image_new_from_stock ("gtk-ok", GTK_ICON_SIZE_BUTTON); gtk_widget_show (image23); gtk_box_pack_start (GTK_BOX (hbox89), image23, TRUE, TRUE, 0); label227 = gtk_label_new_with_mnemonic (_("_Start scan")); gtk_widget_show (label227); gtk_box_pack_start (GTK_BOX (hbox89), label227, FALSE, FALSE, 0); hbuttonbox10 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox10); gtk_box_pack_start (GTK_BOX (vbox36), hbuttonbox10, FALSE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox10), GTK_BUTTONBOX_END); gtk_box_set_spacing (GTK_BOX (hbuttonbox10), 6); button120 = gtk_button_new_from_stock ("gtk-go-back"); gtk_widget_show (button120); gtk_container_add (GTK_CONTAINER (hbuttonbox10), button120); GTK_WIDGET_SET_FLAGS (button120, GTK_CAN_DEFAULT); button121 = gtk_button_new (); gtk_widget_show (button121); gtk_container_add (GTK_CONTAINER (hbuttonbox10), button121); GTK_WIDGET_SET_FLAGS (button121, GTK_CAN_DEFAULT); alignment4 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment4); gtk_container_add (GTK_CONTAINER (button121), alignment4); hbox86 = gtk_hbox_new (FALSE, 2); gtk_widget_show (hbox86); gtk_container_add (GTK_CONTAINER (alignment4), hbox86); image17 = gtk_image_new_from_stock ("gtk-go-forward", GTK_ICON_SIZE_BUTTON); gtk_widget_show (image17); gtk_box_pack_end (GTK_BOX (hbox86), image17, TRUE, TRUE, 0); label224 = gtk_label_new_with_mnemonic (_("_Next")); gtk_widget_show (label224); gtk_box_pack_start (GTK_BOX (hbox86), label224, FALSE, FALSE, 0); label217 = gtk_label_new (_("?: Reuse files")); gtk_widget_show (label217); gtk_notebook_set_tab_label (GTK_NOTEBOOK (pageJigdo), gtk_notebook_get_nth_page (GTK_NOTEBOOK (pageJigdo), 1), label217); gtk_label_set_use_markup (GTK_LABEL (label217), TRUE); gtk_misc_set_alignment (GTK_MISC (label217), 0, 0.5); vbox40 = gtk_vbox_new (FALSE, 12); gtk_widget_show (vbox40); gtk_container_add (GTK_CONTAINER (pageJigdo), vbox40); gtk_container_set_border_width (GTK_CONTAINER (vbox40), 4); vbox41 = gtk_vbox_new (FALSE, 4); gtk_widget_show (vbox41); gtk_box_pack_start (GTK_BOX (vbox40), vbox41, TRUE, TRUE, 0); label239 = gtk_label_new (_("Select servers to download data from:")); gtk_widget_show (label239); gtk_box_pack_start (GTK_BOX (vbox41), label239, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label239), TRUE); gtk_misc_set_alignment (GTK_MISC (label239), 7.45058e-09, 0.5); radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("_Automatic selection: Use \"netselect\" to find the best servers")); gtk_box_pack_start (GTK_BOX (vbox41), radiobutton1, FALSE, FALSE, 0); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton1), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1)); hbox93 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox93); gtk_box_pack_start (GTK_BOX (vbox41), hbox93, FALSE, FALSE, 0); radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("_Simple selection, by country name: ")); gtk_widget_show (radiobutton2); gtk_box_pack_start (GTK_BOX (hbox93), radiobutton2, FALSE, FALSE, 0); gtk_tooltips_set_tip (tooltips, radiobutton2, _("Select the country you live in. jigdo will then try out all available servers on your continent. Sooner or later, it should end up using a server that is fast for you."), NULL); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2)); combo1 = gtk_combo_new (); g_object_set_data (G_OBJECT (GTK_COMBO (combo1)->popwin), "GladeParentKey", combo1); gtk_widget_show (combo1); gtk_box_pack_start (GTK_BOX (hbox93), combo1, TRUE, TRUE, 0); combo_entry1 = GTK_COMBO (combo1)->entry; gtk_widget_show (combo_entry1); gtk_editable_set_editable (GTK_EDITABLE (combo_entry1), FALSE); gtk_entry_set_text (GTK_ENTRY (combo_entry1), _("Select the country you live in â\206\222")); hbox94 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox94); gtk_box_pack_start (GTK_BOX (vbox41), hbox94, TRUE, TRUE, 0); alignment10 = gtk_alignment_new (0.5, 0, 1, 0); gtk_widget_show (alignment10); gtk_box_pack_start (GTK_BOX (hbox94), alignment10, FALSE, FALSE, 0); radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("Ad_vanced: ")); gtk_widget_show (radiobutton3); gtk_container_add (GTK_CONTAINER (alignment10), radiobutton3); gtk_tooltips_set_tip (tooltips, radiobutton3, _("Select or enter server URLs for the different types of servers that the .jigdo file references. You can enter several URLs for each type of server - in this case, jigdo will try to find the fastest."), NULL); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3)); scrolledwindow21 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow21); gtk_box_pack_start (GTK_BOX (hbox94), scrolledwindow21, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow21), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); viewport3 = gtk_viewport_new (NULL, NULL); gtk_widget_show (viewport3); gtk_container_add (GTK_CONTAINER (scrolledwindow21), viewport3); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport3), GTK_SHADOW_NONE); table8 = gtk_table_new (3, 2, FALSE); gtk_widget_show (table8); gtk_container_add (GTK_CONTAINER (viewport3), table8); gtk_table_set_row_spacings (GTK_TABLE (table8), 2); gtk_table_set_col_spacings (GTK_TABLE (table8), 2); label243 = gtk_label_new (_("Type of server: ")); gtk_widget_show (label243); gtk_table_attach (GTK_TABLE (table8), label243, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label243), TRUE); gtk_misc_set_alignment (GTK_MISC (label243), 0, 0.5); label244 = gtk_label_new (_("URL(s) for that server: (space-separated list, preferred URLs first)")); gtk_widget_show (label244); gtk_table_attach (GTK_TABLE (table8), label244, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label244), TRUE); gtk_misc_set_alignment (GTK_MISC (label244), 0, 0.5); label245 = gtk_label_new (_("Debian")); gtk_widget_show (label245); gtk_table_attach (GTK_TABLE (table8), label245, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label245), 0, 0.5); label246 = gtk_label_new (_("Non-US")); gtk_widget_show (label246); gtk_table_attach (GTK_TABLE (table8), label246, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label246), 0, 0.5); combo2 = gtk_combo_new (); g_object_set_data (G_OBJECT (GTK_COMBO (combo2)->popwin), "GladeParentKey", combo2); gtk_widget_show (combo2); gtk_table_attach (GTK_TABLE (table8), combo2, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); combo_entry2 = GTK_COMBO (combo2)->entry; gtk_widget_show (combo_entry2); combo3 = gtk_combo_new (); g_object_set_data (G_OBJECT (GTK_COMBO (combo3)->popwin), "GladeParentKey", combo3); gtk_widget_show (combo3); gtk_table_attach (GTK_TABLE (table8), combo3, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); combo_entry3 = GTK_COMBO (combo3)->entry; gtk_widget_show (combo_entry3); hbuttonbox11 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox11); gtk_box_pack_start (GTK_BOX (vbox40), hbuttonbox11, FALSE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox11), GTK_BUTTONBOX_END); gtk_box_set_spacing (GTK_BOX (hbuttonbox11), 6); button131 = gtk_button_new_from_stock ("gtk-go-back"); gtk_widget_show (button131); gtk_container_add (GTK_CONTAINER (hbuttonbox11), button131); GTK_WIDGET_SET_FLAGS (button131, GTK_CAN_DEFAULT); button132 = gtk_button_new (); gtk_widget_show (button132); gtk_container_add (GTK_CONTAINER (hbuttonbox11), button132); GTK_WIDGET_SET_FLAGS (button132, GTK_CAN_DEFAULT); alignment9 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment9); gtk_container_add (GTK_CONTAINER (button132), alignment9); hbox92 = gtk_hbox_new (FALSE, 2); gtk_widget_show (hbox92); gtk_container_add (GTK_CONTAINER (alignment9), hbox92); image25 = gtk_image_new_from_stock ("gtk-go-forward", GTK_ICON_SIZE_BUTTON); gtk_widget_show (image25); gtk_box_pack_end (GTK_BOX (hbox92), image25, TRUE, TRUE, 0); label242 = gtk_label_new_with_mnemonic (_("_OK, start download!")); gtk_widget_show (label242); gtk_box_pack_start (GTK_BOX (hbox92), label242, FALSE, FALSE, 0); gtk_label_set_use_markup (GTK_LABEL (label242), TRUE); label218 = gtk_label_new_with_mnemonic (_("_2: Select servers")); gtk_widget_show (label218); gtk_notebook_set_tab_label (GTK_NOTEBOOK (pageJigdo), gtk_notebook_get_nth_page (GTK_NOTEBOOK (pageJigdo), 2), label218); gtk_label_set_use_markup (GTK_LABEL (label218), TRUE); gtk_misc_set_alignment (GTK_MISC (label218), 0, 0.5); vbox37 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox37); gtk_container_add (GTK_CONTAINER (pageJigdo), vbox37); gtk_container_set_border_width (GTK_CONTAINER (vbox37), 4); hbox97 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox97); gtk_box_pack_end (GTK_BOX (vbox37), hbox97, FALSE, FALSE, 0); _2lineshighforsettingminheight = gtk_label_new (_("\n")); gtk_widget_show (_2lineshighforsettingminheight); gtk_box_pack_start (GTK_BOX (hbox97), _2lineshighforsettingminheight, FALSE, FALSE, 0); jigdo_buttonInfo = gtk_label_new (""); gtk_widget_show (jigdo_buttonInfo); gtk_box_pack_start (GTK_BOX (hbox97), jigdo_buttonInfo, TRUE, TRUE, 0); gtk_label_set_use_markup (GTK_LABEL (jigdo_buttonInfo), TRUE); gtk_label_set_justify (GTK_LABEL (jigdo_buttonInfo), GTK_JUSTIFY_RIGHT); gtk_label_set_line_wrap (GTK_LABEL (jigdo_buttonInfo), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_buttonInfo), 1, 0.5); gtk_misc_set_padding (GTK_MISC (jigdo_buttonInfo), 12, 0); vbox43 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox43); gtk_box_pack_end (GTK_BOX (hbox97), vbox43, FALSE, FALSE, 0); hbox98 = gtk_hbox_new (FALSE, 6); gtk_widget_show (hbox98); gtk_box_pack_end (GTK_BOX (vbox43), hbox98, FALSE, FALSE, 0); jigdo_closeButton = gtk_button_new (); gtk_widget_show (jigdo_closeButton); gtk_box_pack_end (GTK_BOX (hbox98), jigdo_closeButton, FALSE, FALSE, 0); image28 = create_pixmap (window, "close.png"); gtk_widget_show (image28); gtk_container_add (GTK_CONTAINER (jigdo_closeButton), image28); jigdo_restartButton = gtk_button_new (); gtk_widget_show (jigdo_restartButton); gtk_box_pack_end (GTK_BOX (hbox98), jigdo_restartButton, FALSE, FALSE, 0); image29 = create_pixmap (window, "restart.png"); gtk_widget_show (image29); gtk_container_add (GTK_CONTAINER (jigdo_restartButton), image29); label251 = gtk_label_new (""); gtk_widget_show (label251); gtk_box_pack_end (GTK_BOX (hbox98), label251, FALSE, FALSE, 3); jigdo_stopButton = gtk_button_new (); gtk_widget_show (jigdo_stopButton); gtk_box_pack_end (GTK_BOX (hbox98), jigdo_stopButton, FALSE, FALSE, 0); image30 = create_pixmap (window, "stop.png"); gtk_widget_show (image30); gtk_container_add (GTK_CONTAINER (jigdo_stopButton), image30); jigdo_pauseButton = gtk_button_new (); gtk_widget_show (jigdo_pauseButton); gtk_box_pack_end (GTK_BOX (hbox98), jigdo_pauseButton, FALSE, FALSE, 0); image31 = create_pixmap (window, "pause.png"); gtk_widget_show (image31); gtk_container_add (GTK_CONTAINER (jigdo_pauseButton), image31); jigdo_startButton = gtk_button_new (); gtk_widget_show (jigdo_startButton); gtk_box_pack_end (GTK_BOX (hbox98), jigdo_startButton, FALSE, FALSE, 0); image32 = create_pixmap (window, "start.png"); gtk_widget_show (image32); gtk_container_add (GTK_CONTAINER (jigdo_startButton), image32); scrolledwindow22 = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (scrolledwindow22); gtk_box_pack_end (GTK_BOX (vbox37), scrolledwindow22, FALSE, FALSE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow22), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); viewport4 = gtk_viewport_new (NULL, NULL); gtk_widget_show (viewport4); gtk_container_add (GTK_CONTAINER (scrolledwindow22), viewport4); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport4), GTK_SHADOW_NONE); table7 = gtk_table_new (4, 2, FALSE); gtk_widget_show (table7); gtk_container_add (GTK_CONTAINER (viewport4), table7); gtk_container_set_border_width (GTK_CONTAINER (table7), 6); gtk_table_set_row_spacings (GTK_TABLE (table7), 4); gtk_table_set_col_spacings (GTK_TABLE (table7), 4); label233 = gtk_label_new (_("Saving to ")); gtk_widget_show (label233); gtk_table_attach (GTK_TABLE (table7), label233, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label233), TRUE); gtk_label_set_justify (GTK_LABEL (label233), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label233), 0, 0.5); label235 = gtk_label_new (_("Progress ")); gtk_widget_show (label235); gtk_table_attach (GTK_TABLE (table7), label235, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label235), TRUE); gtk_label_set_justify (GTK_LABEL (label235), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label235), 0, 0.5); label237 = gtk_label_new (_("Status ")); gtk_widget_show (label237); gtk_table_attach (GTK_TABLE (table7), label237, 0, 1, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label237), TRUE); gtk_label_set_justify (GTK_LABEL (label237), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label237), 0, 0.5); label231 = gtk_label_new (_("URL ")); gtk_widget_show (label231); gtk_table_attach (GTK_TABLE (table7), label231, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_use_markup (GTK_LABEL (label231), TRUE); gtk_label_set_justify (GTK_LABEL (label231), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label231), 0, 0.5); jigdo_status = gtk_label_new (_("#")); gtk_widget_show (jigdo_status); gtk_table_attach (GTK_TABLE (table7), jigdo_status, 1, 2, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (jigdo_status, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (jigdo_status), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (jigdo_status), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_status), 0, 0.5); jigdo_progress = gtk_label_new (_("#")); gtk_widget_show (jigdo_progress); gtk_table_attach (GTK_TABLE (table7), jigdo_progress, 1, 2, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (jigdo_progress, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (jigdo_progress), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (jigdo_progress), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_progress), 0, 0.5); jigdo_dest = gtk_label_new (_("#")); gtk_widget_show (jigdo_dest); gtk_table_attach (GTK_TABLE (table7), jigdo_dest, 1, 2, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (jigdo_dest, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (jigdo_dest), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (jigdo_dest), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_dest), 0, 0.5); jigdo_URL = gtk_label_new (_("#")); gtk_widget_show (jigdo_URL); gtk_table_attach (GTK_TABLE (table7), jigdo_URL, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); GTK_WIDGET_SET_FLAGS (jigdo_URL, GTK_CAN_FOCUS); gtk_label_set_justify (GTK_LABEL (jigdo_URL), GTK_JUSTIFY_CENTER); gtk_label_set_selectable (GTK_LABEL (jigdo_URL), TRUE); gtk_misc_set_alignment (GTK_MISC (jigdo_URL), 0, 0.5); label219 = gtk_label_new_with_mnemonic (_("_3: Download")); gtk_widget_show (label219); gtk_notebook_set_tab_label (GTK_NOTEBOOK (pageJigdo), gtk_notebook_get_nth_page (GTK_NOTEBOOK (pageJigdo), 3), label219); gtk_label_set_use_markup (GTK_LABEL (label219), TRUE); gtk_misc_set_alignment (GTK_MISC (label219), 0, 0.5); pageJigdoLabel = gtk_label_new (_("#Jigdo")); gtk_widget_show (pageJigdoLabel); gtk_notebook_set_tab_label (GTK_NOTEBOOK (invisibleNotebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (invisibleNotebook), 3), pageJigdoLabel); progressScroll = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (progressScroll); gtk_paned_pack2 (GTK_PANED (windowPaned), progressScroll, TRUE, TRUE); GTK_WIDGET_UNSET_FLAGS (progressScroll, GTK_CAN_FOCUS); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (progressScroll), GTK_SHADOW_IN); jobs = gtk_tree_view_new (); gtk_widget_show (jobs); gtk_container_add (GTK_CONTAINER (progressScroll), jobs); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (jobs), TRUE); g_signal_connect ((gpointer) window, "delete_event", G_CALLBACK (on_window_delete_event), NULL); g_signal_connect_swapped ((gpointer) toolbarOpen, "clicked", G_CALLBACK (setNotebookPage), GTK_OBJECT (pageOpen)); g_signal_connect_swapped ((gpointer) toolbarReuse, "clicked", G_CALLBACK (setNotebookPage), GTK_OBJECT (pageReuse)); g_signal_connect ((gpointer) toolbarExit, "clicked", G_CALLBACK (on_toolbarExit_clicked), NULL); g_signal_connect ((gpointer) aboutJigdoButton, "clicked", G_CALLBACK (on_aboutJigdoButton_clicked), NULL); g_signal_connect ((gpointer) openButton, "clicked", G_CALLBACK (on_openButton_clicked), NULL); g_signal_connect ((gpointer) open_URL, "activate", G_CALLBACK (on_openButton_clicked), NULL); g_signal_connect ((gpointer) open_dest, "activate", G_CALLBACK (on_openButton_clicked), NULL); g_signal_connect ((gpointer) download_closeButton, "enter", G_CALLBACK (on_download_closeButton_enter), NULL); g_signal_connect ((gpointer) download_closeButton, "leave", G_CALLBACK (on_download_button_leave), NULL); g_signal_connect ((gpointer) download_closeButton, "clicked", G_CALLBACK (on_download_closeButton_clicked), NULL); g_signal_connect ((gpointer) download_restartButton, "enter", G_CALLBACK (on_download_restartButton_enter), NULL); g_signal_connect ((gpointer) download_restartButton, "leave", G_CALLBACK (on_download_button_leave), NULL); g_signal_connect ((gpointer) download_restartButton, "clicked", G_CALLBACK (on_download_restartButton_clicked), NULL); g_signal_connect ((gpointer) download_stopButton, "enter", G_CALLBACK (on_download_stopButton_enter), NULL); g_signal_connect ((gpointer) download_stopButton, "leave", G_CALLBACK (on_download_button_leave), NULL); g_signal_connect ((gpointer) download_stopButton, "clicked", G_CALLBACK (on_download_stopButton_clicked), NULL); g_signal_connect ((gpointer) download_pauseButton, "enter", G_CALLBACK (on_download_pauseButton_enter), NULL); g_signal_connect ((gpointer) download_pauseButton, "leave", G_CALLBACK (on_download_button_leave), NULL); g_signal_connect ((gpointer) download_pauseButton, "clicked", G_CALLBACK (on_download_pauseButton_clicked), NULL); g_signal_connect ((gpointer) download_startButton, "enter", G_CALLBACK (on_download_startButton_enter), NULL); g_signal_connect ((gpointer) download_startButton, "leave", G_CALLBACK (on_download_button_leave), NULL); g_signal_connect ((gpointer) download_startButton, "clicked", G_CALLBACK (on_download_startButton_clicked), NULL); g_signal_connect ((gpointer) entry23, "activate", G_CALLBACK (on_openButton_clicked), NULL); g_signal_connect ((gpointer) jigdo_closeButton, "enter", G_CALLBACK (on_jigdo_closeButton_enter), NULL); g_signal_connect ((gpointer) jigdo_closeButton, "leave", G_CALLBACK (on_jigdo_button_leave), NULL); g_signal_connect ((gpointer) jigdo_closeButton, "clicked", G_CALLBACK (on_jigdo_closeButton_clicked), NULL); g_signal_connect ((gpointer) jigdo_restartButton, "enter", G_CALLBACK (on_jigdo_restartButton_enter), NULL); g_signal_connect ((gpointer) jigdo_restartButton, "leave", G_CALLBACK (on_jigdo_button_leave), NULL); g_signal_connect ((gpointer) jigdo_restartButton, "clicked", G_CALLBACK (on_jigdo_restartButton_clicked), NULL); g_signal_connect ((gpointer) jigdo_stopButton, "enter", G_CALLBACK (on_jigdo_stopButton_enter), NULL); g_signal_connect ((gpointer) jigdo_stopButton, "leave", G_CALLBACK (on_jigdo_button_leave), NULL); g_signal_connect ((gpointer) jigdo_stopButton, "clicked", G_CALLBACK (on_jigdo_stopButton_clicked), NULL); g_signal_connect ((gpointer) jigdo_pauseButton, "enter", G_CALLBACK (on_jigdo_pauseButton_enter), NULL); g_signal_connect ((gpointer) jigdo_pauseButton, "leave", G_CALLBACK (on_jigdo_button_leave), NULL); g_signal_connect ((gpointer) jigdo_pauseButton, "clicked", G_CALLBACK (on_jigdo_pauseButton_clicked), NULL); g_signal_connect ((gpointer) jigdo_startButton, "enter", G_CALLBACK (on_jigdo_startButton_enter), NULL); g_signal_connect ((gpointer) jigdo_startButton, "leave", G_CALLBACK (on_jigdo_button_leave), NULL); g_signal_connect ((gpointer) jigdo_startButton, "clicked", G_CALLBACK (on_jigdo_startButton_clicked), NULL); atko = gtk_widget_get_accessible (aboutJigdoLogo); atk_object_set_description (atko, _("Jigdo logo")); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (window, window, "window"); GLADE_HOOKUP_OBJECT (window, windowVbox, "windowVbox"); GLADE_HOOKUP_OBJECT (window, toolbarHandle, "toolbarHandle"); GLADE_HOOKUP_OBJECT (window, toolbar, "toolbar"); GLADE_HOOKUP_OBJECT (window, toolbarOpen, "toolbarOpen"); GLADE_HOOKUP_OBJECT (window, toolbarReuse, "toolbarReuse"); GLADE_HOOKUP_OBJECT (window, separatortoolitem1, "separatortoolitem1"); GLADE_HOOKUP_OBJECT (window, toolbarSettings, "toolbarSettings"); GLADE_HOOKUP_OBJECT (window, toolbarExit, "toolbarExit"); GLADE_HOOKUP_OBJECT (window, windowPaned, "windowPaned"); GLADE_HOOKUP_OBJECT (window, invisibleNotebook, "invisibleNotebook"); GLADE_HOOKUP_OBJECT (window, pageOpen, "pageOpen"); GLADE_HOOKUP_OBJECT (window, hbox27, "hbox27"); GLADE_HOOKUP_OBJECT (window, aboutJigdoLogo, "aboutJigdoLogo"); GLADE_HOOKUP_OBJECT (window, aboutBgnd, "aboutBgnd"); GLADE_HOOKUP_OBJECT (window, vbox9, "vbox9"); GLADE_HOOKUP_OBJECT (window, aboutJigdoLabel, "aboutJigdoLabel"); GLADE_HOOKUP_OBJECT (window, aboutJigdoButton, "aboutJigdoButton"); GLADE_HOOKUP_OBJECT (window, alignment11, "alignment11"); GLADE_HOOKUP_OBJECT (window, aboutJigdoButtonLabel, "aboutJigdoButtonLabel"); GLADE_HOOKUP_OBJECT (window, hbox24, "hbox24"); GLADE_HOOKUP_OBJECT (window, vbox8, "vbox8"); GLADE_HOOKUP_OBJECT (window, openButton, "openButton"); GLADE_HOOKUP_OBJECT (window, alignment12, "alignment12"); GLADE_HOOKUP_OBJECT (window, hbox95, "hbox95"); GLADE_HOOKUP_OBJECT (window, image27, "image27"); GLADE_HOOKUP_OBJECT (window, label247, "label247"); GLADE_HOOKUP_OBJECT (window, table3, "table3"); GLADE_HOOKUP_OBJECT (window, open_sourceURLLabel, "open_sourceURLLabel"); GLADE_HOOKUP_OBJECT (window, open_destinationLabel, "open_destinationLabel"); GLADE_HOOKUP_OBJECT (window, open_URL, "open_URL"); GLADE_HOOKUP_OBJECT (window, open_dest, "open_dest"); GLADE_HOOKUP_OBJECT (window, open_URLSel, "open_URLSel"); GLADE_HOOKUP_OBJECT (window, open_destSel, "open_destSel"); GLADE_HOOKUP_OBJECT (window, enterUrlLabel, "enterUrlLabel"); GLADE_HOOKUP_OBJECT (window, pageOpenLabel, "pageOpenLabel"); GLADE_HOOKUP_OBJECT (window, pageReuse, "pageReuse"); GLADE_HOOKUP_OBJECT (window, scrolledwindow24, "scrolledwindow24"); GLADE_HOOKUP_OBJECT (window, viewport6, "viewport6"); GLADE_HOOKUP_OBJECT (window, label156, "label156"); GLADE_HOOKUP_OBJECT (window, hbox53, "hbox53"); GLADE_HOOKUP_OBJECT (window, reuse_path, "reuse_path"); GLADE_HOOKUP_OBJECT (window, reuse_pathSel, "reuse_pathSel"); GLADE_HOOKUP_OBJECT (window, hbox58, "hbox58"); GLADE_HOOKUP_OBJECT (window, reuse_scanButton, "reuse_scanButton"); GLADE_HOOKUP_OBJECT (window, hbox59, "hbox59"); GLADE_HOOKUP_OBJECT (window, label161, "label161"); GLADE_HOOKUP_OBJECT (window, reuse_clearButton, "reuse_clearButton"); GLADE_HOOKUP_OBJECT (window, pageReuseLabel, "pageReuseLabel"); GLADE_HOOKUP_OBJECT (window, pageDownload, "pageDownload"); GLADE_HOOKUP_OBJECT (window, hbox96, "hbox96"); GLADE_HOOKUP_OBJECT (window, _2lineshighlabelforsettingminimumheight, "_2lineshighlabelforsettingminimumheight"); GLADE_HOOKUP_OBJECT (window, download_buttonInfo, "download_buttonInfo"); GLADE_HOOKUP_OBJECT (window, vbox42, "vbox42"); GLADE_HOOKUP_OBJECT (window, hbox83, "hbox83"); GLADE_HOOKUP_OBJECT (window, download_closeButton, "download_closeButton"); GLADE_HOOKUP_OBJECT (window, image14, "image14"); GLADE_HOOKUP_OBJECT (window, download_restartButton, "download_restartButton"); GLADE_HOOKUP_OBJECT (window, image15, "image15"); GLADE_HOOKUP_OBJECT (window, label211, "label211"); GLADE_HOOKUP_OBJECT (window, download_stopButton, "download_stopButton"); GLADE_HOOKUP_OBJECT (window, image10, "image10"); GLADE_HOOKUP_OBJECT (window, download_pauseButton, "download_pauseButton"); GLADE_HOOKUP_OBJECT (window, image11, "image11"); GLADE_HOOKUP_OBJECT (window, download_startButton, "download_startButton"); GLADE_HOOKUP_OBJECT (window, image13, "image13"); GLADE_HOOKUP_OBJECT (window, scrolledwindow23, "scrolledwindow23"); GLADE_HOOKUP_OBJECT (window, viewport5, "viewport5"); GLADE_HOOKUP_OBJECT (window, table5, "table5"); GLADE_HOOKUP_OBJECT (window, download_dest, "download_dest"); GLADE_HOOKUP_OBJECT (window, download_progress, "download_progress"); GLADE_HOOKUP_OBJECT (window, label151, "label151"); GLADE_HOOKUP_OBJECT (window, download_status, "download_status"); GLADE_HOOKUP_OBJECT (window, download_URL, "download_URL"); GLADE_HOOKUP_OBJECT (window, label131, "label131"); GLADE_HOOKUP_OBJECT (window, label133, "label133"); GLADE_HOOKUP_OBJECT (window, label149, "label149"); GLADE_HOOKUP_OBJECT (window, pageDownloadLabel, "pageDownloadLabel"); GLADE_HOOKUP_OBJECT (window, pageJigdo, "pageJigdo"); GLADE_HOOKUP_OBJECT (window, vbox34, "vbox34"); GLADE_HOOKUP_OBJECT (window, scrolledwindow20, "scrolledwindow20"); GLADE_HOOKUP_OBJECT (window, viewport2, "viewport2"); GLADE_HOOKUP_OBJECT (window, jigdo_InfoVbox, "jigdo_InfoVbox"); GLADE_HOOKUP_OBJECT (window, label222, "label222"); GLADE_HOOKUP_OBJECT (window, hseparator5, "hseparator5"); GLADE_HOOKUP_OBJECT (window, jigdo_ShortInfo, "jigdo_ShortInfo"); GLADE_HOOKUP_OBJECT (window, jigdo_Info, "jigdo_Info"); GLADE_HOOKUP_OBJECT (window, hbuttonbox9, "hbuttonbox9"); GLADE_HOOKUP_OBJECT (window, button119, "button119"); GLADE_HOOKUP_OBJECT (window, alignment3, "alignment3"); GLADE_HOOKUP_OBJECT (window, hbox85, "hbox85"); GLADE_HOOKUP_OBJECT (window, image16, "image16"); GLADE_HOOKUP_OBJECT (window, label223, "label223"); GLADE_HOOKUP_OBJECT (window, label213, "label213"); GLADE_HOOKUP_OBJECT (window, vbox36, "vbox36"); GLADE_HOOKUP_OBJECT (window, vbox39, "vbox39"); GLADE_HOOKUP_OBJECT (window, label229, "label229"); GLADE_HOOKUP_OBJECT (window, label230, "label230"); GLADE_HOOKUP_OBJECT (window, hbox88, "hbox88"); GLADE_HOOKUP_OBJECT (window, entry23, "entry23"); GLADE_HOOKUP_OBJECT (window, alignment6, "alignment6"); GLADE_HOOKUP_OBJECT (window, button127, "button127"); GLADE_HOOKUP_OBJECT (window, button128, "button128"); GLADE_HOOKUP_OBJECT (window, alignment5, "alignment5"); GLADE_HOOKUP_OBJECT (window, hbox89, "hbox89"); GLADE_HOOKUP_OBJECT (window, image23, "image23"); GLADE_HOOKUP_OBJECT (window, label227, "label227"); GLADE_HOOKUP_OBJECT (window, hbuttonbox10, "hbuttonbox10"); GLADE_HOOKUP_OBJECT (window, button120, "button120"); GLADE_HOOKUP_OBJECT (window, button121, "button121"); GLADE_HOOKUP_OBJECT (window, alignment4, "alignment4"); GLADE_HOOKUP_OBJECT (window, hbox86, "hbox86"); GLADE_HOOKUP_OBJECT (window, image17, "image17"); GLADE_HOOKUP_OBJECT (window, label224, "label224"); GLADE_HOOKUP_OBJECT (window, label217, "label217"); GLADE_HOOKUP_OBJECT (window, vbox40, "vbox40"); GLADE_HOOKUP_OBJECT (window, vbox41, "vbox41"); GLADE_HOOKUP_OBJECT (window, label239, "label239"); GLADE_HOOKUP_OBJECT (window, radiobutton1, "radiobutton1"); GLADE_HOOKUP_OBJECT (window, hbox93, "hbox93"); GLADE_HOOKUP_OBJECT (window, radiobutton2, "radiobutton2"); GLADE_HOOKUP_OBJECT (window, combo1, "combo1"); GLADE_HOOKUP_OBJECT (window, combo_entry1, "combo_entry1"); GLADE_HOOKUP_OBJECT (window, hbox94, "hbox94"); GLADE_HOOKUP_OBJECT (window, alignment10, "alignment10"); GLADE_HOOKUP_OBJECT (window, radiobutton3, "radiobutton3"); GLADE_HOOKUP_OBJECT (window, scrolledwindow21, "scrolledwindow21"); GLADE_HOOKUP_OBJECT (window, viewport3, "viewport3"); GLADE_HOOKUP_OBJECT (window, table8, "table8"); GLADE_HOOKUP_OBJECT (window, label243, "label243"); GLADE_HOOKUP_OBJECT (window, label244, "label244"); GLADE_HOOKUP_OBJECT (window, label245, "label245"); GLADE_HOOKUP_OBJECT (window, label246, "label246"); GLADE_HOOKUP_OBJECT (window, combo2, "combo2"); GLADE_HOOKUP_OBJECT (window, combo_entry2, "combo_entry2"); GLADE_HOOKUP_OBJECT (window, combo3, "combo3"); GLADE_HOOKUP_OBJECT (window, combo_entry3, "combo_entry3"); GLADE_HOOKUP_OBJECT (window, hbuttonbox11, "hbuttonbox11"); GLADE_HOOKUP_OBJECT (window, button131, "button131"); GLADE_HOOKUP_OBJECT (window, button132, "button132"); GLADE_HOOKUP_OBJECT (window, alignment9, "alignment9"); GLADE_HOOKUP_OBJECT (window, hbox92, "hbox92"); GLADE_HOOKUP_OBJECT (window, image25, "image25"); GLADE_HOOKUP_OBJECT (window, label242, "label242"); GLADE_HOOKUP_OBJECT (window, label218, "label218"); GLADE_HOOKUP_OBJECT (window, vbox37, "vbox37"); GLADE_HOOKUP_OBJECT (window, hbox97, "hbox97"); GLADE_HOOKUP_OBJECT (window, _2lineshighforsettingminheight, "_2lineshighforsettingminheight"); GLADE_HOOKUP_OBJECT (window, jigdo_buttonInfo, "jigdo_buttonInfo"); GLADE_HOOKUP_OBJECT (window, vbox43, "vbox43"); GLADE_HOOKUP_OBJECT (window, hbox98, "hbox98"); GLADE_HOOKUP_OBJECT (window, jigdo_closeButton, "jigdo_closeButton"); GLADE_HOOKUP_OBJECT (window, image28, "image28"); GLADE_HOOKUP_OBJECT (window, jigdo_restartButton, "jigdo_restartButton"); GLADE_HOOKUP_OBJECT (window, image29, "image29"); GLADE_HOOKUP_OBJECT (window, label251, "label251"); GLADE_HOOKUP_OBJECT (window, jigdo_stopButton, "jigdo_stopButton"); GLADE_HOOKUP_OBJECT (window, image30, "image30"); GLADE_HOOKUP_OBJECT (window, jigdo_pauseButton, "jigdo_pauseButton"); GLADE_HOOKUP_OBJECT (window, image31, "image31"); GLADE_HOOKUP_OBJECT (window, jigdo_startButton, "jigdo_startButton"); GLADE_HOOKUP_OBJECT (window, image32, "image32"); GLADE_HOOKUP_OBJECT (window, scrolledwindow22, "scrolledwindow22"); GLADE_HOOKUP_OBJECT (window, viewport4, "viewport4"); GLADE_HOOKUP_OBJECT (window, table7, "table7"); GLADE_HOOKUP_OBJECT (window, label233, "label233"); GLADE_HOOKUP_OBJECT (window, label235, "label235"); GLADE_HOOKUP_OBJECT (window, label237, "label237"); GLADE_HOOKUP_OBJECT (window, label231, "label231"); GLADE_HOOKUP_OBJECT (window, jigdo_status, "jigdo_status"); GLADE_HOOKUP_OBJECT (window, jigdo_progress, "jigdo_progress"); GLADE_HOOKUP_OBJECT (window, jigdo_dest, "jigdo_dest"); GLADE_HOOKUP_OBJECT (window, jigdo_URL, "jigdo_URL"); GLADE_HOOKUP_OBJECT (window, label219, "label219"); GLADE_HOOKUP_OBJECT (window, pageJigdoLabel, "pageJigdoLabel"); GLADE_HOOKUP_OBJECT (window, progressScroll, "progressScroll"); GLADE_HOOKUP_OBJECT (window, jobs, "jobs"); GLADE_HOOKUP_OBJECT_NO_REF (window, tooltips, "tooltips"); gtk_widget_grab_focus (openButton); gtk_widget_grab_default (openButton); return window; } GtkWidget* GUI::Filesel::create() { filesel = gtk_file_selection_new (_("Select File")); gtk_container_set_border_width (GTK_CONTAINER (filesel), 10); gtk_window_set_type_hint (GTK_WINDOW (filesel), GDK_WINDOW_TYPE_HINT_DIALOG); okButton = GTK_FILE_SELECTION (filesel)->ok_button; gtk_widget_show (okButton); GTK_WIDGET_SET_FLAGS (okButton, GTK_CAN_DEFAULT); cancelButton = GTK_FILE_SELECTION (filesel)->cancel_button; gtk_widget_show (cancelButton); GTK_WIDGET_SET_FLAGS (cancelButton, GTK_CAN_DEFAULT); g_signal_connect_swapped ((gpointer) filesel, "delete_event", G_CALLBACK (gtk_widget_hide), GTK_OBJECT (filesel)); g_signal_connect_swapped ((gpointer) cancelButton, "clicked", G_CALLBACK (gtk_widget_hide), GTK_OBJECT (filesel)); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (filesel, filesel, "filesel"); GLADE_HOOKUP_OBJECT_NO_REF (filesel, okButton, "okButton"); GLADE_HOOKUP_OBJECT_NO_REF (filesel, cancelButton, "cancelButton"); return filesel; } GtkWidget* GUI::Settings::create() { GtkWidget *vbox26; GtkWidget *hbox40; GtkWidget *hbuttonbox7; GtkWidget *button75; GtkWidget *button76; GtkWidget *button79; GtkWidget *vbox28; GtkWidget *label119; GtkWidget *hbox41; GtkWidget *label120; GtkWidget *spinbutton2; GtkWidget *checkbutton9; GtkWidget *checkbutton10; GtkWidget *hbox42; GtkWidget *checkbutton11; GtkWidget *checkbutton12; GtkWidget *hbox77; GtkWidget *label206; GtkWidget *spinbutton3; GtkWidget *table4; GtkWidget *label121; GtkWidget *label122; GtkWidget *label123; GtkWidget *entry15; GtkWidget *entry16; GtkWidget *label193; GtkWidget *entry17; GtkWidget *entry22; GtkWidget *hseparator4; tooltips = gtk_tooltips_new (); settings = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (settings), _("Settings - Jigsaw Download")); vbox26 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox26); gtk_container_add (GTK_CONTAINER (settings), vbox26); hbox40 = gtk_hbox_new (TRUE, 5); gtk_widget_show (hbox40); gtk_box_pack_end (GTK_BOX (vbox26), hbox40, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox40), 10); hbuttonbox7 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox7); gtk_box_pack_start (GTK_BOX (hbox40), hbuttonbox7, TRUE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox7), GTK_BUTTONBOX_END); gtk_box_set_spacing (GTK_BOX (hbuttonbox7), 8); button75 = gtk_button_new_with_mnemonic (_("OK")); gtk_widget_show (button75); gtk_container_add (GTK_CONTAINER (hbuttonbox7), button75); GTK_WIDGET_SET_FLAGS (button75, GTK_CAN_DEFAULT); button76 = gtk_button_new_with_mnemonic (_("Apply")); gtk_widget_show (button76); gtk_container_add (GTK_CONTAINER (hbuttonbox7), button76); GTK_WIDGET_SET_FLAGS (button76, GTK_CAN_DEFAULT); button79 = gtk_button_new_with_mnemonic (_("Cancel")); gtk_widget_show (button79); gtk_container_add (GTK_CONTAINER (hbuttonbox7), button79); GTK_WIDGET_SET_FLAGS (button79, GTK_CAN_DEFAULT); vbox28 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox28); gtk_box_pack_start (GTK_BOX (vbox26), vbox28, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox28), 6); label119 = gtk_label_new (_("Settings")); gtk_widget_show (label119); gtk_box_pack_start (GTK_BOX (vbox28), label119, TRUE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label119), GTK_JUSTIFY_CENTER); hbox41 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox41); gtk_box_pack_start (GTK_BOX (vbox28), hbox41, TRUE, TRUE, 0); label120 = gtk_label_new (_("Maximum number of simultaneous downloads")); gtk_widget_show (label120); gtk_box_pack_start (GTK_BOX (hbox41), label120, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label120), GTK_JUSTIFY_RIGHT); gtk_misc_set_padding (GTK_MISC (label120), 2, 0); spinbutton2_adj = gtk_adjustment_new (1, 1, 5, 1, 10, 10); spinbutton2 = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton2_adj), 1, 0); gtk_widget_show (spinbutton2); gtk_box_pack_start (GTK_BOX (hbox41), spinbutton2, FALSE, FALSE, 0); checkbutton9 = gtk_check_button_new_with_mnemonic (_("Display tooltips")); gtk_widget_show (checkbutton9); gtk_box_pack_start (GTK_BOX (vbox28), checkbutton9, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton9), TRUE); checkbutton10 = gtk_check_button_new_with_mnemonic (_("Switch to download screen once download of a .jigdo file is complete")); gtk_widget_show (checkbutton10); gtk_box_pack_start (GTK_BOX (vbox28), checkbutton10, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbutton10), TRUE); hbox42 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox42); gtk_box_pack_start (GTK_BOX (vbox28), hbox42, FALSE, FALSE, 0); checkbutton11 = gtk_check_button_new_with_mnemonic (_("Save .jigdo files in destination directory")); gtk_widget_show (checkbutton11); gtk_box_pack_start (GTK_BOX (hbox42), checkbutton11, TRUE, FALSE, 0); checkbutton12 = gtk_check_button_new_with_mnemonic (_("Save .template files in destination directory")); gtk_widget_show (checkbutton12); gtk_box_pack_start (GTK_BOX (hbox42), checkbutton12, TRUE, FALSE, 0); hbox77 = gtk_hbox_new (FALSE, 4); gtk_widget_show (hbox77); gtk_box_pack_start (GTK_BOX (vbox28), hbox77, TRUE, TRUE, 0); label206 = gtk_label_new (_("Remove finished download jobs from list after (seconds)")); gtk_widget_show (label206); gtk_box_pack_start (GTK_BOX (hbox77), label206, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label206), GTK_JUSTIFY_RIGHT); gtk_misc_set_padding (GTK_MISC (label206), 2, 0); spinbutton3_adj = gtk_adjustment_new (1, 1, 100000000, 1, 10, 10); spinbutton3 = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton3_adj), 1, 0); gtk_widget_show (spinbutton3); gtk_box_pack_start (GTK_BOX (hbox77), spinbutton3, FALSE, FALSE, 0); table4 = gtk_table_new (4, 2, FALSE); gtk_widget_show (table4); gtk_box_pack_start (GTK_BOX (vbox28), table4, TRUE, TRUE, 0); gtk_table_set_row_spacings (GTK_TABLE (table4), 2); gtk_table_set_col_spacings (GTK_TABLE (table4), 4); label121 = gtk_label_new (_("HTTP proxy")); gtk_widget_show (label121); gtk_table_attach (GTK_TABLE (table4), label121, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (label121), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (label121), 7.45058e-09, 0.5); gtk_misc_set_padding (GTK_MISC (label121), 2, 0); label122 = gtk_label_new (_("FTP proxy")); gtk_widget_show (label122); gtk_table_attach (GTK_TABLE (table4), label122, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (label122), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label122), 7.45058e-09, 0.5); gtk_misc_set_padding (GTK_MISC (label122), 2, 0); label123 = gtk_label_new (_("no proxy")); gtk_widget_show (label123); gtk_table_attach (GTK_TABLE (table4), label123, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label123), 7.45058e-09, 0.5); gtk_misc_set_padding (GTK_MISC (label123), 2, 0); entry15 = gtk_entry_new (); gtk_widget_show (entry15); gtk_table_attach (GTK_TABLE (table4), entry15, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); entry16 = gtk_entry_new (); gtk_widget_show (entry16); gtk_table_attach (GTK_TABLE (table4), entry16, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); label193 = gtk_label_new (_("Default servers")); gtk_widget_show (label193); gtk_table_attach (GTK_TABLE (table4), label193, 0, 1, 3, 4, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_label_set_justify (GTK_LABEL (label193), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment (GTK_MISC (label193), 0, 0.5); entry17 = gtk_entry_new (); gtk_widget_show (entry17); gtk_table_attach (GTK_TABLE (table4), entry17, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, entry17, _("Enter a list of domains (separated with spaces) for which you do not want to use the proxies you set up above. Example: \"mycompany.com local.network.lan\""), NULL); entry22 = gtk_entry_new (); gtk_widget_show (entry22); gtk_table_attach (GTK_TABLE (table4), entry22, 1, 2, 3, 4, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_tooltips_set_tip (tooltips, entry22, _("Enter a list of domains (separated with spaces) for which you do not want to use the proxies you set up above. Example: \"mycompany.com local.network.lan\""), NULL); hseparator4 = gtk_hseparator_new (); gtk_widget_show (hseparator4); gtk_box_pack_start (GTK_BOX (vbox26), hseparator4, FALSE, FALSE, 0); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (settings, settings, "settings"); GLADE_HOOKUP_OBJECT (settings, vbox26, "vbox26"); GLADE_HOOKUP_OBJECT (settings, hbox40, "hbox40"); GLADE_HOOKUP_OBJECT (settings, hbuttonbox7, "hbuttonbox7"); GLADE_HOOKUP_OBJECT (settings, button75, "button75"); GLADE_HOOKUP_OBJECT (settings, button76, "button76"); GLADE_HOOKUP_OBJECT (settings, button79, "button79"); GLADE_HOOKUP_OBJECT (settings, vbox28, "vbox28"); GLADE_HOOKUP_OBJECT (settings, label119, "label119"); GLADE_HOOKUP_OBJECT (settings, hbox41, "hbox41"); GLADE_HOOKUP_OBJECT (settings, label120, "label120"); GLADE_HOOKUP_OBJECT (settings, spinbutton2, "spinbutton2"); GLADE_HOOKUP_OBJECT (settings, checkbutton9, "checkbutton9"); GLADE_HOOKUP_OBJECT (settings, checkbutton10, "checkbutton10"); GLADE_HOOKUP_OBJECT (settings, hbox42, "hbox42"); GLADE_HOOKUP_OBJECT (settings, checkbutton11, "checkbutton11"); GLADE_HOOKUP_OBJECT (settings, checkbutton12, "checkbutton12"); GLADE_HOOKUP_OBJECT (settings, hbox77, "hbox77"); GLADE_HOOKUP_OBJECT (settings, label206, "label206"); GLADE_HOOKUP_OBJECT (settings, spinbutton3, "spinbutton3"); GLADE_HOOKUP_OBJECT (settings, table4, "table4"); GLADE_HOOKUP_OBJECT (settings, label121, "label121"); GLADE_HOOKUP_OBJECT (settings, label122, "label122"); GLADE_HOOKUP_OBJECT (settings, label123, "label123"); GLADE_HOOKUP_OBJECT (settings, entry15, "entry15"); GLADE_HOOKUP_OBJECT (settings, entry16, "entry16"); GLADE_HOOKUP_OBJECT (settings, label193, "label193"); GLADE_HOOKUP_OBJECT (settings, entry17, "entry17"); GLADE_HOOKUP_OBJECT (settings, entry22, "entry22"); GLADE_HOOKUP_OBJECT (settings, hseparator4, "hseparator4"); GLADE_HOOKUP_OBJECT_NO_REF (settings, tooltips, "tooltips"); gtk_widget_grab_default (button75); return settings; } GtkWidget* GUI::License::create() { license = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (license), _("License for Jigsaw Download")); gtk_window_set_default_size (GTK_WINDOW (license), 516, 300); licenseScroll = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (licenseScroll); gtk_container_add (GTK_CONTAINER (license), licenseScroll); gtk_container_set_border_width (GTK_CONTAINER (licenseScroll), 4); GTK_WIDGET_UNSET_FLAGS (licenseScroll, GTK_CAN_FOCUS); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (licenseScroll), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (licenseScroll), GTK_SHADOW_IN); licenseText = gtk_text_view_new (); gtk_widget_show (licenseText); gtk_container_add (GTK_CONTAINER (licenseScroll), licenseText); gtk_text_view_set_editable (GTK_TEXT_VIEW (licenseText), FALSE); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (licenseText), GTK_WRAP_WORD); gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (licenseText), FALSE); gtk_text_view_set_left_margin (GTK_TEXT_VIEW (licenseText), 4); gtk_text_view_set_right_margin (GTK_TEXT_VIEW (licenseText), 4); g_signal_connect_swapped ((gpointer) license, "delete_event", G_CALLBACK (gtk_widget_hide), GTK_OBJECT (license)); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (license, license, "license"); GLADE_HOOKUP_OBJECT (license, licenseScroll, "licenseScroll"); GLADE_HOOKUP_OBJECT (license, licenseText, "licenseText"); return license; } jigdo-0.7.3/src/gtk/interface.hh0000644000175000017500000000532310433432676016277 0ustar richardrichard// Automatically created from `interface.cc.tmp' by glade-filter.awk #include #ifndef INTERFACE_HH #define INTERFACE_HH #include #include namespace GUI { struct Window { GtkWidget* create(); GtkWidget *window; GtkWidget *windowVbox; GtkWidget *toolbarHandle; GtkWidget *toolbar; GtkIconSize tmp_toolbar_icon_size; GtkWidget *toolbarOpen; GtkWidget *toolbarReuse; GtkWidget *toolbarSettings; GtkWidget *toolbarExit; GtkWidget *windowPaned; GtkWidget *invisibleNotebook; GtkWidget *pageOpen; GtkWidget *aboutJigdoLogo; AtkObject *atko; GtkWidget *aboutBgnd; GtkWidget *aboutJigdoLabel; GtkWidget *aboutJigdoButton; GtkWidget *aboutJigdoButtonLabel; GtkWidget *openButton; GtkWidget *open_sourceURLLabel; GtkWidget *open_destinationLabel; GtkWidget *open_URL; GtkWidget *open_dest; GtkWidget *open_URLSel; GtkWidget *open_destSel; GtkWidget *enterUrlLabel; GtkWidget *pageOpenLabel; GtkWidget *pageReuse; GtkWidget *reuse_path; GtkWidget *reuse_pathSel; GtkWidget *reuse_scanButton; GtkWidget *reuse_clearButton; GtkWidget *pageReuseLabel; GtkWidget *pageDownload; GtkWidget *download_buttonInfo; GtkWidget *download_closeButton; GtkWidget *download_restartButton; GtkWidget *download_stopButton; GtkWidget *download_pauseButton; GtkWidget *download_startButton; GtkWidget *download_dest; GtkWidget *download_progress; GtkWidget *download_status; GtkWidget *download_URL; GtkWidget *pageDownloadLabel; GtkWidget *pageJigdo; GtkWidget *jigdo_InfoVbox; GtkWidget *jigdo_ShortInfo; GtkWidget *jigdo_Info; GSList *radiobutton1_group ; GtkWidget *combo_entry1; GtkWidget *combo_entry2; GtkWidget *combo_entry3; GtkWidget *jigdo_buttonInfo; GtkWidget *jigdo_closeButton; GtkWidget *jigdo_restartButton; GtkWidget *jigdo_stopButton; GtkWidget *jigdo_pauseButton; GtkWidget *jigdo_startButton; GtkWidget *jigdo_status; GtkWidget *jigdo_progress; GtkWidget *jigdo_dest; GtkWidget *jigdo_URL; GtkWidget *pageJigdoLabel; GtkWidget *progressScroll; GtkWidget *jobs; GtkTooltips *tooltips; }; struct Filesel { GtkWidget* create(); GtkWidget *filesel; GtkWidget *okButton; GtkWidget *cancelButton; }; struct Settings { GtkWidget* create(); GtkWidget *settings; GtkObject *spinbutton2_adj; GtkObject *spinbutton3_adj; GtkTooltips *tooltips; }; struct License { GtkWidget* create(); GtkWidget *license; GtkWidget *licenseScroll; GtkWidget *licenseText; }; } // namespace GUI #endif /* INTERFACE_HH */ jigdo-0.7.3/src/gtk/jigdo.cc0000644000175000017500000001432310264046720015412 0ustar richardrichard/* $Id: jigdo.cc,v 1.14 2005/07/09 22:21:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ #include #if DEBUG # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #if WINDOWS # include #endif //______________________________________________________________________ #if WINDOWS string packageDataDir; #endif //______________________________________________________________________ namespace { #if WINDOWS const char* binaryName = "jigdo"; #else const char* binaryName; #endif vector optUris; enum OptProxy { GUESS, ON, OFF } optProxy = GUESS; string optDebug; void tryHelp() { cerr << subst(_("%L1: Try `%L1 -h' or `man jigdo' for more " "information"), binaryName) << endl; exit(3); } enum { LONGOPT_DEBUG = 0x100, LONGOPT_NODEBUG }; inline void cmdOptions(int argc, char* argv[]) { bool optHelp = false; bool optVersion = false; if (!WINDOWS) binaryName = argv[0]; bool error = false; while (true) { static const struct option longopts[] = { { "debug", optional_argument, 0, LONGOPT_DEBUG }, { "help", no_argument, 0, 'h' }, { "no-debug", no_argument, 0, LONGOPT_NODEBUG }, { "proxy", required_argument, 0, 'Y' }, { "version", no_argument, 0, 'v' }, { 0, 0, 0, 0 } }; int c = getopt_long(argc, argv, "hvY:", longopts, 0); if (c == -1) break; switch (c) { case 'h': optHelp = true; break; case 'v': optVersion = true; break; case 'Y': if (strcmp(optarg, "guess") == 0) optProxy = GUESS; else if (strcmp(optarg, "on") == 0) optProxy = ON; else if (strcmp(optarg, "off") == 0) optProxy = OFF; else cerr << subst(_("%L1: Please specify `on', `off' or `guess' after" " --proxy"), binaryName) << endl; break; case LONGOPT_DEBUG: if (optarg) optDebug = optarg; else optDebug = "all"; break; case LONGOPT_NODEBUG: optDebug.erase(); break; case '?': error = true; case ':': break; default: msg("getopt returned %1", static_cast(c)); break; } } if (error) tryHelp(); if (optHelp || optVersion) { if (optVersion) cout << "jigdo version " JIGDO_VERSION << endl; if (optHelp) cout << subst(_( "Usage: %L1 [OPTIONS] [URL]\n" "Options:\n" " -h --help Output help\n" " -Y --proxy=on/off/guess [guess]\n" " Turn proxy on (i.e. use env vars http_proxy,\n" " ftp_proxy, all_proxy) or off, or guess (from\n" " Mozilla/KDE/wget/lynx settings)\n" " -v --version Output version info\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n"), binaryName) << endl; exit(0); } Logger::scanOptions(optDebug, binaryName); while (optind < argc) optUris.push_back(argv[optind++]); } #if WINDOWS inline void getPackageDataDir() { char buf[MAX_PATH]; GetModuleFileName(NULL, buf, MAX_PATH); char* end = strrchr(buf, '\\'); packageDataDir.append(buf, end - buf + 1); } void noPrintHandler(const char*) { return; } void noLogHandler(const gchar*, GLogLevelFlags, const gchar*, gpointer) { return; } #endif } // local namespace //______________________________________________________________________ int main(int argc, char *argv[]) { # if WINDOWS getPackageDataDir(); if (!DEBUG) { // Switch off error messages, to avoid that the console window is opened g_set_print_handler(noPrintHandler); g_set_printerr_handler(noPrintHandler); g_log_set_handler(0, static_cast(-1), noLogHandler, 0); } # endif # if ENABLE_NLS bindtextdomain(PACKAGE, packageLocaleDir); textdomain(PACKAGE); bind_textdomain_codeset(PACKAGE, "UTF-8"); # endif # if DEBUG Logger::setEnabled("general"); # else Debug::abortAfterFailedAssertion = false; # endif try { // Initialize GTK+ and display window gtk_set_locale(); gtk_init(&argc, &argv); { # if !WINDOWS add_pixmap_directory("../gfx"); add_pixmap_directory("gfx"); # endif string pixDir = packageDataDir; pixDir += "pixmaps"; add_pixmap_directory("..\\gfx"); add_pixmap_directory(pixDir.c_str()); } cmdOptions(argc, argv); GUI::create(); gtk_widget_show(GUI::window.window); // Initialize networking code Download::init(); if (optProxy == OFF) { // Make libcurl ignore environment variables, simply by unsetting them putenv("http_proxy="); putenv("https_proxy="); putenv("ftp_proxy="); putenv("gopher_proxy="); putenv("no_proxy="); putenv("all_proxy="); } else if (optProxy == GUESS) { proxyGuess(); } // Start downloads of any URIs specified on command line const char* dest = g_get_current_dir(); for (vector::const_iterator i = optUris.begin(), e = optUris.end(); i != e; ++i) JobLine::create(i->c_str(), dest); optUris.clear(); g_free((gpointer)dest); gtk_main(); // Here be dragons } catch (Cleanup c) { msg("[Cleanup %1]", c.returnValue); GUI::jobList.finalize(); Download::cleanup(); return c.returnValue; } GUI::jobList.finalize(); Download::cleanup(); # if DEBUG && !WINDOWS const char* preload = getenv("LD_PRELOAD"); if (preload != 0 && strstr(preload, "libmemintercept") != 0) { msg("Detected memprof - doing sleep() to allow you to find leaks"); while (true) sleep(1024); } # endif return 0; } jigdo-0.7.3/src/gtk/jobline.cc0000644000175000017500000001267310067256756015763 0ustar richardrichard/* $Id: jobline.cc,v 1.7 2004/06/26 11:28:46 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. One line in a JobList, in the lower part of the jigdo GUI window */ #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_TO(JobList::debug) namespace { /* Returns true if last characters of uri are the same as ext. Cannot use string::compare because of incompatibility between GCC2.95/3.0 */ inline bool compareEnd(const string& uri, const char* ext) { static const unsigned extLen = strlen(ext); if (uri.size() < extLen) return false; unsigned pos = uri.size() - extLen; for (unsigned i = 0; i < extLen; ++i) if (ext[i] != uri[pos + i]) return false; return true; } } //______________________________________________________________________ void JobLine::create(const char* uri, const char* dest) { if (*uri == '\0') { MessageBox* m = new MessageBox(MessageBox::INFO, MessageBox::OK, _("Field for source URL/filename is empty"), _("Please enter an \"http\" or \"ftp\" URL to download, or the " "name/URL of a .jigdo file to process.")); m->show(); return; } /* Removing trailing '\' is necessary on Windows. Also, makeimagedl expects no trailing DIRSEP. */ string destination(dest); unsigned destLen = destination.length(); while (destLen > 1 && destination[destLen - 1] == DIRSEP) --destLen; destination.resize(destLen); struct stat fileInfo; int statDest = stat(destination.c_str(), &fileInfo); bool destIsDir = S_ISDIR(fileInfo.st_mode); if (statDest != 0 && errno != ENOENT) { // destination is present, but error accessing it MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing destination"), subst(_("The destination `%LE1' is present, but cannot be accessed: " "%LE2"), destination, strerror(errno))); m->show(); return; } // Also create parent object unsigned lastDirSep = destination.rfind(DIRSEP); if (lastDirSep == 0) lastDirSep = 1; // Parent of "/tmp" is "/" not "" string destParent(destination, 0, lastDirSep); // can be ==destination int statDestParent = stat(destParent.c_str(), &fileInfo); if (statDestParent != 0) { MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, _("Error accessing directory to save to"), subst(_("The destination `%LE1' cannot be accessed: %LE2"), destination, strerror(errno))); m->show(); return; } /* We perform a regular download if the destination is either a filename (as opposed to a dir name), or the URI is not a jigdo file. */ string uriStr(uri); if ((statDest != 0 && errno == ENOENT) // destination does not exist || (statDest == 0 && !destIsDir) // exists as file || !compareEnd(uriStr, ".jigdo")) { // URI end not ".jigdo" if (statDest == 0 && destIsDir) { // Append filename from source to directory name unsigned lastDirSep = uriStr.rfind('/'); destination += DIRSEP; /* If URL ends with /, use index.html as filename. DON'T try to use any name supplied by the server during a HTTP redirect - a malicious server could trick us into overwriting files! */ if (lastDirSep == uriStr.length() - 1) destination.append("index.html"); else destination.append(uriStr, lastDirSep + 1, string::npos); } GtkSingleUrl* result = new GtkSingleUrl(uriStr, destination); GUI::jobList.prepend(result); result->run(); // NB result may already have deleted itself at this point return; } static bool dom = false; if (!dom) { dom = true; MessageBox* m = new MessageBox(MessageBox::INFO, MessageBox::NONE, "values of β will give rise to dom!", "Processing of .jigdo files is not yet implemented - only " "downloads of single files (e.g. .iso images) work.\n" "jigdo might crash any minute now - don't complain, I'm working on " "it! :-)\n" "Please use jigdo-lite to process .jigdo URLs."); m->addButton(_("_Cool!"), 0); m->addButton(_("_Awesome!"), 0); m->addButton(_("_Fantastic!"), GTK_RESPONSE_CANCEL); m->show(); } // Not a regular download => a .jigdo download // destination is a directory, uriStr ends in ".jigdo" GtkMakeImage* result = new GtkMakeImage(uriStr, destination); GUI::jobList.prepend(result); try { result->run(); } catch (Error e) { MessageBox* m = new MessageBox(MessageBox::ERROR, MessageBox::OK, 0, subst("%E1", e.message)); m->show(); delete result; return; } return; } //______________________________________________________________________ JobLine::~JobLine() { if (needTicks()) jobList()->unregisterTicks(); jobList()->erase(row()); } //______________________________________________________________________ void JobLine::waitTick() { if (--waitCountdown == 0) callRegularly(waitDestination); debug("waitCountdown=%1", waitCountdown); } jigdo-0.7.3/src/gtk/jobline.fh0000644000175000017500000000100607701377734015760 0ustar richardrichard/* $Id: jobline.fh,v 1.1.1.1 2003/07/04 22:29:48 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Interface to the GtkTreeView of running jobs (downloads etc), GUI::window.jobs, i.e. the list at the bottom of the jigdo window. */ class JobLine; jigdo-0.7.3/src/gtk/jobline.hh0000644000175000017500000001171210226060300015735 0ustar richardrichard/* $Id: jobline.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file One line in a JobList, in the lower part of the jigdo GUI window */ #ifndef JOBLINE_HH #define JOBLINE_HH #include #include #include #include //______________________________________________________________________ /** One "job", e.g. file download, scanning through files, or image download (which has many file downloads as children). Virtual class. */ class JobLine : NoCopy { friend class JobList; // Overwrites jobVec and rowVal on insert() public: inline JobLine(); /** If the JobLine was in a needTicks() state, jobList()->unregisterTicks() is called by this dtor. Additionally, removes the line from the GtkTreeList. */ virtual ~JobLine(); /** Start/restart this job. When called, must also create GUI elements for this Job in row number row() of GtkTreeView jobList()->view(). Is called after Job is added to the list, and when it is restarted (if at all). Child classes should provide an implementation. @return SUCCESS if everything OK, FAILURE if this object has deleted itself. */ virtual bool run() = 0; /** Called when the JobLine's line is selected in the list. */ virtual void selectRow() = 0; /** Is the job currently paused? */ virtual bool paused() const = 0; /** Pause the job. */ virtual void pause() = 0; /** Continue executing the job. */ virtual void cont() = 0; /** Stop executing the job. */ virtual void stop() = 0; /** When called, must fill in current nr of bytes downloaded and total nr of bytes. */ virtual void percentDone(uint64* cur, uint64* total) = 0; /** Kind of a JobLine factory, called when the user clicks on OK to start a new download. It decides whether a normal file download or a .jigdo download is needed and creates the appropriate object. Additionally, the Job is appended to the list of jobs, and run. @param uri What to download (if it ends in ".jigdo" and dest is a directory, will start jigdo processing) @param dest Where to put downloaded data, either a dir or a file. */ static void create(const char* uri, const char* dest); protected: typedef void (JobLine::*TickHandler)(); /// Pointer to JobList inline JobList* jobList() const; /** Iterator for our row in the list. Do not modify the returned value, copy it instead! (It is non-const because various gtk functions which read it are incorrectly(?) declared non-const.) NB: The returned iter is UNINITIALIZED until the JobLine is added to a JobList! */ inline GtkTreeIter* row(); /** Register a tick handler. The handler will be called every JobList::TICK_INTERVAL milliseconds by a GTK+ callback function that JobList registers. Handy for updating progress reports at regular intervals. Only register a handler if you really need it. Otherwise, pass 0 as the handler function pointer - if no JobLine at all registers a tick handler, the callback fnc will not be registered at all, saving some CPU time. */ inline void callRegularly(TickHandler handler); /** Return current tick handler */ TickHandler getHandler() const { return tick; } /** Does this object need to be called regularly? */ bool needTicks() const { return tick != 0; } /** Wait appropriate nr of ticks, then register the supplied handler. Effectively, this means the JobLine pauses for a while - e.g. so the user can read some status report. */ inline void callRegularlyLater(const int milliSec, TickHandler handler); private: void waitTick(); int waitCountdown; TickHandler waitDestination; JobList* jobVec; TickHandler tick; /* A reference to the row that this JobLine object is stored in. GtkTreeModel doc sez: GtkTreeStore and GtkListStore "guarantee that an iterator is valid for as long as the node it refers to is valid" */ GtkTreeIter rowVal; }; //====================================================================== JobLine::JobLine() : jobVec(0), tick(0) { } JobList* JobLine::jobList() const { return jobVec; } GtkTreeIter* JobLine::row() { return &rowVal; } //________________________________________ void JobLine::callRegularly(TickHandler handler) { if (!needTicks() && handler != 0) jobList()->registerTicks(); // no previous handler, now handler else if (needTicks() && handler == 0) jobList()->unregisterTicks(); // previous handler present, now none tick = handler; } void JobLine::callRegularlyLater(const int milliSec, TickHandler handler) { waitCountdown = milliSec / JobList::TICK_INTERVAL; waitDestination = handler; callRegularly(&JobLine::waitTick); } #endif jigdo-0.7.3/src/gtk/joblist.cc0000644000175000017500000002661610261546437016003 0ustar richardrichard/* $Id: joblist.cc,v 1.15 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Interface to the GtkTreeView of running jobs (downloads etc), window.jobs */ #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ #ifndef DOXYGEN_SKIP # if DEBUG Logger JobList::debug("joblist"); # endif #endif JobList GUI::jobList; const char* const JobList::PROGRESS_IMAGE_FILE = "progress-green.png"; GdkPixbuf* JobList::progressImage = 0; vector JobList::progressGfx; GValue JobList::progressValue; //______________________________________________________________________ void JobList::finalize() { /* Don't delete any widgets, GTK should take care of this itself when the window is deleted. */ // Delete active callback, if any if (selectRowIdleId != 0) { g_source_remove(selectRowIdleId); selectRowIdleId = 0; } /* Delete Jobs. When deleted, the job will erase itself from the list, so just keep getting the first list element. Careful: This might be called during static finalization, even in the case that GTK+ was not initialized (e.g. because DISPLAY was not set). So only make calls to GTK+ if non-empty. */ GtkTreeIter row; if (!empty()) { while (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store()), &row)) { debug("~JobList: Deleting %1", &row); delete get(&row); } } if (store()) { g_object_unref(store()); storeVal = 0; } for (vector::iterator i = progressGfx.begin(), e = progressGfx.end(); i != e; ++i) g_object_unref(*i); progressGfx.clear(); } JobList::~JobList() { finalize(); } //______________________________________________________________________ // Called from gtk-gui.cc void JobList::postGtkInit() { // Set up list of downloads ("jobs") in lower part of window Assert(store() == 0); Paranoid(NR_OF_COLUMNS == 3); storeVal = gtk_tree_store_new(NR_OF_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); gtk_tree_view_set_model(view(), GTK_TREE_MODEL(store())); // The status column contains two renderers: GtkTreeViewColumn* statusColumn = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(statusColumn, _("Status")); // 1. Pixbuf renderer, value set via function GtkCellRenderer* pixbufRenderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(statusColumn, pixbufRenderer, FALSE); gtk_tree_view_column_set_cell_data_func(statusColumn, pixbufRenderer, &pixbufForJobLine, (gpointer)this, NULL); // 2. Text renderer, value set directly from COLUMN_STATUS GtkCellRenderer* statusRenderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_end(statusColumn, statusRenderer, TRUE); gtk_tree_view_column_set_attributes(statusColumn, statusRenderer, "markup", COLUMN_STATUS, NULL); // Set column sizes and other attributes gtk_tree_view_column_set_sizing(statusColumn,GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_fixed_width(statusColumn, WIDTH_STATUS); gtk_tree_view_column_set_resizable(statusColumn, TRUE); gtk_tree_view_append_column(view(), statusColumn); GtkTreeViewColumn* objectColumn = gtk_tree_view_column_new_with_attributes( _("Click on lines below to display corresponding info above"), gtk_cell_renderer_text_new(), "text", COLUMN_OBJECT, NULL); gtk_tree_view_column_set_resizable(objectColumn, TRUE); gtk_tree_view_append_column(view(), objectColumn); gtk_tree_view_set_expander_column(view(), objectColumn); gtk_tree_view_set_reorderable(view(), FALSE); /* In order to make the GUI easier to understand for first-time users, don't display list headers until their info is really appropriate. */ gtk_tree_view_set_headers_visible(view(), FALSE); // Register callback function if a row is selected gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(view()), &JobList::selectRowCallback, this, NULL); // Multiple selected rows possible gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view()), GTK_SELECTION_MULTIPLE); //____________________ pixbufForJobLine_init(); } //______________________________________________________________________ void JobList::pixbufForJobLine_init() { if (progressImage != 0) return; progressImage = create_pixbuf(PROGRESS_IMAGE_FILE); if (progressImage == 0) return; // Init value memset(&progressValue, 0, sizeof(progressValue)); g_value_init(&progressValue, G_TYPE_FROM_INSTANCE(progressImage)); unsigned width = gdk_pixbuf_get_width(progressImage); unsigned height = gdk_pixbuf_get_height(progressImage); // height must be evenly divisible by PROGRESS_SUBDIV Assert(height % PROGRESS_SUBDIV == 0); unsigned subHeight = height / PROGRESS_SUBDIV; for (unsigned y = 0; y < height; y += subHeight) { GdkPixbuf* sub = gdk_pixbuf_new_subpixbuf(progressImage, 0, y, width, subHeight); progressGfx.push_back(sub); } Paranoid(progressGfx.size() == PROGRESS_SUBDIV); /* progressImage shares pixels with the subpixbufs, so will not be deleted immediately: */ g_object_unref(progressImage); } //________________________________________ void JobList::pixbufForJobLine(GtkTreeViewColumn*, GtkCellRenderer* cell, GtkTreeModel*, GtkTreeIter* iter, gpointer data) { JobList* self = static_cast(data); if (progressImage == 0) return; JobLine* j = self->get(iter); uint64 cur, total; j->percentDone(&cur, &total); unsigned subNr; if (cur == 0 && total == 0) { subNr = 0; } else if (cur >= total) { subNr = PROGRESS_SUBDIV - 1; } else { subNr = cur * (PROGRESS_SUBDIV - 1) / total; } if (subNr >= progressGfx.size() || progressGfx[subNr] == 0) return; g_value_set_object(&progressValue, (gpointer)progressGfx[subNr]); g_object_set_property(G_OBJECT(cell), "pixbuf", &progressValue); } //______________________________________________________________________ void JobList::prepend(JobLine* j, JobLine* parent) { Paranoid(j != 0); GtkTreeIter* parentIter = (parent == 0 ? NULL : parent->row()); GtkAdjustment* scrollbar = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(GUI::window.progressScroll)); double pos = gtk_adjustment_get_value(scrollbar); gtk_tree_store_prepend(store(), j->row(), parentIter); // Add empty row gtk_tree_store_set(store(), j->row(), COLUMN_DATA, j, -1); if (pos < 0.1) { // We were at top, so scroll to top again gtk_tree_view_scroll_to_point(view(), -1, 0); // Actually, has no effect g_idle_add((GSourceFunc)progressScrollToTop, view()); } if (++sizeVal == 1) gtk_tree_view_set_headers_visible(view(), TRUE); ++entryCountVal; j->jobVec = this; } gboolean JobList::progressScrollToTop(gpointer view) { gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(view), -1, 0); return FALSE; } void JobList::append(JobLine* j, JobLine* parent) { Paranoid(j != 0); GtkTreeIter* parentIter = (parent == 0 ? NULL : parent->row()); gtk_tree_store_append(store(), j->row(), parentIter); // Add empty row gtk_tree_store_set(store(), j->row(), COLUMN_DATA, j, -1); if (++sizeVal == 1) gtk_tree_view_set_headers_visible(view(), TRUE); ++entryCountVal; j->jobVec = this; } void JobList::erase(GtkTreeIter* row) { // Delete entry JobLine* j = get(row); gtk_tree_store_remove(store(), row); if (j != 0) { //delete j; Paranoid(entryCountVal > 0); --entryCountVal; } Paranoid(sizeVal > 0); --sizeVal; } void JobList::makeRowBlank(GtkTreeIter* row) { // Decrease number of data entries JobLine* j = get(row); if (j != 0) { Paranoid(entryCountVal > 0); --entryCountVal; } // Set row blank, clear data field Paranoid(NR_OF_COLUMNS == 3); gtk_tree_store_set(store(), row, 0, 0, 1, 0, 2, 0, -1); // Deselect row GtkTreeSelection* selection = gtk_tree_view_get_selection(view()); gtk_tree_selection_unselect_iter(selection, row); } //______________________________________________________________________ #if DEBUG void JobList::assertValid() const { size_type realEntryCount = 0, realSize = 0; int realNeedTicks = 0; GtkTreeIter row; gboolean ok = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store()), &row); while (ok) { ++realSize; JobLine* j = get(&row); if (j != 0) { ++realEntryCount; if (j->needTicks()) ++realNeedTicks; } ok = gtk_tree_model_iter_next_depth(GTK_TREE_MODEL(store()), &row); } if (realSize != size()) debug("realSize=%1 size()=%2", realSize, size()); Assert(realEntryCount == entryCount()); Assert(realSize == size()); Assert(entryCount() <= size()); Assert(realNeedTicks == needTicks); } #endif //______________________________________________________________________ gint JobList::timeoutCallback(gpointer jobList) { JobList* self = static_cast(jobList); if (DEBUG) self->assertValid(); // For each JobLine entry in the list that needs it, make a call to tick() GtkTreeIter row; gboolean ok = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(self->store()), &row); while (ok) { JobLine* j = self->get(&row); ok = gtk_tree_model_iter_next_depth(GTK_TREE_MODEL(self->store()), &row); // Yargh! This syntax took me 30 minutes to get right: if (j != 0 && j->needTicks()) (j->*(j->tick))(); } return TRUE; } //______________________________________________________________________ /* Called when the user clicks on a line in the job list. Simply passes the click on to the object whose line was clicked on, by calling its selectRow() virtual method. This is often called >1 times for just one click. Thus, register a callback which is executed at the next iteration of the main loop, and only call selectRow() if the callback isn't yet registered. */ gboolean JobList::selectRowCallback(GtkTreeSelection* /*selection*/, GtkTreeModel* model, GtkTreePath* path, gboolean path_currently_selected, gpointer data) { if (path_currently_selected) return TRUE; JobList* self = static_cast(data); if (self->selectRowIdleId != 0) return TRUE; // Callback already pending - do nothing self->selectRowIdleId = g_idle_add(&selectRowIdle, self); debug("selectRowCallback"); GtkTreeIter row; bool ok = gtk_tree_model_get_iter(model, &row, path); Assert(ok); JobLine* job = self->get(&row); if (job != 0) job->selectRow(); return TRUE; } gboolean JobList::selectRowIdle(gpointer data) { debug("selectRowIdle"); JobList* self = static_cast(data); self->selectRowIdleId = 0; return FALSE; // "Don't call me again" } jigdo-0.7.3/src/gtk/joblist.hh0000644000175000017500000002177510226060300015773 0ustar richardrichard/* $Id: joblist.hh,v 1.10 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Interface to the GtkTreeView of running jobs (downloads etc), GUI::window.jobs, i.e. the list at the bottom of the jigdo window. */ #ifndef JOBLIST_HH #define JOBLIST_HH #include #include #include #if DEBUG # include #endif #include #include #include #include #include #include //______________________________________________________________________ /** A bit like a vector, but uses the GtkTreeStore for storing the elements. There can be empty entries in the vector which hold null pointers and which are displayed as empty lines on screen. ~JobList deletes all JobLine objects in the list. */ class JobList : NoCopy { public: enum { /** Pixmap "% done" + text message ("x kB/sec" etc) */ COLUMN_STATUS, /** URL */ COLUMN_OBJECT, /** pointer to JobLine object (not displayed on screen) */ COLUMN_DATA, /** Assumed number of columns in job display (progress bar, URL) */ NR_OF_COLUMNS }; static const int WIDTH_STATUS = 280; /** Time values are in milliseconds. All values should be multiples of TICK_INTERVAL */ static const int TICK_INTERVAL = 250; // = progress report update interval LOCAL_DEBUG_UNIT_DECL; typedef unsigned size_type; inline JobList(); /** Any Jobs still in the list are deleted */ ~JobList(); /** Like the dtor; later call to the dtor will not cause anything to happen */ void finalize(); /** The GTK data structure that contains the linked list of items for this JobList. */ inline GtkTreeStore* store() const; /** The GTK data structure responsible for drawing this list on screen. Currently, we cheat and always return the same static object, GUI::window.jobs, rather than storing a pointer in the JobList. This only works as long as only one JobList is ever created. */ inline GtkTreeView* view() const; /** Size *includes* null pointer entries. */ inline size_type size() const; /** Number of non-null entries (always <= size()) */ inline size_type entryCount() const; inline bool empty() const; /** Retrieve the data for a list entry */ inline JobLine* get(GtkTreeIter* row) const; /** Simply overwrites pointer, will not delete the old entry */ inline void set(size_type n, JobLine* j); /** Deletes row from the list. The Job object pointed to by the entry is *not* deleted. */ void erase(GtkTreeIter* row); /** Insert new JobLine before position n. Calls j->run() so the Job can visualize itself. */ inline void insert(size_type n, JobLine* j); /** Add new JobLine at start of list, or as first child of parent if parent != 0. Also scrolls to the top to display the new entry. You should call j->run() after this so the Job can visualize itself. */ void prepend(JobLine* j, JobLine* parent = 0); /** Append new JobLine at end of list or as last child of parent if parent != 0. You should call j->run() after this so the Job can visualize itself. */ void append(JobLine* j, JobLine* parent = 0); /** Make row blank by setting text labels to "". Sets data pointer to 0, like erase() does *not* delete JobLine object of that row. */ void makeRowBlank(GtkTreeIter* row); /** Set a static var of the JobList class to the supplied value. The Job that is currently selected and is in charge of updating the main window (e.g. with progress info) calls this with j==this, and subsequently uses isWindowOwner(this) to check whether it is still in charge of updating the window. This way, it is ensured that only one JobLine at a time updates the window. Supply 0 to unset. */ inline void setWindowOwner(JobLine* j); /** Check whether the supplied JobLine is in charge of updating the window. */ inline bool isWindowOwner(JobLine* j) const; /** Get the current JobLine in charge of the main window. This will be called if a button is clicked in the main window, to find the JobLine the click should be "forwarded" to. */ inline JobLine* windowOwner() const; /** (De)register a JobLine whose tick() method should be called regularly. As soon as there is at least one such JobLine, a GTK timeout function is registered which does a freeze(), then scans through the whole list calling objects' tick handler where present, then thaw()s the list. As soon as the count of tick-needing JobLines reaches 0, the timeout function is unregistered again. */ inline void registerTicks(); inline void unregisterTicks(); /** To be called sometime after gtk_init, but before any JobLines/JobVectors are used. */ void postGtkInit(); # if DEBUG /** Perform internal integrity check, failed assertion if it fails. */ void assertValid() const; # else void assertValid() const { } # endif private: // GTK+ timeout function: calls tick() on stored JobLine objects static gint timeoutCallback(gpointer jobList); // GTK+ callback function, called when a line in the list is selected static gboolean selectRowCallback(GtkTreeSelection*, GtkTreeModel*, GtkTreePath*, gboolean, gpointer); /* Function registered with GTK+, sets up pixbuf with progress bar in the GtkTreeView. */ static void pixbufForJobLine(GtkTreeViewColumn*, GtkCellRenderer* cell, GtkTreeModel*, GtkTreeIter* iter, gpointer data); // Load file with progress bar images, prepare it for display static void pixbufForJobLine_init(); // Helper for prepend() static gboolean progressScrollToTop(gpointer view); // Set selectRowIdleId to 0 static gboolean selectRowIdle(gpointer data); /* Used by initAfterGtk(): Nr of pixbufs to subdivide the progress XPM into, filename to load from. */ static const unsigned PROGRESS_SUBDIV = 61; static const char* const PROGRESS_IMAGE_FILE; static GdkPixbuf* progressImage; // Pixel data static vector progressGfx; // sub-GdkPixbufs of progressImage static GValue progressValue; GtkTreeStore* storeVal; // GTK store of the displayed list unsigned sizeVal; // Number of rows in table unsigned entryCountVal; // Number of entries (= sizeVal - nr_of_empty_rows) JobLine* windowOwnerValue; /* Count the number of entries in the list which are in a state in which they need tick() calls. */ int needTicks; int timeoutId; // as returned by gtk_timeout_add() // Callback avoids mult. calls to entries' selectRow() unsigned selectRowIdleId; }; //______________________________________________________________________ /** Global list of running jobs */ namespace GUI { extern JobList jobList; } //====================================================================== JobList::JobList() : storeVal(0), sizeVal(0), entryCountVal(0), windowOwnerValue(0), needTicks(0), selectRowIdleId(0) { // Mustn't access widgets here because GTK+ is not initialized yet! } JobList::size_type JobList::size() const { return sizeVal; } bool JobList::empty() const { return sizeVal == 0; } JobList::size_type JobList::entryCount() const { return entryCountVal; } GtkTreeStore* JobList::store() const { return storeVal; } GtkTreeView* JobList::view() const { return GTK_TREE_VIEW(GUI::window.jobs); } JobLine* JobList::get(GtkTreeIter* row) const { gpointer ptr; gtk_tree_model_get(GTK_TREE_MODEL(store()), row, COLUMN_DATA, &ptr, -1); return static_cast(ptr); } #if 0 Job* JobList::get(size_type n) { return static_cast(gtk_clist_get_row_data(list(), n)); } void JobList::set(size_type n, JobLine* j) { gtk_clist_unselect_row(list(), n, 0); gtk_clist_set_row_data(list(), n, j); if (j) { j->jobVec = this; j->rowVal = n; } } void JobList::insert(size_type n, JobLine* j) { Paranoid(j != 0); gtk_clist_insert(list(), n, noText); // 0 => no text initially gtk_clist_set_row_data(list(), n, j); ++sizeVal; ++entryCountVal; j->jobVec = this; j->rowVal = n; j->run(); } #endif void JobList::registerTicks() { if (++needTicks == 1) { timeoutId = g_timeout_add(TICK_INTERVAL, timeoutCallback, this); } debug("registerTicks: %1", needTicks); } /* No further action is required. The timeout function will unregister itself next time it is called, by returning FALSE. */ void JobList::unregisterTicks() { if (--needTicks == 0) g_source_remove(timeoutId); debug("unregisterTicks: %1", needTicks); } void JobList::setWindowOwner(JobLine* j) { windowOwnerValue = j; } bool JobList::isWindowOwner(JobLine* j) const { return windowOwnerValue==j; } JobLine* JobList::windowOwner() const { return windowOwnerValue; } #endif jigdo-0.7.3/src/gtk/messagebox.cc0000644000175000017500000001513507730450721016461 0ustar richardrichard/* $Id: messagebox.cc,v 1.6 2003/09/12 23:08:01 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Display an error box with a message and standard or user-supplied buttons. */ #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("messagebox") const char* const MessageBox::MESSAGE = "gtk-dialog-info"; const char* const MessageBox::INFO = "gtk-dialog-info"; const char* const MessageBox::WARNING = "gtk-dialog-warning"; const char* const MessageBox::QUESTION = "gtk-dialog-question"; const char* const MessageBox::ERROR = "gtk-dialog-error"; //______________________________________________________________________ void MessageBox::init(const char* type, int buttons, const char* heading, const char* message) { escapeButton = 0; ref = 0; // Create dialog layout dialog = gtk_dialog_new(); const char* title = "Jigsaw Download"; # if DEBUG static unsigned bark = 0; if ((++bark & 3) == 0) { if (strcmp(type, MESSAGE) == 0 || strcmp(type, INFO) == 0) title = "Wicked message with subliminal meaning"; else if (strcmp(type, WARNING) == 0) title = "Clueless user alert"; else if (strcmp(type, QUESTION) == 0) title = "Innocent-looking question"; else if (strcmp(type, ERROR) == 0) title = "Sigh, I _knew_ you were going to do that"; } # endif gtk_window_set_title(GTK_WINDOW(dialog), title); gtk_container_set_border_width(GTK_CONTAINER(dialog), 6); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); GtkWidget* vbox = GTK_DIALOG(dialog)->vbox; gtk_box_set_spacing(GTK_BOX(vbox), 12); gtk_widget_show(vbox); GtkWidget* hbox = gtk_hbox_new(FALSE, 12); gtk_container_set_border_width(GTK_CONTAINER(hbox), 6); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); GtkWidget* image = gtk_image_new_from_stock(type, GTK_ICON_SIZE_DIALOG); gtk_widget_show(image); gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); gtk_misc_set_alignment(GTK_MISC(image), 0.0, 0.0); // Add message label GtkWidget* msg; if (heading == 0) { label = message; } else { label = ""; label += heading; label += ""; if (message != 0 && *message != '\0') { label += "\n\n"; label += message; } } msg = gtk_label_new(label.c_str()); gtk_label_set_use_markup(GTK_LABEL(msg), TRUE); gtk_label_set_selectable(GTK_LABEL(msg), TRUE); gtk_widget_show(msg); gtk_box_pack_end(GTK_BOX(hbox), msg, FALSE, FALSE, 0); gtk_label_set_line_wrap(GTK_LABEL(msg), TRUE); // Add buttons gtk_widget_show(GTK_DIALOG(dialog)->action_area); gtk_button_box_set_layout(GTK_BUTTON_BOX(GTK_DIALOG(dialog)->action_area), GTK_BUTTONBOX_END); if (buttons & HELP) addStockButton("gtk-help", GTK_RESPONSE_HELP); if (buttons & CANCEL) addStockButton("gtk-cancel", GTK_RESPONSE_CANCEL); if (buttons & CLOSE) addStockButton("gtk-close", GTK_RESPONSE_CLOSE); if (buttons & OK) addStockButton("gtk-ok", GTK_RESPONSE_OK); g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(destroyHandler), (gpointer)this); /* "close" is emitted when the user presses Escape. The default behaviour of GTK+ 2.2 is silly: If the dialog has 1 button, nothing happens, if it has >1, the dialog is destroyed. To avoid lots of extra code, we prevent it from being destroyed and simulate a button click instead. */ g_signal_connect(G_OBJECT(dialog), "close", G_CALLBACK(closeHandler), (gpointer)this); } //______________________________________________________________________ MessageBox* MessageBox::show_noAutoClose() { if (escapeButton == 0) { /* No "Cancel" button was added to this MessageBox. If the dialog only has one button, instead simulate a click on that button when the user presses Escape. */ GtkContainer* c = GTK_CONTAINER(GTK_DIALOG(dialog)->action_area); GList* l = gtk_container_get_children(c); if (l != 0 && g_list_nth(l, 1) == NULL) { debug("Show: single button, not cancel"); escapeButton = GTK_WIDGET(g_list_first(l)->data); } } gtk_widget_show(dialog); return this; } //______________________________________________________________________ MessageBox::~MessageBox() { debug("~MessageBox this=%1 ref=%2", this, ref); if (ref != 0) { Assert(ref->get() == this); ref->messageBox = 0; } if (dialog != 0) { GtkWidget* tmp = dialog; dialog = 0; gtk_widget_destroy(tmp); } } //______________________________________________________________________ GtkWidget* MessageBox::addButton(const char* buttonText, int response) { GtkWidget* b = gtk_button_new_with_mnemonic(buttonText); gtk_widget_show(b); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), b, response); GTK_WIDGET_SET_FLAGS(b, GTK_CAN_DEFAULT); gtk_widget_grab_default(b); if (response == GTK_RESPONSE_CANCEL && escapeButton == 0) escapeButton = b; return b; } //______________________________________________________________________ GtkWidget* MessageBox::addStockButton(const char* buttonType, int response) { GtkWidget* b = gtk_button_new_from_stock(buttonType); gtk_widget_show(b); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), b, response); GTK_WIDGET_SET_FLAGS(b, GTK_CAN_DEFAULT); gtk_widget_grab_default(b); if (response == GTK_RESPONSE_CANCEL) escapeButton = b; return b; } //______________________________________________________________________ void MessageBox::destroyHandler(GtkDialog*, MessageBox* m) { debug("destroyHandler this=%1 dialog=%2", m, m->dialog); if (m->dialog != 0) delete m; } void MessageBox::autocloseHandler(GtkDialog*, int r, MessageBox* m) { debug("autoCloseHandler this=%1 dialog=%2 response=%3", m, m->dialog, r); if (r != GTK_RESPONSE_HELP) delete m; } void MessageBox::closeHandler(GtkDialog* dialog, MessageBox* m) { g_signal_stop_emission_by_name(dialog, "close"); if (m->escapeButton != 0) { debug("closeHandler click"); g_signal_emit_by_name(G_OBJECT(m->escapeButton), "clicked", m->escapeButton); } } jigdo-0.7.3/src/gtk/messagebox.hh0000644000175000017500000001771310324272113016465 0ustar richardrichard/* $Id: messagebox.hh,v 1.4 2005/10/15 21:27:39 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//**@file Display an error box with a message and standard or user-supplied buttons. */ #ifndef GTK_MESSAGEBOX_HH #define GTK_MESSAGEBOX_HH #include #include #include #include #include #ifdef MessageBox # undef MessageBox // fsck Windows!! #endif #ifdef ERROR # undef ERROR #endif //______________________________________________________________________ /** Display an error box with a message and standard or user-supplied buttons. */ class MessageBox : NoCopy { public: class Ref; friend class Ref; enum { NONE = 0, HELP = 1 << 0, OK = 1 << 1, CLOSE = 1 << 2, CANCEL = 1 << 3 }; static const char* const MESSAGE; static const char* const INFO; static const char* const WARNING; static const char* const QUESTION; static const char* const ERROR; /** Displays message in a GtkMessageDialog - when the user clicks the OK button, the window is closed again. Any number of independent error boxes can be open at the same time. For non-standard buttons, use GTK_BUTTONS_NONE, then addButton(). heading and message must be valid UTF-8. Markup is not escaped, do this yourself if necessary. message is optional. @param type Type of icon to display @param buttons Or'ed together: HELP, OK, CLOSE, CANCEL. HELP is special in that the button is added at the left side of the dialog. OK is special because a default signal handler is added which closes the dialog. @param heading Error text to be printed in big font at top of dialog, or null if only message is to be displayed. @param message Main error message */ inline MessageBox(const char* type, int buttons, const char* heading, const char* message = 0); /** As above, but with string& instead of char* */ inline MessageBox(const char* type, int buttons, const string& heading, const char* message); /** As above, but with string& instead of char* */ inline MessageBox(const char* type, int buttons, const char* heading, const string& message); /** As above, but with string& instead of char* */ inline MessageBox(const char* type, int buttons, const string& heading); /** As above, but with string& instead of char* */ inline MessageBox(const char* type, int buttons, const string& heading, const string& message); ~MessageBox(); /** Open the dialog box. (This isn't done in the ctor because if you add your own buttons after opening it, its size changes, which may cause the window manager to display it partially off-screen.) Also registers a callback which closes the window once a button is pressed, *except* when that button causes a response GTK_RESPONSE_HELP @return "this" */ inline MessageBox* show(); /** As above, but don't close when a button is pressed. */ MessageBox* show_noAutoClose(); /** Add a text button to the dialog. @return The new button */ GtkWidget* addButton(const char* buttonText, int response); inline GtkWidget* addButton(const char* buttonText, GtkResponseType response); /** Add a standard button to the dialog. @param buttonType e.g. "gtk-cancel" @param response e.g. GTK_RESPONSE_CANCEL, or a positive value of your choice @return The new button */ GtkWidget* addStockButton(const char* buttonType, int response); inline GtkWidget* addStockButton(const char* buttonType, GtkResponseType response); /** implResponseHandler(GtkDialog* diag, int response, gpointer data) @param diag @param response Code of response. Either one of the responses you registered, or GTK_RESPONSE_DELETE_EVENT if the user clicked on the window's close icon. If the user presses the Escape key, either any GTK_RESPONSE_CANCEL (if present) is simulated as pressed, or the only button of the dialog is simulated as pressed (if there is only one), or nothing happens (i.e. >1 buttons and none of them is GTK_RESPONSE_CANCEL). @param data The pointer passed to onResponse(). */ typedef void (*ResponseHandler)(GtkDialog*, int, gpointer); /** You must take care yourself to be delivered signals when the user clicks on a button. The MessageBox object is deleted when the dialog is destroyed - usually, this happens automatically, but it won't happen for a click on a "Help" button and if you used show_noAutoClose() instead of show(). @return "this" */ inline MessageBox* onResponse(ResponseHandler handler, gpointer data); private: void init(const char* type, int buttons, const char* heading, const char* message); static void destroyHandler(GtkDialog*, MessageBox* m); static void autocloseHandler(GtkDialog*, int, MessageBox* m); static void closeHandler(GtkDialog*, MessageBox* m); GtkWidget* dialog; // The button to simulate a click on if Escape is pressed, or null GtkWidget* escapeButton; Ref* ref; string label; }; //______________________________________________________________________ /** Ref object to store reference to a MessageBox in. If the Ref object already contained a reference, that MessageBox is deleted (i.e. closed on screen). If the MessageBox is deleted, it sets its reference to null. Useful to ensure that only one MessageBox is ever open for a certain Ref instance. */ class MessageBox::Ref { friend class MessageBox; public: Ref() : messageBox(0) { } inline ~Ref(); inline void set(MessageBox* m); MessageBox* get() const { return messageBox; } private: // Don't copy inline MessageBox& operator=(const MessageBox&); MessageBox* messageBox; }; //______________________________________________________________________ MessageBox::MessageBox(const char* type, int buttons, const char* heading, const char* message) { init(type, buttons, heading, message); } MessageBox::MessageBox(const char* type, int buttons, const string& heading, const char* message) { init(type, buttons, heading.c_str(), message); } MessageBox::MessageBox(const char* type, int buttons, const char* heading, const string& message) { init(type, buttons, heading, message.c_str()); } MessageBox::MessageBox(const char* type, int buttons, const string& heading) { init(type, buttons, heading.c_str(), 0); } MessageBox::MessageBox(const char* type, int buttons, const string& heading, const string& message) { init(type, buttons, heading.c_str(), message.c_str()); } GtkWidget* MessageBox::addButton(const char* buttonText, GtkResponseType response) { return addButton(buttonText, static_cast(response)); } GtkWidget* MessageBox::addStockButton(const char* buttonType, GtkResponseType response) { return addStockButton(buttonType, static_cast(response)); } MessageBox* MessageBox::onResponse(ResponseHandler handler, gpointer data) { g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(handler), data); return this; } MessageBox* MessageBox::show() { g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(&autocloseHandler), (gpointer)this); return show_noAutoClose(); } MessageBox::Ref::~Ref() { set(0); } void MessageBox::Ref::set(MessageBox* m) { if (messageBox == m) return; if (messageBox != 0) { messageBox->ref = 0; delete messageBox; } if (m != 0) { Assert(m->ref == 0); m->ref = this; } messageBox = m; } #endif jigdo-0.7.3/src/gtk/support.cc0000644000175000017500000000720510261546437016042 0ustar richardrichard/* * Generated by Glade, but modified */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name) { GtkWidget *parent, *found_widget; for (;;) { if (GTK_IS_MENU (widget)) parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); else parent = widget->parent; #if 0 /* RA: Disabled, doesn't compile. But why not??? */ if (!parent) parent = g_object_get_data (G_OBJECT (widget), "GladeParentKey"); #endif if (parent == NULL) break; widget = parent; } found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), widget_name); if (!found_widget) g_warning ("Widget not found: %s", widget_name); return found_widget; } static GList *pixmaps_directories = NULL; /* Use this function to set the directory containing installed pixmaps. */ void add_pixmap_directory (const gchar *directory) { pixmaps_directories = g_list_prepend (pixmaps_directories, g_strdup (directory)); } /* This is an internally used function to find pixmap files. */ static gchar* find_pixmap_file (const gchar *filename) { GList *elem; /* We step through each of the pixmaps directory to find it. */ elem = pixmaps_directories; while (elem) { gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, G_DIR_SEPARATOR_S, filename); if (g_file_test (pathname, G_FILE_TEST_EXISTS)) return pathname; g_free (pathname); elem = elem->next; } return NULL; } /* This is an internally used function to create pixmaps. */ GtkWidget* create_pixmap (GtkWidget */*widget*/, const gchar *filename) { gchar *pathname = NULL; GtkWidget *pixmap; if (!filename || !filename[0]) return gtk_image_new (); pathname = find_pixmap_file (filename); if (!pathname) { g_warning (_("Couldn't find pixmap file: %s"), filename); return gtk_image_new (); } pixmap = gtk_image_new_from_file (pathname); g_free (pathname); return pixmap; } /* This is an internally used function to create pixmaps. */ GdkPixbuf* create_pixbuf (const gchar *filename) { gchar *pathname = NULL; GdkPixbuf *pixbuf; GError *error = NULL; if (!filename || !filename[0]) return NULL; pathname = find_pixmap_file (filename); if (!pathname) { g_warning (_("Couldn't find pixmap file: %s"), filename); return NULL; } pixbuf = gdk_pixbuf_new_from_file (pathname, &error); if (!pixbuf) { fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", pathname, error->message); g_error_free (error); } g_free (pathname); return pixbuf; } /* This is used to set ATK action descriptions. */ void glade_set_atk_action_description (AtkAction *action, const gchar *action_name, const gchar *description) { gint n_actions, i; n_actions = atk_action_get_n_actions (action); for (i = 0; i < n_actions; i++) { if (!strcmp (atk_action_get_name (action, i), action_name)) atk_action_set_description (action, i, description); } } jigdo-0.7.3/src/gtk/support.hh0000644000175000017500000000374307701377734016066 0ustar richardrichard/* $Id: support.hh,v 1.1.1.1 2003/07/04 22:29:48 atterer Exp $ Originally created by glade, modified by Richard Atterer. */ #ifndef GTK_SUPPORT_HH #define GTK_SUPPORT_HH #ifdef HAVE_CONFIG_H # include #endif #include /* * Standard gettext macros. */ #if ENABLE_NLS # include # undef _ # define _(String) dgettext (PACKAGE, String) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define textdomain(String) (String) # define gettext(String) (String) # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) # define _(String) (String) # define N_(String) (String) #endif /* * Public Functions. */ /* * This function returns a widget in a component created by Glade. * Call it with the toplevel widget in the component (i.e. a window/dialog), * or alternatively any widget in the component, and the name of the widget * you want returned. */ GtkWidget* lookup_widget (GtkWidget *widget, const gchar *widget_name); /* Use this function to set the directory containing installed pixmaps. */ void add_pixmap_directory (const gchar *directory); /* * Private Functions. */ /* This is used to create the pixmaps used in the interface. */ GtkWidget* create_pixmap (GtkWidget *widget, const gchar *filename); /* This is used to create the pixbufs used in the interface. */ GdkPixbuf* create_pixbuf (const gchar *filename); /* This is used to set ATK action descriptions. */ void glade_set_atk_action_description (AtkAction *action, const gchar *action_name, const gchar *description); #endif /* GTK_SUPPORT_HH */ jigdo-0.7.3/src/gtk/treeiter.cc0000644000175000017500000000214607701377725016157 0ustar richardrichard/* $Id: treeiter.cc,v 1.1.1.1 2003/07/04 22:29:41 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Algorithm for depth-first traversal of the objects in a GtkTreeModel, the parent is visited before its children. */ #include //______________________________________________________________________ bool gtk_tree_model_iter_next_depth(GtkTreeModel* model, GtkTreeIter* iter) { GtkTreeIter x; if (gtk_tree_model_iter_children(model, &x, iter) == TRUE) { // Descend to first child *iter = x; return true; } while (true) { x = *iter; // Try to move to right sibling if (gtk_tree_model_iter_next(model, iter) == TRUE) return true; /* No right sibling, so step upward to parent, then (looping back) move to its right sibling. */ if (gtk_tree_model_iter_parent(model, iter, &x) == FALSE) return false; } } jigdo-0.7.3/src/gtk/treeiter.hh0000644000175000017500000000157510226060300016144 0ustar richardrichard/* $Id: treeiter.hh,v 1.2 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Algorithm for depth-first traversal of the objects in a GtkTreeModel, the parent is visited before its children. Usage: GtkTreeIter row; gboolean ok = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store()), &row); while (ok) { // Do something with "row" ok = gtk_tree_model_iter_next_depth(GTK_TREE_MODEL(store()), &row); } */ #ifndef GTK_TREEITER_HH #define GTK_TREEITER_HH #include #include bool gtk_tree_model_iter_next_depth(GtkTreeModel* model, GtkTreeIter* iter); #endif jigdo-0.7.3/src/jigdo-file-cmd.cc0000644000175000017500000004740710264201222016302 0ustar richardrichard/* $Id: jigdo-file-cmd.cc,v 1.16 2005/07/10 11:12:18 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Implementation of the different jigdo-file commands */ #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ namespace { #if !HAVE_WORKING_FSTREAM /* ie istream and bistream are not the same */ /* Open named file or stdin if name is "-". Store pointer to stream obj in dest and return it (except when it points to an object which should not be deleted by the caller; in this case return null). */ bistream* openForInput(bistream*& dest, const string& name) throw(Cleanup) { if (name == "-") { dest = &bcin; return 0; } bifstream* fdest = new bifstream(name.c_str(), ios::binary); dest = fdest; if (!*dest /*|| !fdest->is_open()*/) { cerr << subst(_("%1: Could not open `%2' for input: %3"), binName(), name, strerror(errno)) << endl; throw Cleanup(3); } return dest; } #endif /* Open named file or stdin if name is "-". Store pointer to stream obj in dest and return it (except when it points to an object which should not be deleted by the caller; in this case return null). */ istream* openForInput(istream*& dest, const string& name) throw(Cleanup) { if (name == "-") { /* EEEEK! There's no standard way to switch mode of cin to binary. (There might be an implementation-dependent way? close()ing and re-open()ing cin may have strange effects.) */ dest = reinterpret_cast(&cin); return 0; } ifstream* fdest = new ifstream(name.c_str(), ios::binary); dest = fdest; if (!*dest || !fdest->is_open()) { cerr << subst(_("%1: Could not open `%2' for input: %3"), binName(), name, strerror(errno)) << endl; throw Cleanup(3); } return dest; } /* Ensure that an output file is not already present. Should use this for all files before openForOutput() */ int willOutputTo(const string& name, bool optForce, bool errorMessage = true) { if (optForce) return 0; struct stat fileInfo; int err = stat(name.c_str(), &fileInfo); if (err == -1 && errno == ENOENT) return 0; if (errorMessage) { cerr << subst(_("%1: Output file `%2' already exists - delete it or use " "--force"), binName(), name) << endl; } return 1; } #if !HAVE_WORKING_FSTREAM /* ie istream and bistream are not the same */ bostream* openForOutput(bostream*& dest, const string& name) throw(Cleanup) { if (name == "-") { dest = &bcout; return 0; } dest = new bofstream(name.c_str(), ios::binary|ios::trunc); if (!*dest) { cerr << subst(_("%1: Could not open `%2' for output: %3"), binName(), name, strerror(errno)) << endl; throw Cleanup(3); } return dest; } #endif ostream* openForOutput(ostream*& dest, const string& name) throw(Cleanup) { if (name == "-") { dest = reinterpret_cast(&cout); // EEEEK! return 0; } else { dest = new ofstream(name.c_str(), ios::binary|ios::trunc); if (!*dest) { cerr << subst(_("%1: Could not open `%2' for output: %3"), binName(), name, strerror(errno)) << endl; throw Cleanup(3); } return dest; } } } // local namespace //______________________________________________________________________ /* Read contents of optLabels/optUris and call addLabel() for the supplied cache object to set up the label mapping. optLabels/optUris is cleared after use. */ int JigdoFileCmd::addLabels(JigdoCache& cache) { int result = 0; string path, label, uri; // Create a map from label name to URI map uriMap; for (vector::iterator i = optUris.begin(), e = optUris.end(); i != e; ++i) { pair entry; string::size_type firstEquals = i->find('='); if (firstEquals == string::npos) { cerr << subst(_("%1: Invalid argument to --uri: `%2'"), binaryName, *i) << '\n'; result = 1; } entry.first.assign(*i, 0U, firstEquals); entry.second.assign(*i, firstEquals + 1, string::npos); // Add mapping to uriMap msg("URI mapping: `%1' => `%2'", entry.first, entry.second); uriMap.insert(entry); } optUris.clear(); // Go through list of --label arguments and add them to JigdoCache for (vector::iterator i = optLabels.begin(), e = optLabels.end(); i != e; ++i) { // Label name is everything before first '=' in argument to --label string::size_type firstEquals = i->find('='); if (firstEquals == string::npos) { cerr << subst(_("%1: Invalid argument to --label: `%2'"), binaryName, *i) << '\n'; result = 1; } label.assign(*i, 0U, firstEquals); path.assign(*i, firstEquals + 1, string::npos); map::iterator m = uriMap.find(label); // Lookup if (m == uriMap.end()) { uri = "file:"; uri += path; compat_swapFileUriChars(uri); if (uri[uri.length() - 1] != '/') uri += '/'; ConfigFile::quote(uri); } else { uri = m->second; } cache.addLabel(path, label, uri); } optLabels.clear(); return result; } /* As above, but add URIs to the beginning of the [Servers] section of a ConfigFile. rescan() is only necessary when changing section lines. */ void JigdoFileCmd::addUris(ConfigFile& config) { // Let ci point to the line before which the label mapping will be inserted ConfigFile::iterator ci = config.firstSection("Servers"); if (ci == config.end()) { // No [Servers] section yet; append it at end config.insert(ci, string("[Servers]")); config.rescan(); } else { ++ci; } for (vector::iterator i = optUris.begin(), e = optUris.end(); i != e; ++i) { // Just add it, no matter what it contains... config.insert(ci, *i); } optUris.clear(); return; } //______________________________________________________________________ int JigdoFileCmd::makeTemplate() { if (imageFile.empty() || jigdoFile.empty() || templFile.empty()) { cerr << subst(_("%1" " make-template: Not all of --image, --jigdo, --template specified.\n" "(Attempt to deduce missing names failed.)\n"), binaryName); exit_tryHelp(); } if (fileNames.empty()) { optReporter->info(_("Warning - no files specified. The template will " "contain the complete image contents!")); } // Give >1 error messages if >1 output files not present, hence no "||" if (willOutputTo(jigdoFile, optForce) + willOutputTo(templFile, optForce) > 0) throw Cleanup(3); // Open files bistream* image; auto_ptr imageDel(openForInput(image, imageFile)); auto_ptr cfDel(new ConfigFile()); ConfigFile* cf = cfDel.get(); if (!jigdoMergeFile.empty()) { // Load file to add to jigdo output istream* jigdoMerge; auto_ptr jigdoMergeDel(openForInput(jigdoMerge, jigdoMergeFile)); *jigdoMerge >> *cf; if (jigdoMerge->bad()) { string err = subst(_("%1 make-template: Could not read `%2' (%3)"), binaryName, jigdoMergeFile, strerror(errno)); optReporter->error(err); return 3; } } JigdoConfig jc(jigdoFile, cfDel.release(), *optReporter); bostream* templ; auto_ptr templDel(openForOutput(templ, templFile)); //____________________ JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter); cache.setParams(blockLength, md5BlockLength); cache.setCheckFiles(optCheckFiles); if (addLabels(cache)) return 3; while (true) { try { cache.readFilenames(fileNames); } // Recurse through directories catch (RecurseError e) { optReporter->error(e.message); continue; } break; } // Create and run MkTemplate operation auto_ptr op(new MkTemplate(&cache, image, &jc, templ, *optReporter, optZipQuality, readAmount, optAddImage, optAddServers, optBzip2)); op->setMatchExec(optMatchExec); op->setGreedyMatching(optGreedyMatching); size_t lastDirSep = imageFile.rfind(DIRSEP); if (lastDirSep == string::npos) lastDirSep = 0; else ++lastDirSep; string imageFileLeaf(imageFile, lastDirSep); lastDirSep = templFile.rfind(DIRSEP); if (lastDirSep == string::npos) lastDirSep = 0; else ++lastDirSep; string templFileLeaf(templFile, lastDirSep); if (op->run(imageFileLeaf, templFileLeaf)) return 3; // Write out jigdo file ostream* jigdoF; auto_ptr jigdoDel(openForOutput(jigdoF, jigdoFile)); *jigdoF << jc.configFile(); if (jigdoF->bad()) { string err = subst(_("%1 make-template: Could not write `%2' (%3)"), binaryName, jigdoFile, strerror(errno)); optReporter->error(err); return 3; } return 0; } //______________________________________________________________________ int JigdoFileCmd::makeImage() { if (imageFile.empty() || templFile.empty()) { cerr << subst(_( "%1 make-image: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n"), binaryName); exit_tryHelp(); } if (imageFile != "-" && willOutputTo(imageFile, optForce) > 0) return 3; JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter); cache.setParams(blockLength, md5BlockLength); while (true) { try { cache.readFilenames(fileNames); } // Recurse through directories catch (RecurseError e) { optReporter->error(e.message); continue; } break; } string imageTmpFile; if (imageFile != "-") { imageTmpFile = imageFile; imageTmpFile += EXTSEPS"tmp"; } bistream* templ; auto_ptr templDel(openForInput(templ, templFile)); try { return JigdoDesc::makeImage(&cache, imageFile, imageTmpFile, templFile, templ, optForce, *optReporter, readAmount, optMkImageCheck); } catch (Error e) { string err = binaryName; err += " make-image: "; err += e.message; optReporter->error(err); return 3; } } //______________________________________________________________________ int JigdoFileCmd::listTemplate() { if (templFile.empty()) { cerr << subst(_("%1 list-template: --template not specified.\n"), binaryName); exit_tryHelp(); } if (templFile == "-") { cerr << subst(_("%1 list-template: Sorry, cannot read from standard " "input.\n"), binaryName); exit_tryHelp(); } if (JigdoFileCmd::optHex) Base64String::hex = true; // Open file bistream* templ; auto_ptr templDel(openForInput(templ, templFile)); if (JigdoDesc::isTemplate(*templ) == false) optReporter->info( _("Warning: This does not seem to be a template file")); JigdoDescVec contents; try { JigdoDesc::seekFromEnd(*templ); *templ >> contents; contents.list(cout); if (!*templ) { string err = subst(_("%1 list-template: %2"), binaryName, strerror(errno)); optReporter->error(err); return 3; } } catch (JigdoDescError e) { string err = subst(_("%1: %2"), binaryName, e.message); optReporter->error(err); return 3; } return 0; } //______________________________________________________________________ int JigdoFileCmd::verifyImage() { if (imageFile.empty() || templFile.empty()) { cerr << subst(_( "%1 verify: Not both --image and --template specified.\n" "(Attempt to deduce missing names failed.)\n"), binaryName); exit_tryHelp(); } bistream* image; auto_ptr imageDel(openForInput(image, imageFile)); JigdoDescVec contents; JigdoDesc::ImageInfo* info; try { bistream* templ; auto_ptr templDel(openForInput(templ, templFile)); if (JigdoDesc::isTemplate(*templ) == false) optReporter->info( _("Warning: This does not seem to be a template file")); JigdoDesc::seekFromEnd(*templ); *templ >> contents; if (!*templ) { string err = subst(_("%1 verify: %2"), binaryName, strerror(errno)); optReporter->error(err); return 3; } info = dynamic_cast(contents.back()); if (info == 0) { string err = subst(_("%1 verify: Invalid template data - " "corrupted file?"), binaryName); optReporter->error(err); return 3; } } catch (JigdoDescError e) { string err = subst(_("%1: %2"), binaryName, e.message); optReporter->error(err); return 3; } MD5Sum md; // MD5Sum of image md.updateFromStream(*image, info->size(), readAmount, *optReporter); md.finish(); if (*image) { image->get(); if (image->eof() && md == info->md5()) { optReporter->info(_("OK: Checksums match, image is good!")); return 0; } } optReporter->error(_( "ERROR: Checksums do not match, image might be corrupted!")); return 2; } //______________________________________________________________________ /* Look up a query (e.g. "MyServer:foo/path/bar") in the JigdoConfig mapping. Returns true if something was found, and prints out all resulting URIs. */ bool JigdoFileCmd::printMissing_lookup(JigdoConfig& jc, const string& query, bool printAll) { JigdoConfig::Lookup l(jc, query); string uri; if (!l.next(uri)) return false; do { // Omit "file:" when printing if (uri[0] == 'f' && uri[1] == 'i' && uri[2] == 'l' && uri[3] == 'e' && uri[4] == ':') { string nativeFilename(uri, 5); compat_swapFileUriChars(nativeFilename); cout << nativeFilename << endl; } else { cout << uri << '\n'; } } while (printAll && l.next(uri)); return true; } //______________________________ int JigdoFileCmd::printMissing(Command command) { if (imageFile.empty() || jigdoFile.empty() || templFile.empty()) { cerr << subst(_( "%1 make-template: Not all of --image, --jigdo, --template specified.\n" "(Attempt to deduce missing names failed.)\n"), binaryName); exit_tryHelp(); } bistream* templ; auto_ptr templDel(openForInput(templ, templFile)); string imageTmpFile; if (imageFile != "-") { imageTmpFile = imageFile; imageTmpFile += EXTSEPS"tmp"; // If image file exists, assume that it is complete; print nothing struct stat fileInfo; int err = stat(imageFile.c_str(), &fileInfo); if (err == 0) return 0; } // Read .jigdo file istream* jigdo; auto_ptr jigdoDel(openForInput(jigdo, jigdoFile)); auto_ptr cfDel(new ConfigFile()); ConfigFile* cf = cfDel.get(); *jigdo >> *cf; JigdoConfig jc(jigdoFile, cfDel.release(), *optReporter); // Add any mappings specified on command line if (!optUris.empty()) { addUris(jc.configFile()); jc.rescan(); } set sums; try { JigdoDesc::listMissing(sums, imageTmpFile, templFile, templ, *optReporter); } catch (Error e) { string err = subst(_("%1 print-missing: %2"), binaryName, e.message); optReporter->error(err); return 3; } //____________________ string partsSection = "Parts"; switch (command) { case PRINT_MISSING: { // To list just the first URI for (set::iterator i = sums.begin(), e = sums.end(); i != e; ++i) { Base64String m; m.write(i->sum, 16).flush(); string& s(m.result()); vector words; size_t off; bool found = false; for (ConfigFile::Find f(cf, partsSection, s, &off); !f.finished(); off = f.next()) { // f.section() points to "[section]" line, or end() if 0th section // f.label() points to "label=..." line, or end() if f.finished() // off is offset of part after "label=", or 0 words.clear(); ConfigFile::split(words, *f.label(), off); // Ignore everything but the first word if (printMissing_lookup(jc, words[0], false)) { found = true; break;} } if (!found) { /* No mapping found in [Parts] (this shouldn't happen) - create fake "MD5sum:" label line */ s.insert(0, "MD5Sum:"); printMissing_lookup(jc, s, false); } } break; } case PRINT_MISSING_ALL: { // To list all URIs for each missing file, separated by empty lines: for (set::iterator i = sums.begin(), e = sums.end(); i != e; ++i){ Base64String m; m.write(i->sum, 16).flush(); string& s(m.result()); vector words; size_t off; for (ConfigFile::Find f(cf, partsSection, s, &off); !f.finished(); off = f.next()) { // f.section() points to "[section]" line, or end() if 0th section // f.label() points to "label=..." line, or end() if f.finished() // off is offset of part after "label=", or 0 words.clear(); ConfigFile::split(words, *f.label(), off); // Ignore everything but the first word printMissing_lookup(jc, words[0], true); } // Last resort: "MD5sum:" label line s.insert(0, "MD5Sum:"); printMissing_lookup(jc, s, true); cout << endl; } break; } default: Paranoid(false); } // end switch() return 0; } //______________________________________________________________________ // Enter all file arguments into the cache int JigdoFileCmd::scanFiles() { if (cacheFile.empty()) { cerr << subst(_("%1 scan: Please specify a --cache file.\n"), binaryName); exit_tryHelp(); } JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter); cache.setParams(blockLength, md5BlockLength); if (addLabels(cache)) return 3; while (true) { try { cache.readFilenames(fileNames); } // Recurse through directories catch (RecurseError e) { optReporter->error(e.message); continue; } break; } JigdoCache::iterator ci = cache.begin(), ce = cache.end(); if (optScanWholeFile) { // Cause entire file to be read while (ci != ce) { ci->getMD5Sum(&cache); ++ci; } } else { // Only cause first md5 block to be read; not scanning the whole file while (ci != ce) { ci->getSums(&cache, 0); ++ci; } } return 0; // Cache data is written out when the JigdoCache is destroyed } //______________________________________________________________________ /* Print MD5 checksums of arguments like md5sum(1), but using our Base64-like encoding for the checksum, not hexadecimal like md5sum(1). Additionally, try to make use of the cache, and only print out the part of any filename following any "//". This is actually very similar to scanFiles() above. */ int JigdoFileCmd::md5sumFiles() { JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter); cache.setParams(blockLength, md5BlockLength); cache.setCheckFiles(optCheckFiles); while (true) { try { cache.readFilenames(fileNames); } // Recurse through directories catch (RecurseError e) { optReporter->error(e.message); continue; } break; } if (JigdoFileCmd::optHex) Base64String::hex = true; JigdoCache::iterator ci = cache.begin(), ce = cache.end(); while (ci != ce) { Base64String m; // Causes whole file to be read const MD5Sum* md = ci->getMD5Sum(&cache); if (md != 0) { m.write(md->digest(), 16).flush(); string& s(m.result()); s += " "; if (ci->getPath() == "/") s += '/'; s += ci->leafName(); // Output checksum line optReporter->coutInfo(s); } ++ci; } return 0; // Cache data is written out when the JigdoCache is destroyed } jigdo-0.7.3/src/jigdo-file-cmd.hh0000644000175000017500000001175310262476154016327 0ustar richardrichard/* $Id: jigdo-file-cmd.hh,v 1.13 2005/07/05 12:26:20 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Implementation of the different jigdo-file commands. To be used only by main() in jigdo-file.cc */ #ifndef JIGDO_FILE_CMD_HH #define JIGDO_FILE_CMD_HH #include #include #include #include #include #include #include #include //______________________________________________________________________ /** class for "pointer to any *Reporter class", with disambiguation members */ struct AnyReporter : public MkTemplate::ProgressReporter, public JigdoCache::ProgressReporter, public JigdoDesc::ProgressReporter, public MD5Sum::ProgressReporter, public JigdoConfig::ProgressReporter { virtual void error(const string& message) { MD5Sum::ProgressReporter::error(message); } virtual void info(const string& message) { MD5Sum::ProgressReporter::info(message); } virtual void coutInfo(const string& message) { cout << message << endl; } }; //______________________________________________________________________ /** Class providing functionality only to jigdo-file.cc */ class JigdoFileCmd { friend int main(int argc, char* argv[]); //________________________________________ enum Command { MAKE_TEMPLATE, MAKE_IMAGE, PRINT_MISSING, PRINT_MISSING_ALL, SCAN, VERIFY, LIST_TEMPLATE, MD5SUM }; //________________________________________ // Command line options, to be used by the jigdo-file commands # if WINDOWS static const char* const binaryName; # else friend const string& binName(); static string binaryName; // of the program # endif // Names of files given on command line, and of --files-from files static RecurseDir fileNames; static string imageFile; static string jigdoFile; static string templFile; static string jigdoMergeFile; static string cacheFile; static size_t optCacheExpiry; // Expiry time for cache in seconds static vector optLabels; // Strings of the form "Label=/some/path" static vector optUris; // "Label=http://some.server/" static size_t blockLength; // of rsync algorithm, is also minimum file size static size_t md5BlockLength; static size_t readAmount; static int optZipQuality; static bool optBzip2; static bool optForce; // true => Silently delete existent output static bool optMkImageCheck; // true => check MD5sums static bool optCheckFiles; // true => check if files exist static bool optScanWholeFile; // false => read only first block // true => skip smaller matches if a larger match could be possible static bool optGreedyMatching; static bool optAddImage; // true => Add [Image] section to output .jigdo static bool optAddServers; // true => Add [Servers] to output .jigdo static bool optHex; // true => Use hex not base64 output for md5/ls cmds static string optDebug; // list of debug msg to turn on, or all/help // Reporter is defined in config.h and is the base of all other *Reporter's static AnyReporter* optReporter; static string optMatchExec; //________________________________________ /** Defined in jigdo-file.cc - reads command line options and sets the static vars above, returns command requested by user. Will throw Cleanup() for things like --help, --version or invalid cmd line args. */ static Command cmdOptions(int argc, char* argv[]); //________________________________________ /** @name Functions corresponding to the jigdo-file commands, defined in jigdo-file-cmd.cc */ //@{ static int makeTemplate(); static int makeImage(); static int printMissing(Command command = PRINT_MISSING); static int scanFiles(); static int verifyImage(); static int listTemplate(); static int md5sumFiles(); //@} /** @name Helper functions for the above functions, only to be used in jigdo-file-cmd.cc */ //@{ static int addLabels(JigdoCache& cache); static void addUris(ConfigFile& config); static bool printMissing_lookup(JigdoConfig& jc, const string& query, bool printAll); //@} }; //______________________________________________________________________ /** Convenience function: Return name of executable, for printing in error messages etc. */ #if WINDOWS inline const char* binName() { return "jigdo-file"; } #else inline const string& binName() { return JigdoFileCmd::binaryName; } #endif /** Prints "jigdo-file: Try `jigdo-file --help' or `man jigdo-file' for more information", then throws Cleanup(3). */ extern void exit_tryHelp(); //______________________________________________________________________ #endif jigdo-0.7.3/src/jigdo-file.cc0000644000175000017500000007362710431672643015563 0ustar richardrichard/* $Id: jigdo-file.cc,v 1.20 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Command line utility to create .jigdo and .template files */ #include #include #include #include #if ENABLE_NLS # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ RecurseDir JigdoFileCmd::fileNames; string JigdoFileCmd::imageFile; string JigdoFileCmd::jigdoFile; string JigdoFileCmd::templFile; string JigdoFileCmd::jigdoMergeFile; string JigdoFileCmd::cacheFile; size_t JigdoFileCmd::optCacheExpiry = 60*60*24*30; // default: 30 days vector JigdoFileCmd::optLabels; vector JigdoFileCmd::optUris; size_t JigdoFileCmd::blockLength = 1*1024U; size_t JigdoFileCmd::md5BlockLength = 128*1024U - 55; size_t JigdoFileCmd::readAmount = 128*1024U; int JigdoFileCmd::optZipQuality = Z_BEST_COMPRESSION; bool JigdoFileCmd::optBzip2 = false; bool JigdoFileCmd::optForce = false; bool JigdoFileCmd::optMkImageCheck = true; bool JigdoFileCmd::optCheckFiles = true; bool JigdoFileCmd::optScanWholeFile = false; bool JigdoFileCmd::optGreedyMatching = true; bool JigdoFileCmd::optAddImage = true; bool JigdoFileCmd::optAddServers = true; bool JigdoFileCmd::optHex = false; string JigdoFileCmd::optDebug; AnyReporter* JigdoFileCmd::optReporter = 0; string JigdoFileCmd::optMatchExec; #if WINDOWS const char* const JigdoFileCmd::binaryName = "jigdo-file"; #else string JigdoFileCmd::binaryName; #endif //______________________________________________________________________ namespace { // Absolute minimum for --min-length (i.e. blockLength), in bytes const size_t MINIMUM_BLOCKLENGTH = 256; char optHelp = '\0'; bool optVersion = false; // Return value of main(), for "delayed error exit" int returnValue = 0; // Size of image file (zero if stdin), for percentage progress reports static uint64 imageSize; //______________________________________________________________________ /// Progress report class that writes informational messages to cerr class MyProgressReporter : public AnyReporter { public: MyProgressReporter(bool prog) : printProgress(prog) { } // Length of "100% 9999k/9999k " part before "scanning ..." etc message static const unsigned PROGRESS_WIDTH = 23; virtual void error(const string& message) { print(message); } virtual void info(const string& message) { print(message); } virtual void coutInfo(const string& message); virtual void scanningFile(const FilePart* file, uint64 offInFile) { if (!printProgress) return; string m; append(m, 100 * offInFile / file->size(), 3); // 3 m += '%'; // 1 char append(m, offInFile / 1024, 8); // >= 8 chars m += "k/"; // 2 chars append(m, file->size() / 1024); // want >= 8 chars m += 'k'; // 1 char if (m.size() < 3+1+8+2+8+1) m += " " + 10 - (3+1+8+2+8+1 - m.size()); Paranoid(m.length() == PROGRESS_WIDTH); m += _("scanning"); m += " `"; m += file->leafName(); m += '\''; print(m, false); } virtual void scanningImage(uint64 offset) { if (!printProgress) return; string m; if (imageSize != 0) { append(m, 100 * (totalAll + offset) / (totalAll + imageSize), 3); // 3 m += '%'; // 1 char } else { m += " "; } append(m, offset / 1024, 8); // >= 8 chars m += 'k'; // 1 char if (imageSize != 0) { m += '/'; // 1 char append(m, imageSize / 1024); // want >= 8 chars m += 'k'; // 1 char } if (m.size() < 3+1+8+1+1+8+1) m += " " + 10 - (3+1+8+1+1+8+1 - m.size()); Paranoid(m.length() == PROGRESS_WIDTH); m += _("scanning image"); print(m, false); } virtual void readingMD5(uint64 offInStream, uint64 size) { if (!printProgress) return; string m; append(m, 100 * offInStream / size, 3); // 3 m += '%'; // 1 char append(m, offInStream / 1024, 8); // >= 8 chars m += "k/"; // 2 chars append(m, size / 1024); // want >= 8 chars m += 'k'; // 1 char if (m.size() < 3+1+8+2+8+1) m += " " + 10 - (3+1+8+2+8+1 - m.size()); Paranoid(m.length() == PROGRESS_WIDTH); m += _("verifying image"); print(m, false); } virtual void writingImage(uint64 written, uint64 totalToWrite, uint64 imgOff, uint64 imgSize) { if (!printProgress) return; string m; append(m, 100 * written / totalToWrite, 3); // 3 m += '%'; // 1 char append(m, imgOff / 1024, 8); // >= 8 chars m += "k/"; // 2 chars append(m, imgSize / 1024); // want >= 8 chars m += 'k'; // 1 char if (m.size() < 3+1+8+2+8+1) m += " " + 10 - (3+1+8+2+8+1 - m.size()); Paranoid(m.length() == PROGRESS_WIDTH); m += _("writing image"); print(m, false); } virtual void abortingScan() { string m(_("Error scanning image - abort")); print(m); } virtual void matchFound(const FilePart* file, uint64 offInImage) { string m = subst(_("Match of `%1' at offset %2"), file->leafName(), offInImage); print(m); } virtual void finished(uint64 imageSize) { if (!printProgress) return; string m = subst(_("Finished - image size is %1 bytes."), imageSize); print(m); } private: static void print(string s, bool addNewline = true); bool printProgress; static string prevLine; static uint64 totalAll; }; //________________________________________ string MyProgressReporter::prevLine; uint64 MyProgressReporter::totalAll = 0; /* Print the string to stderr. Repeatedly overwrite the same line with different progress reports if requested with addNewline==false. If the tail of the message is the same as a previous message printed on the same line, do not print the tail. */ void MyProgressReporter::print(string s, bool addNewline) { size_t screenWidth = static_cast(ttyWidth()) - 1; // For progress messages, truncate if too long if (!addNewline && screenWidth != 0 && s.length() > screenWidth) { string::size_type tick = s.find("`", PROGRESS_WIDTH); if (tick != string::npos) s.replace(tick + 1, s.length() - screenWidth + 3, "...", 3); if (s.length() > screenWidth) { s.resize(screenWidth); s[screenWidth - 1] = '$'; } } if (s.size() != prevLine.size()) { // Print new message, maybe pad with spaces to overwrite rest of old one cerr << s; if (s.size() < prevLine.size()) { size_t nrSpaces = prevLine.size() - s.size(); while (nrSpaces >= 10) { cerr << " "; nrSpaces -= 10; } if (nrSpaces > 0) cerr << (" " + 9 - nrSpaces); } } else { /* Same length as previous - to reduce cursor flicker, only redraw as much as necessary */ int i = s.size() - 1; while (i >= 0 && s[i] == prevLine[i]) --i; for (int j = 0; j <= i; ++j) cerr << s[j]; } // Should the message just printed be overwritten on next call? if (addNewline) { // No, leave it visible cerr << endl; prevLine = ""; } else { // Yes, overwrite with next message cerr << '\r' << flush; prevLine = s; } } //________________________________________ /* If stdout is redirected to a file, the file should just contain the progress reports, none of the padding space chars. */ void MyProgressReporter::coutInfo(const string& message) { cout << message << flush; // Need to flush because we mix cout and cerr if (message.size() < prevLine.size()) { size_t nrSpaces = prevLine.size() - message.size(); while (nrSpaces >= 10) { cerr << " "; nrSpaces -= 10; } if (nrSpaces > 0) cerr << (" " + 9 - nrSpaces); } cout << endl; cerr << '\r' << flush; /* Ensure good-looking progress reports even if cout goes to a file and cerr to the terminal. */ if (message.size() > prevLine.size()) prevLine.resize(message.size(), '\0'); else prevLine[prevLine.size() - 1] = '\0'; } //________________________________________ /// Progress report class that writes informational messages to cerr class MyGrepProgressReporter : public AnyReporter { public: // Default error()/info() will print to cerr virtual void matchFound(const FilePart* file, uint64 offInImage) { cout << offInImage << ' ' << file->getPath() << file->leafName() << endl; } }; //________________________________________ /// Progress report class that writes informational messages to cerr class MyQuietProgressReporter : public AnyReporter { virtual void info(const string&) { } }; //________________________________________ MyProgressReporter reporterDefault(true); MyProgressReporter reporterNoprogress(false); MyGrepProgressReporter reporterGrep; MyQuietProgressReporter reporterQuiet; //______________________________________________________________________ inline void printUsage(bool detailed, size_t blockLength, size_t md5BlockLength, size_t readAmount) { if (detailed) { cout << subst(_( "\n" "Copyright (C) 2001-%1 Richard Atterer \n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License, version 2. See\n" "the file COPYING or for details.\n" "\n"), CURRENT_YEAR); } cout << subst(_( "\n" "Usage: %1 COMMAND [OPTIONS] [FILES...]\n" "Commands:\n" " make-template mt Create template and jigdo from image and files\n" " make-image mi Recreate image from template and files (can merge\n" " files in >1 steps, uses `IMG%2tmp' for --image=IMG)\n" " print-missing pm After make-image, print files still missing for\n" " the image to be completely recreated\n"), binName(), EXTSEPS); if (detailed) cout << _( " print-missing-all pma\n" " Print all URIs for each missing file\n" " scan sc Update cache with information about supplied files\n"); cout << _( " verify ver Check whether image matches checksum from template\n" " md5sum md5 Print MD5 checksums similar to md5sum(1)\n"); if (detailed) { cout << _( " list-template ls Print low-level listing of contents of template\n" " data or tmp file\n"); } cout << subst(_( "\n" "Important options:\n" " -i --image=FILE Output/input filename for image file\n" " -j --jigdo=FILE Input/output filename for jigdo file\n" " -t --template=FILE\n" " Input/output filename for template file\n" " -T --files-from=FILE\n" " Read further filenames from FILE (`-' for stdin)\n" " -r --report=default|noprogress|quiet|grep\n" " Control format of status reports to stderr (or\n" " stdout in case of `grep')\n" " -f --force Silently delete existent output files\n" " --label Label=%1%2path\n" " [make-template] Replace name of input file\n" " `%1%2path%3a%2file%4txt' (note the `%3') with\n" " `Label:a/file%4txt' in output jigdo\n" " --uri Label=http://www.site.com\n" " [make-template] Add mapping from Label to given\n" " URI instead of default `file:' URI\n" " [print-missing] Override mapping in input jigdo\n" " -0 to -9 Set amount of compression in output template\n" " --bzip2 Use bzip2 compression instead of default --gzip\n" " --cache=FILE Store/reload information about any files scanned\n"), (WINDOWS ? "C:" : ""), DIRSEPS, SPLITSEP, EXTSEPS); if (detailed) { cout << _( " --no-cache Do not cache information about scanned files\n" " --cache-expiry=SECONDS[h|d|w|m|y]\n" " Remove cache entries if last access was longer\n" " ago than given amount of time [default 30 days]\n" " -h --help Output short help\n" " -H --help-all Output this help\n"); } else { cout << _(" -h --help Output this help\n" " -H --help-all Output more detailed help\n"); } cout << _(" -v --version Output version info") << endl; if (detailed) { cout << subst(_( "\n" "Further options: (can append 'k', 'M', 'G' to any BYTES argument)\n" " --merge=FILE [make-template] Add FILE contents to output jigdo\n" " --no-force Do not delete existent output files [default]\n" " --min-length=BYTES [default %1]\n" " [make-template] Minimum length of files to search\n" " for in image data\n" " --md5-block-size=BYTES [default %2]\n" " Uninteresting internal parameter -\n" " jigdo-file enforces: min-length < md5-block-size\n" " --readbuffer=BYTES [default %3k]\n" " Amount of data to read at a time\n" " --check-files [default]\n" " [make-template,md5sum] Check if files exist and\n" " get or verify checksums, date and size\n" " [make-image] Verify checksum of files written to\n" " image\n" " --no-check-files [make-template,md5sum] when used with --cache,\n" " [make-image] Do not verify checksums of files\n" " --scan-whole-file [scan] Scan whole file instead of only first block\n" " --no-scan-whole-file [scan] Scan only first block [default]\n" " --greedy-matching [make-template] Prefer immediate matches of small\n" " files now over possible (but uncertain) matches of \n" " larger files later [default]\n" " --no-greedy-matching\n" " [make-template] Skip a smaller match and prefer a\n" " pending larger one, with the risk of missing both\n" " --image-section [default]\n" " --no-image-section\n" " --servers-section [default]\n" " --no-servers-section\n" " [make-template] When creating the jigdo file, do\n" " or do not add the sections `[Image]' or `[Servers]'\n" " --debug[=all|=UNIT1,UNIT2...|=help]\n" " Print debugging information for all units, or for\n" " specified units, or print list of units.\n" " Can use `~', e.g. `all,~libwww'\n" " --no-debug No debugging info [default]\n" " --match-exec=CMD [make-template] Execute command when files match\n" " CMD is passed to a shell, with environment set up:\n" " LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE\n" " e.g. 'mkdir -p \"${LABEL:-.}/$MATCHPATH\" && ln -f \"$FILE\" \"${LABEL:-.}/$MATCHPATH$LEAF\"'\n" " --no-hex [default]\n" " --hex [md5sum, list-template] Output checksums in\n" " hexadecimal, not Base64\n" " --gzip [default] Use gzip compression, not --bzip2\n"), blockLength, md5BlockLength, readAmount / 1024) << endl; } return; } //______________________________________________________________________ /* Like atoi(), but tolerate exactly one suffix 'k', 'K', 'm', 'M', 'g' or 'G' for kilo, mega, giga */ size_t scanMemSize(const char* str) { const char* s = str; size_t x = 0; while (*s >= '0' && *s <= '9') { x = 10 * x + *s - '0'; ++s; } switch (*s) { // Fallthrough mania! case 'g': case 'G': x = x * 1024; case 'm': case 'M': x = x * 1024; case 'k': case 'K': x = x * 1024; if (*++s == '\0') return x; default: cerr << subst(_("%1: Invalid size specifier `%2'"), binName(), str) << endl; throw Cleanup(3); case '\0': return x; } } /* Like atoi(), but tolerate exactly one suffix 'h', 'd', 'w', 'm', 'y' for hours, days, weeks, months, years. Returns seconds. Special value "off" results in 0 to be returned. */ size_t scanTimespan(const char* str) { if (strcmp(str, "off") == 0) return 0; const char* s = str; size_t x = 0; while (*s >= '0' && *s <= '9') { x = 10 * x + *s - '0'; ++s; } switch (*s) { case 'h': case 'H': x = x * 60 * 60; ++s; break; case 'd': case 'D': x = x * 60 * 60 * 24; ++s; break; case 'w': case 'W': x = x * 60 * 60 * 24 * 7; ++s; break; case 'm': case 'M': x = x * 60 * 60 * 24 * 30; ++s; break; case 'y': case 'Y': x = x * 60 * 60 * 24 * 365; ++s; break; } if (*s == '\0') return x; cerr << subst(_("%1: Invalid time specifier `%2'"), binName(), str) << endl; throw Cleanup(3); } //______________________________________________________________________ /* Try creating a filename in dest by stripping any file extension from source and appending ext. Only deduceName() should call deduceName2(). */ void deduceName2(string& dest, const char* ext, const string& src) { Paranoid(dest.empty()); string::size_type lastDot = src.rfind(EXTSEP); if (lastDot != string::npos) { if (src.find(DIRSEP, lastDot + 1) != string::npos) lastDot = string::npos; } dest.assign(src, 0U, lastDot); dest += ext; if (dest == src) dest = ""; } inline void deduceName(string& dest, const char* ext, const string& src) { if (!dest.empty()) return; deduceName2(dest, ext, src); } //______________________________________________________________________ void outOfMemory() { cerr << subst(_("%1: Out of memory - aborted."), binName()) << endl; exit(3); } //______________________________________________________________________ } // local namespace enum { LONGOPT_BUFSIZE = 0x100, LONGOPT_NOFORCE, LONGOPT_MINSIZE, LONGOPT_MD5SIZE, LONGOPT_MKIMAGECHECK, LONGOPT_NOMKIMAGECHECK, LONGOPT_LABEL, LONGOPT_URI, LONGOPT_ADDSERVERS, LONGOPT_NOADDSERVERS, LONGOPT_ADDIMAGE, LONGOPT_NOADDIMAGE, LONGOPT_NOCACHE, LONGOPT_CACHEEXPIRY, LONGOPT_MERGE, LONGOPT_HEX, LONGOPT_NOHEX, LONGOPT_DEBUG, LONGOPT_NODEBUG, LONGOPT_MATCHEXEC, LONGOPT_BZIP2, LONGOPT_GZIP, LONGOPT_SCANWHOLEFILE, LONGOPT_NOSCANWHOLEFILE, LONGOPT_GREEDYMATCHING, LONGOPT_NOGREEDYMATCHING }; // Deal with command line switches JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) { # if !WINDOWS binaryName = argv[0]; # endif bool error = false; optReporter = &reporterDefault; while (true) { static const struct option longopts[] = { { "bzip2", no_argument, 0, LONGOPT_BZIP2 }, { "cache", required_argument, 0, 'c' }, { "cache-expiry", required_argument, 0, LONGOPT_CACHEEXPIRY }, { "check-files", no_argument, 0, LONGOPT_MKIMAGECHECK }, { "debug", optional_argument, 0, LONGOPT_DEBUG }, { "files-from", required_argument, 0, 'T' }, // "-T" like tar's { "force", no_argument, 0, 'f' }, { "greedy-matching", no_argument, 0, LONGOPT_GREEDYMATCHING }, { "gzip", no_argument, 0, LONGOPT_GZIP }, { "help", no_argument, 0, 'h' }, { "help-all", no_argument, 0, 'H' }, { "hex", no_argument, 0, LONGOPT_HEX }, { "image", required_argument, 0, 'i' }, { "image-section", no_argument, 0, LONGOPT_ADDIMAGE }, { "jigdo", required_argument, 0, 'j' }, { "label", required_argument, 0, LONGOPT_LABEL }, { "match-exec", required_argument, 0, LONGOPT_MATCHEXEC }, { "md5-block-size", required_argument, 0, LONGOPT_MD5SIZE }, { "merge", required_argument, 0, LONGOPT_MERGE }, { "min-length", required_argument, 0, LONGOPT_MINSIZE }, { "no-cache", no_argument, 0, LONGOPT_NOCACHE }, { "no-check-files", no_argument, 0, LONGOPT_NOMKIMAGECHECK }, { "no-debug", no_argument, 0, LONGOPT_NODEBUG }, { "no-force", no_argument, 0, LONGOPT_NOFORCE }, { "no-greedy-matching", no_argument, 0, LONGOPT_NOGREEDYMATCHING }, { "no-hex", no_argument, 0, LONGOPT_NOHEX }, { "no-image-section", no_argument, 0, LONGOPT_NOADDIMAGE }, { "no-scan-whole-file", no_argument, 0, LONGOPT_NOSCANWHOLEFILE }, { "no-servers-section", no_argument, 0, LONGOPT_NOADDSERVERS }, { "readbuffer", required_argument, 0, LONGOPT_BUFSIZE }, { "report", required_argument, 0, 'r' }, { "scan-whole-file", no_argument, 0, LONGOPT_SCANWHOLEFILE }, { "servers-section", no_argument, 0, LONGOPT_ADDSERVERS }, { "template", required_argument, 0, 't' }, { "uri", required_argument, 0, LONGOPT_URI }, { "version", no_argument, 0, 'v' }, { 0, 0, 0, 0 } }; int c = getopt_long(argc, argv, "0123456789hHvT:i:j:t:c:fr:", longopts, 0); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': optZipQuality = c - '0'; break; case LONGOPT_BZIP2: optBzip2 = true; break; case LONGOPT_GZIP: optBzip2 = false; break; case 'h': case 'H': optHelp = c; break; case 'v': optVersion = true; break; case 'T': fileNames.addFilesFrom( strcmp(optarg, "-") == 0 ? "" : optarg); break; case 'i': imageFile = optarg; break; case 'j': jigdoFile = optarg; break; case 't': templFile = optarg; break; case LONGOPT_MERGE: jigdoMergeFile = optarg; break; case 'c': cacheFile = optarg; break; case LONGOPT_NOCACHE: cacheFile.erase(); break; case LONGOPT_CACHEEXPIRY: optCacheExpiry = scanTimespan(optarg); break; case 'f': optForce = true; break; case LONGOPT_NOFORCE: optForce = false; break; case LONGOPT_MINSIZE: blockLength = scanMemSize(optarg); break; case LONGOPT_MD5SIZE: md5BlockLength = scanMemSize(optarg); break; case LONGOPT_BUFSIZE: readAmount = scanMemSize(optarg); break; case 'r': if (strcmp(optarg, "default") == 0) { optReporter = &reporterDefault; } else if (strcmp(optarg, "noprogress") == 0) { optReporter = &reporterNoprogress; } else if (strcmp(optarg, "quiet") == 0) { optReporter = &reporterQuiet; } else if (strcmp(optarg, "grep") == 0) { optReporter = &reporterGrep; } else { cerr << subst(_("%1: Invalid argument to --report (allowed: " "default noprogress quiet grep)"), binName()) << '\n'; error = true; } break; case LONGOPT_MKIMAGECHECK: optMkImageCheck = true; optCheckFiles = true; break; case LONGOPT_NOMKIMAGECHECK: optMkImageCheck = false; optCheckFiles = false; break; case LONGOPT_GREEDYMATCHING: optGreedyMatching = true; break; case LONGOPT_NOGREEDYMATCHING: optGreedyMatching = false; break; case LONGOPT_SCANWHOLEFILE: optScanWholeFile = true; break; case LONGOPT_NOSCANWHOLEFILE: optScanWholeFile = false; break; case LONGOPT_ADDSERVERS: optAddServers = true; break; case LONGOPT_NOADDSERVERS: optAddServers = false; break; case LONGOPT_ADDIMAGE: optAddImage = true; break; case LONGOPT_NOADDIMAGE: optAddImage = false; break; case LONGOPT_LABEL: optLabels.push_back(string(optarg)); break; case LONGOPT_URI: optUris.push_back(string(optarg)); break; case LONGOPT_HEX: optHex = true; break; case LONGOPT_NOHEX: optHex = false; break; case LONGOPT_DEBUG: if (optarg) optDebug = optarg; else optDebug = "all"; break; case LONGOPT_NODEBUG: optDebug.erase(); break; case LONGOPT_MATCHEXEC: optMatchExec = optarg; break; case '?': error = true; case ':': break; default: msg("getopt returned %1", static_cast(c)); break; } } if (error) exit_tryHelp(); if (optHelp != '\0' || optVersion) { if (optVersion) cout << "jigdo-file version " JIGDO_VERSION << endl; if (optHelp != '\0') printUsage(optHelp == 'H', blockLength, md5BlockLength, readAmount); throw Cleanup(0); } # if WINDOWS Logger::scanOptions(optDebug, binName()); # else Logger::scanOptions(optDebug, binName().c_str()); # endif //______________________________ // Silently correct invalid blockLength/md5BlockLength args if (blockLength < MINIMUM_BLOCKLENGTH) blockLength = MINIMUM_BLOCKLENGTH; if (blockLength >= md5BlockLength) md5BlockLength = blockLength + 1; // Round to next k*64+55 for efficient MD5 calculation md5BlockLength = ((md5BlockLength + 63 - 55) & ~63U) + 55; Paranoid(blockLength >= MINIMUM_BLOCKLENGTH && blockLength < md5BlockLength); //______________________________ // Complain if name of command isn't there if (optind >= argc) { cerr << subst(_("%1: Please specify a command"), binName()) << '\n'; exit_tryHelp(); } // Find Command code corresponding to command string on command line :) Command result; { const char* command = argv[optind++]; struct CodesEntry { char* name; Command code; }; const CodesEntry codes[] = { { "make-template", MAKE_TEMPLATE }, { "mt", MAKE_TEMPLATE }, { "make-image", MAKE_IMAGE }, { "mi", MAKE_IMAGE }, { "print-missing", PRINT_MISSING }, { "pm", PRINT_MISSING }, { "print-missing-all", PRINT_MISSING_ALL }, { "pma", PRINT_MISSING_ALL }, { "verify", VERIFY }, { "ver", VERIFY }, { "scan", SCAN }, { "sc", SCAN }, { "list-template", LIST_TEMPLATE }, { "ls", LIST_TEMPLATE }, { "md5sum", MD5SUM }, { "md5", MD5SUM } }; const CodesEntry *c = codes; const CodesEntry *end = codes+sizeof(codes)/sizeof(CodesEntry); while (true) { if (strcmp(command, c->name) == 0) { result = c->code; break; } ++c; if (c == end) { string allCommands; for (const CodesEntry *c = codes, *end = codes+sizeof(codes)/sizeof(CodesEntry); c != end; ++c) { allCommands += ' '; allCommands += c->name; } cerr << subst(_("%1: Invalid command `%2'\n(Must be one of:%3)"), binName(), command, allCommands) << '\n'; exit_tryHelp(); } } } //____________________ while (optind < argc) fileNames.addFile(argv[optind++]); # if 0 /* If no --files-from given and no files on command line, assume we are to read any list of filenames from stdin. */ if (fileNames.empty()) fileNames.addFilesFrom(""); # endif //____________________ // If --image, --jigdo or --template not given, create name from other args if (!imageFile.empty() && imageFile != "-") { deduceName(jigdoFile, EXTSEPS"jigdo", imageFile); deduceName(templFile, EXTSEPS"template", imageFile); } else if (!jigdoFile.empty() && jigdoFile != "-") { deduceName(imageFile, "", jigdoFile); deduceName(templFile, EXTSEPS"template", jigdoFile); } else if (!templFile.empty() && templFile != "-") { deduceName(imageFile, "", templFile); deduceName(jigdoFile, EXTSEPS"jigdo", templFile); } if (imageFile != "-") { struct stat fileInfo; if (stat(imageFile.c_str(), &fileInfo) == 0) imageSize = fileInfo.st_size; } //____________________ if (msg) { msg("Image file: %1", imageFile); msg("Jigdo: %1", jigdoFile); msg("Template: %1", templFile); } return result; } //______________________________________________________________________ void exit_tryHelp() { cerr << subst(_("%1: Try `%1 -h' or `man jigdo-file' for more " "information"), binName()) << endl; throw Cleanup(3); } //______________________________________________________________________ int main(int argc, char* argv[]) { # if ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain(PACKAGE, PACKAGE_LOCALE_DIR); textdomain(PACKAGE); # endif # if DEBUG Logger::setEnabled("general"); # else Debug::abortAfterFailedAssertion = false; # endif try { set_new_handler(outOfMemory); JigdoFileCmd::Command command = JigdoFileCmd::cmdOptions(argc, argv); switch (command) { case JigdoFileCmd::MAKE_TEMPLATE: returnValue = JigdoFileCmd::makeTemplate(); break; case JigdoFileCmd::MAKE_IMAGE: returnValue = JigdoFileCmd::makeImage(); break; case JigdoFileCmd::PRINT_MISSING: case JigdoFileCmd::PRINT_MISSING_ALL: returnValue = JigdoFileCmd::printMissing(command); break; case JigdoFileCmd::SCAN: returnValue = JigdoFileCmd::scanFiles(); break; case JigdoFileCmd::VERIFY: returnValue = JigdoFileCmd::verifyImage(); break; case JigdoFileCmd::LIST_TEMPLATE: returnValue = JigdoFileCmd::listTemplate(); break; case JigdoFileCmd::MD5SUM: returnValue = JigdoFileCmd::md5sumFiles(); break; } } catch (bad_alloc) { outOfMemory(); } catch (Cleanup c) { msg("[Cleanup %1]", c.returnValue); return c.returnValue; } catch (Error e) { string err = binName(); err += ": "; err += e.message; JigdoFileCmd::optReporter->error(err); return 3; } catch (...) { // Uncaught exception - this should not happen(tm) string err = binName(); err += ": Unknown error"; JigdoFileCmd::optReporter->error(err); return 3; } msg("[exit(%1)]", returnValue); return returnValue; } jigdo-0.7.3/src/jigdo.rc0000644000175000017500000000003407701377706014653 0ustar richardrichardwindow ICON jigdo32.ico jigdo-0.7.3/src/jigdoconfig-test.cc0000644000175000017500000000223110121135314016751 0ustar richardrichard/* $Id: jigdoconfig-test.cc,v 1.5 2004/09/12 21:08:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Representation for config data in a .jigdo file - based on ConfigFile #test-deps jigdoconfig.o util/configfile.o */ #include #include #include //______________________________________________________________________ namespace { struct PR : public JigdoConfig::ProgressReporter { void error(const string& message, const size_t lineNr) { if (lineNr > 0) cerr << lineNr << ": "; cerr << message << endl; } void info(const string& message, const size_t lineNr) { if (lineNr > 0) cerr << lineNr << ": "; cerr << message << endl; } }; PR myPR; } int main(int argc, char* argv[]) { if (argc == 1) { cerr << "Syntax: " << argv[0] << " " << endl; return 1; } JigdoConfig jc(argv[1], myPR); } jigdo-0.7.3/src/jigdoconfig.cc0000644000175000017500000002411110225765142016012 0ustar richardrichard/* $Id: jigdoconfig.cc,v 1.6 2005/04/09 14:44:50 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Representation for config data in a .jigdo file - based on ConfigFile */ #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("jigdoconfig") void JigdoConfig::ProgressReporter::error(const string& message) { cerr << message << endl; } void JigdoConfig::ProgressReporter::info(const string& message) { cerr << message << endl; } //________________________________________ namespace { void forwardPrep(string& result, const string& fileName, size_t lineNr, const string& message) { result = subst("%1:%2: %3", fileName, static_cast(lineNr), message); } } void JigdoConfig::ForwardReporter::error(const string& message, const size_t lineNr) { string s; forwardPrep(s, fileName, lineNr, message); reporter->error(s); } void JigdoConfig::ForwardReporter::info(const string& message, const size_t lineNr) { string s; forwardPrep(s, fileName, lineNr, message); reporter->info(s); } //______________________________________________________________________ JigdoConfig::JigdoConfig(const char* jigdoFile, ProgressReporter& pr) : config(0), serverMap(), freporter(pr, jigdoFile) { ifstream f(jigdoFile); if (!f) { string err = subst(_("Could not open `%1' for input: %2"), jigdoFile, (errno != 0 ? strerror(errno) : "")); freporter.reporter->error(err); return; } // Read config data config = new ConfigFile(freporter); f >> *config; rescan(); # if DEBUG if (debug.enabled()) { debug("[Servers] mapping is:"); for (Map::iterator i = serverMap.begin(), e =serverMap.end(); i != e; ++i){ for (vector::iterator j = i->second.begin(), k = i->second.end(); j != k; ++j) debug(" %1 => %2", i->first, *j); } } # endif } //______________________________________________________________________ JigdoConfig::JigdoConfig(const char* jigdoFile, ConfigFile* configFile, ProgressReporter& pr) : config(configFile), serverMap(), freporter(pr, jigdoFile) { configFile->setReporter(freporter); rescan(); } JigdoConfig::JigdoConfig(const string& jigdoFile, ConfigFile* configFile, ProgressReporter& pr) : config(configFile), serverMap(), freporter(pr, jigdoFile) { configFile->setReporter(freporter); rescan(); } //______________________________________________________________________ namespace { const char* const SECTION_NAME = "Servers"; } /* Creates mapping from label name to URI. Syntactically incorrect lines are just ignored here, the file is assumed to be correct. Error in case of loops, e.g. "LabelA=LabelB:foo/", "LabelB=LabelA:bar". */ void JigdoConfig::rescan() { ConfigFile::iterator first = config->firstSection(SECTION_NAME); ConfigFile::iterator i = first; ConfigFile::iterator end = config->end(); list entries; serverMap.clear(); /* Build a list of all the entries inside [Servers] sections in the file, to ease their subsequent handling. */ while (i != end) { ServerLine l; // For each "Label=..." line inside a "[Servers]" section while (i.nextLabel()) { // Go to next non-comment, non-empty line if (!i.setLabelOffsets(l.labelStart, l.labelEnd, l.valueStart)) continue; l.line = i; entries.push_back(l); } // Go to next "[Servers]" section --i; // because the first thing nextSection() does is advance i i.nextSection(SECTION_NAME); } // Set up serverMap bool printError = true; while (!entries.empty()) { ServerLine& l = entries.front(); string label(*l.line, l.labelStart, l.labelEnd - l.labelStart); debug("rescan: `%1'", label); rescan_addLabel(entries, label, printError); } scanVersionInfo(); } //________________________________________ void JigdoConfig::rescan_makeSubst(list& entries, Map::iterator mapl, const ServerLine& l, bool& printError) { // Split the value, "Foo:some/path", into whitespace-separated words vector words; ConfigFile::split(words, *l.line, l.valueStart); # if 0 if (words.size() > 1 && printError) { /* In the future, there might be support for --switches, so don't allow >1 words per URI. */ string err = subst(_("Line contains more than one URI, ignoring part " "after `%1' (maybe you need to use \"\" quotes?)"), words[0]); size_t lineNr = 1; ConfigFile::iterator findLineNr = l.line; while (findLineNr != config->begin()) { ++lineNr; --findLineNr; } freporter.error(err, lineNr); printError = false; } # endif //____________________ // Where to append new URIs for this label vector& uris = mapl->second; // Extract label reference ("Foo") from value if (words.empty()) { // Empty value, e.g. for *l.line=="MD5Sum=", so add empty string uris.push_back(""); return; } string& s = words.front(); string::size_type colon = s.find(':'); if (colon == string::npos) { debug("Appending to entry for `%1' fixed mapping `%2'", mapl->first, s); uris.push_back(s); // No label ref, append directly return; } //____________________ string refLabel(s, 0, colon); debug("rescan_addLabel ref `%1'", refLabel); // Ensure there's a map entry for refLabel, even if it's empty - RECURSE Map::iterator mapr = rescan_addLabel(entries, refLabel, printError); Paranoid(mapr != serverMap.end()); if (mapr->second.empty()) { debug("Appending to entry for `%1' fixed mapping `%2'", mapl->first, s); uris.push_back(s); // Label not defined, so no substitution return; } /* For each of the mappings of "Foo", substitute the "Foo:" in "label" with the mapping */ debug("Appending to entry for `%1' mappings for `%2':", mapl->first, refLabel); for (vector::iterator i = mapr->second.begin(), e = mapr->second.end(); i != e; ++i) { uris.push_back(*i); uris.back().append(s, colon + 1, string::npos); debug(" %1", uris.back()); } return; } //________________________________________ /* Add to serverMap a mapping for a line "Label=Foo:some/path" in the config file. "Label" is the key for serverMap, and this method adds entries to the corresponding value_type (which is a vector). If the value of the line begins with a reference to another label, e.g. "Foo" for the value "Foo:some/path", recursively try to find a list of absolute URIs that the reference expands to. If that list is empty (this will be the case for labels like "http", "ftp"!), append the value unchanged to the vector, otherwise append one string to the vector for each URI in the list, which is formed by replacing the label reference "Foo:" with the URI. Infinite recursion due to a loop of labels referencing each other is not possible, because we create an empty entry *before* recursing. NB, in case of loops, the resulting mapping is undefined; in practice, all the mappings "below" what caused the loop will be omitted, not only the members of the loop themselves. first is just a (small) efficiency improvement; it points to the first "Label=Foo:some/path" in the config file. */ JigdoConfig::Map::iterator JigdoConfig::rescan_addLabel( list& entries, const string& label, bool& printError) { Map::iterator mapl = serverMap.find(label); if (mapl != serverMap.end()) return mapl; // Create entry in serverMap for this label. debug("rescan_addLabel label `%1'", label); pair x = serverMap.insert(make_pair(label, vector())); mapl = x.first; // Search through the list for entries with label. while (true) { list::iterator i = entries.begin(), e = entries.end(); // - 2 different libstdc++ versions, not one single way for this: //while (i != e && label.compare(*(i->line), i->labelStart, // i->labelEnd - i->labelStart) != 0) ++i; //while (i != e // && label.compare(0, string::npos, // *(i->line), i->labelStart, i->labelEnd - i->labelStart) != 0) ++i; // So do it manually - advance i until "label" matches "label{Start,End}" while (i != e) { size_t labelLen = i->labelEnd - i->labelStart; if (label.size() == labelLen) { size_t j = 0; while (j < labelLen && label[j] == (*i->line)[i->labelStart + j]) ++j; if (j == labelLen) break; // Found } ++i; } if (i == e) break; /* Entry found - add it to mapl.second, substituting any "Foo:" references with the possible values for "Foo". */ rescan_makeSubst(entries, mapl, *i, printError); // Remove entry entries.erase(i); } return mapl; } //______________________________________________________________________ void JigdoConfig::scanVersionInfo() { ConfigFile::iterator versionLine = config->firstSection("Jigdo"); if (versionLine == config->end()) return; size_t n = versionLine.nextLabel("Version"); if (n == 0) return; vector value; ConfigFile::split(value, *versionLine, n); if (value.empty()) return; unsigned ver = 0; string::const_iterator i = value.front().begin(); string::const_iterator e = value.front().end(); while (i != e && *i >= '0' && *i <= '9') { ver = 10 * ver + *i - '0'; ++i; } debug("scanVersionInfo major=%1", ver); if (ver > FILEFORMAT_MAJOR) { throw Error(_("Upgrade required - this .jigdo file needs " "a newer version of the jigdo program")); //freporter.reporter->error(err); } } jigdo-0.7.3/src/jigdoconfig.fh0000644000175000017500000000072107701377674016041 0ustar richardrichard/* $Id: jigdoconfig.fh,v 1.1.1.1 2003/07/04 22:29:16 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Representation for config data in a .jigdo file - based on ConfigFile */ class JigdoConfig; jigdo-0.7.3/src/jigdoconfig.hh0000644000175000017500000001556210225765142016036 0ustar richardrichard/* $Id: jigdoconfig.hh,v 1.5 2005/04/09 14:44:50 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Representation for config data in a .jigdo file - based on ConfigFile Mostly, this class just "forwards" requests by making the appropriate calls to the ConfigFile object, with one exception: It caches the labels in the [Servers] section and detects loops. */ #ifndef JIGDOCONFIG_HH #define JIGDOCONFIG_HH #include #include #include #include //______________________________________________________________________ /** Representation for config data in a .jigdo file - based on ConfigFile */ class JigdoConfig { private: /* multimap doesn't make guarantees about order of inserted values with equal key, which needs to be preserved in our case. */ typedef map > Map; //________________________________________ public: /** To be implemented by anyone who is interested in errors/info from the JigdoConfig */ class ProgressReporter { public: virtual ~ProgressReporter() { } virtual void error(const string& message); virtual void info(const string& message); }; //________________________________________ #if 0 /* Use same function from net/uri.hh */ /** Helper function: Given a string, return 0 if the string has no "Label:" prefix, otherwise return the offset of the ':'. The "Label" portion of the string can contain *any* character except '/' and whitespace/control characters */ static inline unsigned findLabelColon(const string& s); #endif //________________________________________ /** Open file for input and create a ConfigFile object */ JigdoConfig(const char* jigdoFile, ProgressReporter& pr); JigdoConfig(const string& jigdoFile, ProgressReporter& pr); /** Take over possession of existing ConfigFile - configFile will be deleted in ~JigdoConfig()! jigdoFile argument is not used except for error messages. */ JigdoConfig(const char* jigdoFile, ConfigFile* configFile, ProgressReporter& pr); JigdoConfig(const string& jigdoFile, ConfigFile* configFile, ProgressReporter& pr); ~JigdoConfig() { delete config; } ConfigFile& configFile() { return *config; } //________________________________________ /** Prepare internal map from label name to URIs. Is called automatically during JigdoConfig(), but must be called manually afterwards whenever any entry in the ConfigFile's "[Servers]" section changes. May throw an Error if the jigdo file format version is not supported. */ void rescan(); /** Change reporter for error messages */ inline void setReporter(ProgressReporter& pr); /** Given an URI-style string like "MyServer:dir/foo/file.gz", do label lookup (looking for [Servers] entries like "MyServer=...") and return the resulting strings, e.g. "ftp://mysite.com/dir/foo/file.gz". The class will enumerate all the possible URIs. NB: After a jc.rescan(), no further calls to next() of existing Lookups for that JigdoConfig are allowed. Also, query must stay valid throughout the lifetime of Lookup, since a reference to it is maintained. */ class Lookup { public: inline Lookup(const JigdoConfig& jc, const string& query); /** If true returned, result has been overwritten with next value. Otherwise, end of list has been reached and result is unchanged. */ inline bool next(string& result); // Default copy ctor and dtor private: const JigdoConfig& config; // "MyServer:dir/foo/file.gz" or null: next() returns false const string* uri; string::size_type colon; // Offset of ':' in query // Pointer in list for "MyServer" mappings; pointer to end of list vector::const_iterator cur, end; }; friend class Lookup; //________________________________________ private: /* Adds filename and line number before reporting. This is used by JigdoConfig to talk to ConfigFile. */ struct ForwardReporter : ConfigFile::ProgressReporter { inline ForwardReporter(JigdoConfig::ProgressReporter& pr, const string& file); inline ForwardReporter(JigdoConfig::ProgressReporter& pr, const char* file); virtual ~ForwardReporter() { } virtual void error(const string& message, const size_t lineNr = 0); virtual void info(const string& message, const size_t lineNr = 0); JigdoConfig::ProgressReporter* reporter; string fileName; }; //________________________________________ struct ServerLine { ConfigFile::iterator line; size_t labelStart, labelEnd, valueStart; }; Map::iterator rescan_addLabel(list& entries, const string& label, bool& printError); inline void rescan_makeSubst(list& entries, Map::iterator mapl, const ServerLine& l, bool& printError); void scanVersionInfo(); // Check for supported file format version number ConfigFile* config; Map serverMap; ForwardReporter freporter; }; //______________________________________________________________________ JigdoConfig::ForwardReporter::ForwardReporter( JigdoConfig::ProgressReporter& pr, const string& file) : reporter(&pr), fileName(file) { } JigdoConfig::ForwardReporter::ForwardReporter( JigdoConfig::ProgressReporter& pr, const char* file) : reporter(&pr), fileName(file) { } void JigdoConfig::setReporter(ProgressReporter& pr) { freporter.reporter = ≺ } //________________________________________ JigdoConfig::Lookup::Lookup(const JigdoConfig& jc, const string& query) : config(jc), uri(&query), colon(query.find(':')), cur() { /* colon == string::npos means: The URI doesn't contain a ':', or it does but the label before it ("MyServer") isn't listed in the mapping, or the label is listed but the corresponding vector is empty. In all these cases, the Lookup will only return one string - the original query. */ if (colon != string::npos) { string label(query, 0, colon); Map::const_iterator vec = config.serverMap.find(label); ++colon; if (vec != config.serverMap.end() && vec->second.size() != 0) { cur = vec->second.begin(); end = vec->second.end(); } else { colon = string::npos; } } } bool JigdoConfig::Lookup::next(string& result) { if (uri == 0) return false; if (colon == string::npos) { // Only return one value result = *uri; uri = 0; return true; } // Iterate through vector result = *cur; result.append(*uri, colon, string::npos); ++cur; if (cur == end) uri = 0; return true; } #endif jigdo-0.7.3/src/job/cached-url.cc0000644000175000017500000001410310261546437016315 0ustar richardrichard/* $Id: cached-url.cc,v 1.7 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Spool data from cache file LIGHTLY TESTED ONLY, BOUND TO CONTAIN BUGS */ #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("cached-url") using namespace Job; namespace { /* Maximum nr of microseconds the idle callback funtion spoolDataCallback() should run before giving up control again. This is intentionally a multiple of the value of the GTK+ frontend's JobList::TICK_INTERVAL: The frontend should still be able to update the screen etc every n-th tick. (This should be a much higher value (>=1000?) for non-interactive frontends.) */ const unsigned MAX_CALLBACK_DURATION = 500000; } CachedUrl::CachedUrl(const string& filename, uint64 prio) : DataSource(), filenameVal(filename), priority(prio), progressVal(), file(0) { struct stat fileInfo; int status = stat(filename.c_str(), &fileInfo); Assert (status == 0); // Should be ensured by creator of object if (status == 0) progressVal.setDataSize(fileInfo.st_size); } CachedUrl::~CachedUrl() { active.erase(this); delete file; } const Progress* CachedUrl::progress() const { return &progressVal; } const string& CachedUrl::location() const { return filenameVal; } void CachedUrl::run() { debug("CachedUrl %1 run()", this); IOSOURCE_SEND(DataSource::IO, io, dataSource_dataSize, (progressVal.dataSize())); cont(); } bool CachedUrl::paused() const { Set::const_iterator i = active.find(const_cast(this)); return (i == active.end()); } void CachedUrl::pause() { active.erase(this); } // Add this to active set, maybe register glib callback void CachedUrl::cont() { active.insert(this); if (active.size() == 1 && spoolDataCallbackId == 0) { debug("Callback on"); g_idle_add(&spoolDataCallback, 0); } } CachedUrl::Set CachedUrl::active; int CachedUrl::spoolDataCallbackId = 0; // Initially assume very slow access: 20kB/sec unsigned CachedUrl::readSpeed = 20 << 10; /* This function treats the set of active CachedUrls as a queue and keeps reading data from the first object. The difficult bit is that we have to try to do 2 contradicting things equally well: 1) Always return before MAX_CALLBACK_DURATION microseconds are over - we want to avoid that the frontend appears to "hang". 2) Read the data from the file in chunks which are as big as possible, for best speed. It is even conceivable that the file resides on NFS and that the available network bandwidth varies over time... Solution (imperfect, but more than sufficient in practice): Imitate TCP's slow start algorithm: Read in smaller chunks at first, then keep adjusting the size depending on the measured read speed. */ gboolean CachedUrl::spoolDataCallback(gpointer) { if (active.empty()) { debug("Callback off"); spoolDataCallbackId = 0; return FALSE; // "Don't call me again" } // FIXME: Code below only lightly tested, probably buggy debug("Callback working"); GTimeVal start; g_get_current_time(&start); const unsigned BUFSIZE = 256 << 10; ArrayAutoPtr bufDel(new byte[BUFSIZE]); byte* buf = bufDel.get(); unsigned left = MAX_CALLBACK_DURATION; // usecs left before timeout while (true) { CachedUrl* x = *active.begin(); IOSource& io = x->io; // Ensure file is open if (x->file == 0) { x->file = new bifstream(x->filenameVal.c_str(), ios::binary); if (!*x->file) { string err = subst(_("Could not open `%L1' for input: %L2"), x->filenameVal, strerror(errno)); IOSOURCE_SEND(DataSource::IO, io, job_failed, (err)); active.erase(x); break; } } /* toRead = nr of bytes to read from file, such that "left" usecs pass during the read with an assumed speed of readSpeed. */ unsigned toRead = uint64(readSpeed) * left / 1000000; if (toRead > BUFSIZE) toRead = BUFSIZE; readBytes(*x->file, buf, toRead); unsigned n = x->file->gcount(); debug(" readSpeed %1 bytes/sec, %2 usecs left => reading %3 bytes", readSpeed, left, toRead); // Pass data to consumer uint64 currentSize = x->progressVal.currentSize() + n; x->progressVal.setCurrentSize(currentSize); IOSOURCE_SEND(DataSource::IO, io, dataSource_data, (buf, n, currentSize)); if (x->file->eof()) { IOSOURCE_SEND(DataSource::IO, io, job_succeeded, ()); active.erase(x); break; } if (!*(x->file)) { string err = subst(_("Could not read from `%L1': %L2"), x->filenameVal, strerror(errno)); IOSOURCE_SEND(DataSource::IO, io, job_failed, (err)); active.erase(x); break; } GTimeVal nowTime; g_get_current_time(&nowTime); // now = usecs since start's value unsigned now = (nowTime.tv_sec - start.tv_sec) * 1000000 + nowTime.tv_usec - start.tv_usec; if (now + 50*1000 >= MAX_CALLBACK_DURATION) { // Out of time (or nearly so; allowing 50ms earlier), stop for now break; } // timeTaken = usecs it took to read n bytes unsigned timeTaken = now + left - MAX_CALLBACK_DURATION; unsigned newSpeed = uint64(n) * 1000000 / timeTaken; // At most double or halve the readSpeed if (newSpeed < readSpeed / 2) readSpeed /= 2; else if (newSpeed > readSpeed * 2) readSpeed *= 2; else readSpeed = newSpeed; debug(" Got %1 bytes in %2 usec (%3 bytes/sec), new readSpeed %4 " "bytes/sec", n, timeTaken, newSpeed, readSpeed); left = MAX_CALLBACK_DURATION - now; } // endwhile (true) return TRUE; } jigdo-0.7.3/src/job/cached-url.hh0000644000175000017500000000536710120166075016331 0ustar richardrichard/* $Id: cached-url.hh,v 1.3 2004/09/09 23:50:21 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Spool data from cache file A CachedUrl is started when MakeImageDl::childFor() was instructed to return a DataSource for an URL/md5sum, but that data was already present on the local disc. */ #ifndef CACHED_URL_HH #define CACHED_URL_HH #include #include #include #include #include #include #include //______________________________________________________________________ namespace Job { class CachedUrl; } /** Spool data from cache file */ class Job::CachedUrl : public Job::DataSource { public: /** Create object, but don't start outputting data yet - use run() to do that. @param filename File to spool from @param prio "Priority" - if >1 CachedUrls are running, the ones with lower prio get spooled first. */ CachedUrl(const string& filename, uint64 prio); virtual ~CachedUrl(); virtual void run(); /** Is the download currently paused? From DataSource. */ virtual bool paused() const; /** Pause the download. From DataSource. */ virtual void pause(); /** Continue downloading. From DataSource. */ virtual void cont(); /** Return the internal progress object. From DataSource. */ virtual const Progress* progress() const; /** Return the URL used to download the data. From DataSource. */ virtual const string& location() const; //inline const string& filename() const; private: /* Set of all active (non-paused) downloads. After construction, CachedUrls are initially paused, run() and cont() are identical. */ struct Cmp { inline bool operator()(const CachedUrl* a, const CachedUrl* b) const; }; friend struct Cmp; typedef set Set; static Set active; static unsigned readSpeed; // Bytes per sec read from active.front()->file // glib callback, spools data when main loop is otherwise idle. static gboolean spoolDataCallback(gpointer); static int spoolDataCallbackId; // glib event source ID for above string filenameVal; uint64 priority; Progress progressVal; bifstream* file; }; //______________________________________________________________________ //const string& Job::CachedUrl::filename() const { return filenameVal; } bool Job::CachedUrl::Cmp::operator()(const CachedUrl* a, const CachedUrl* b) const { if (a->priority == b->priority) return (a < b); else return (a->priority < b->priority); } #endif jigdo-0.7.3/src/job/datasource.cc0000644000175000017500000000077410114225320016427 0ustar richardrichard/* $Id: datasource.cc,v 1.2 2004/08/29 01:01:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Interface for objects returning data from the network or from disk */ #include using namespace Job; //DataSource::~DataSource() { } jigdo-0.7.3/src/job/datasource.hh0000644000175000017500000000611010226060300016426 0ustar richardrichard/* $Id: datasource.hh,v 1.7 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Interface for objects returning data from the network or from disk */ #ifndef DATASOURCE_HH #define DATASOURCE_HH #include #include #include #include #include //______________________________________________________________________ namespace Job { class DataSource; } /** Interface for objects returning data from the network or from disk. Implemented by SingleUrl and CachedUrl. MakeImageDl::dataSourceFor() is the function which examines the local jigdo download's temporary directory and creates a SingleUrl/CachedUrl as appropriate before putting it inside a MakeImageDl::Child. */ class Job::DataSource : NoCopy { public: /** User interaction for DataSource. Instances of derived classes are attached to any DataSources that the MakeImageDl creates. */ class IO : public Job::IO { public: // Not overriding here: // virtual IO* job_removeIo(IO* rmIo); /** Called by the job when it is deleted. If the IO object considers itself owned by its job, it can delete itself. */ virtual void job_deleted() = 0; /** Called when the job has successfully completed its task. */ virtual void job_succeeded() = 0; /** Called when the job fails. The only remaining action after getting this is to delete the job object. */ virtual void job_failed(const string& message) = 0; /** Informational message. */ virtual void job_message(const string& message) = 0; /** Called as soon as the size of the downloaded data is known. May not be called at all if the size is unknown. Problem with libwww: Returns size as long int - 2 GB size limit! */ virtual void dataSource_dataSize(uint64 n) = 0; /** Called during download whenever data arrives, with the data that just arrived. You can write the data to a file, copy it away etc. currentSize is the offset into the downloaded data (including the "size" new bytes) - useful for "x% done" messages. */ virtual void dataSource_data(const byte* data, unsigned size, uint64 currentSize) = 0; }; //____________________ IOSource io; DataSource() : io() { }; virtual ~DataSource() { } /** Start delivering data. */ virtual void run() = 0; /** Is the data stream currently paused? */ virtual bool paused() const = 0; /** Pause the data stream. */ virtual void pause() = 0; /** Continue after pause(). */ virtual void cont() = 0; /** Return the internal progress object. */ virtual const Progress* progress() const = 0; /** Return the URL used to download the data, or its filename on disc */ virtual const string& location() const = 0; }; #endif jigdo-0.7.3/src/job/jigdo-io-test.cc0000644000175000017500000004116710226053701016762 0ustar richardrichard/* $Id: jigdo-io-test.cc,v 1.13 2005/04/09 22:31:29 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. #test-deps job/datasource.o util/gunzip.o util/configfile.o util/md5sum.o #test-deps util/glibc-md5.o net/uri.o job/url-mapping.o #test-ldflags $(LIBS) */ #define DEBUG 1 #include #include #include #include #include #include #include /* Cannot link against jigdo-io.o, because this is not always compiled with DEBUG==1 */ #include //______________________________________________________________________ using namespace Job; typedef MakeImageDl::Child Child; namespace { GMainLoop* gLoop = 0; // Map of URL to content at that URL typedef map MapWww; MapWww www; /** Special DataSource which outputs data from memory */ class MemData : public Job::DataSource { public: /* If contents == 0, will call job_failed() */ MemData(DataSource::IO* ioPtr, const string& loc, const char* contents) : DataSource(), locVal(loc), data(reinterpret_cast(contents)), cur(data), dataEnd(data) { if (ioPtr) io.addListener(*ioPtr); if (contents != 0) dataEnd += strlen(contents); msg("MemData %1 len=%2", location(), dataEnd - data); } virtual ~MemData(); virtual void run() { if (data == 0) { msg("MemData fail %1", location()); string err = "Failed"; //io->job_failed(&err); IOSOURCE_SEND(DataSource::IO, io, job_failed, (err)); } } virtual bool paused() const { return false; } virtual void pause() { } virtual void cont() { } virtual const Progress* progress() const { return 0; } virtual const string& location() const { return locVal; } void reset() { cur = data; } bool finished() { return cur == dataEnd; } /* Give n lines of data to ioPtr. default means all data. Must be called manually to feed data to the rest of the code, since we do not use the glib idle loop. */ void output(unsigned n = UINT_MAX) { const byte* newCur = cur; while (newCur < dataEnd && n > 0) { if (*newCur == '\n') --n; ++newCur; } if (newCur == cur) return; IOSOURCE_SEND(DataSource::IO, io, dataSource_data, (cur, newCur - cur, newCur - data)); cur = newCur; if (cur == dataEnd) IOSOURCE_SEND(DataSource::IO, io, job_succeeded, ()); } private: string locVal; const byte* data; const byte* cur; const byte* dataEnd; }; MemData::~MemData() { msg("~MemData %1", location()); } //______________________________________________________________________ // Get MemData from Child inline MemData* memData(Child* c) { MemData* result = dynamic_cast(c->source()); Assert(result != 0); return result; } // Get MemData from MakeImageDl and URL MemData* memData(MakeImageDl& m, const char* url) { Assert(!m.children().empty()); MemData* result = 0; typedef MakeImageDl::ChildList::const_iterator Iter; for (Iter i = m.children().begin(), e = m.children().end(); i != e; ++i){ if (i->get()->source()->location() == url) { result = dynamic_cast(i->get()->source()); break; } } if (result == 0) msg("No download of %1 currently active", url); Assert(result != 0); return result; } inline void output(MakeImageDl& m, const char* url, size_t n = UINT_MAX) { memData(m, url)->output(n); } //______________________________________________________________________ // Execute callbacks void idle() { while (g_main_context_iteration(0, FALSE) == true) { } } //______________________________________________________________________ const char* const hexDigits = "0123456789abcdef"; void escapedChar(string* o, byte c) { switch (c) { case 0: *o += "\\0"; break; case '\n': *o += "\\n"; break; case '\t': *o += "\\t"; break; case '"': case '\\': *o += '\\'; *o += c; break; default: if (c >= ' ' && c <= '~') { *o += c; } else { *o += "\\x"; *o += hexDigits[unsigned(c) >> 4]; *o += hexDigits[unsigned(c) & 0xfU]; } } } inline string escapedString(const string& s) { string result; for (unsigned i = 0; i < s.length(); ++i) escapedChar(&result, s[i]); return result; } //______________________________________________________________________ /* We want to record the "...: imgSect..." lines. */ bool jigdoIoEnabled = false; string imgSectLogged; void loggerPut(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]) { if (unitName == "jigdo-io") { if (strncmp(format, "imgSect", 7) == 0 || strncmp(format, "generateError", 13) == 0) { imgSectLogged += Subst::subst(format, args, arg); imgSectLogged += '\n'; } if (!jigdoIoEnabled) return; } Logger::defaultPut(unitName, unitNameLen, format, args, arg); } void loggerInit() { Logger::setOutputFunction(&loggerPut); Logger* l = Logger::enumerate(); while (l != 0 && strcmp(l->name(), "jigdo-io") == 0) l = Logger::enumerate(l); Assert(l != 0); // jigdo-io.cc must have been compiled with DEBUG==1 jigdoIoEnabled = l->enabled(); if (!jigdoIoEnabled) Logger::setEnabled("jigdo-io"); } } // namespace //====================================================================== MakeImageDl::Child* MakeImageDl::childFor(const string& url, const MD5* md, string* leafnameOut, Child*) { Assert(md == 0); if (leafnameOut != 0) *leafnameOut = url; // Look up content const char* contents; MapWww::iterator i = www.find(url); if (i == www.end()) contents = 0; else contents = i->second; auto_ptr dl(new MemData(0, url, contents)); Child* c = new Child(this, &childrenVal, dl.get(), 0); dl.release(); c->childSuccFail = true; return c; } MakeImageDl::MakeImageDl(/*IO* ioPtr,*/ const string& jigdoUri, const string& destination) : io(/*ioPtr*/), stateVal(DOWNLOADING_JIGDO), jigdoUrl(jigdoUri), childrenVal(), dest(destination), tmpDirVal("/tmp"), mi(), imageNameVal(), imageInfoVal(), imageShortInfoVal(), templateUrls(0), templateMd5Val(0) { if (!jigdoUri.empty()) { Child* a = childFor(jigdoUri); JigdoIO* jio = new JigdoIO(a, jigdoUrl); a->source()->io.addListener(*jio); a->source()->run(); } } Job::MakeImageDl::~MakeImageDl() { } // const char* Job::MakeImageDl::destDescTemplateVal = // _("Cache entry %1 -- %2"); void MakeImageDl::childFailed(Child* childDl) { msg("childFailed: %1", childDl->source() ? childDl->source()->location() :"[deleted source]"); // No: delete childDl; } // void MakeImageDl::childSucceeded(Child* childDl, DataSource::IO* /*chldIo*/) { // msg("childSucceeded: %1", // childDl->source() ? childDl->source()->location() :"[deleted source]"); // // No: delete childDl; // } void MakeImageDl::setImageSection(string* imageName, string*, string*, PartUrlMapping*, MD5**) { Paranoid(!haveImageSection()); imageNameVal.swap(*imageName); } void MakeImageDl::jigdoFinished() { debug("jigdoFinished"); } void Job::MakeImageDl::killAllChildren() { debug("killAllChildren"); } void MakeImageDl::Child::job_deleted() { } void MakeImageDl::Child::job_succeeded() { } void MakeImageDl::Child::job_failed(const string&) { } void MakeImageDl::Child::job_message(const string&) { } void MakeImageDl::Child::dataSource_dataSize(uint64) { } void MakeImageDl::Child::dataSource_data(const byte*, unsigned, uint64) { } //====================================================================== // Basic check: Does it find the image section? void testSimple() { msg("---------------------------------------- testSimple"); www.insert(make_pair("http://simple", "[Jigdo]\n" "Version = 0 #\n" "Version=1.1\n" "Generator=jigdo-file/0.7.0\n" "\n" "[Servers]\n" "X=http://10.0.0.5/~richard/ironmaiden/\n" "X=http://localhost/~richard/ironmaiden/fb-\n" "\n" "[Image]\n" "Filename=image\n" "Template=image.template\n" "Template-MD5Sum=h5FAyHqEsvXSTuGUNdhzJw\n" "xx = 8\\ 9 a b c' \"'\"d' e\" #ffo\n" "ShortInfo='\"Debian GNU/Linux 3.0 r1 \\\"Woody\\\" - i386 B-$num\"'\n" "Info='Generated on Sun, 16 Mar 2003 04:45:40 -0700'\n" "\n" "[Image]\n" "Filename=image\n" "Template=image.template\n" "Template-MD5Sum=h5FAyHqEsvXSTuGUNdhzJw\n" "\n" "[Parts]\n" "FsGFXNwcbCdvWTamkRdp7g=X:part4\n" "H2-Rw7tuyjWdxVeqnS_vcw=X:part8\n" "gG64beTeh9nZWMVvv80zgw=X:part9\n")); MakeImageDl m("", ""); imgSectLogged.clear(); Child* a = m.childFor("http://simple"); a->source()->io.addListener(*new JigdoIO(a, "http://simple")); while (a->source() != 0 && !memData(a)->finished()) { // Feed single bytes memData(a)->output(1); idle(); } msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_parsed: http://simple:17\n" "imgSect_eof: Finished\n"); } // Error message: No image section found void testNoMD5() { msg("---------------------------------------- testNoMD5"); www.insert(make_pair("http://no-md5", "[Image]\n" "Filename=image\n" "Template=image.template\n")); MakeImageDl m("", ""); imgSectLogged.clear(); Child* a = m.childFor("http://no-md5"); a->source()->io.addListener(*new JigdoIO(a, "http://no-md5")); memData(a)->output(); idle(); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "generateError: `Template-MD5Sum=...' line missing" " in [Image] section (line 2 in http://no-md5)\n"); } // Handle missing newline at end of file void testMinimal() { msg("---------------------------------------- testMinimal"); www.insert(make_pair("http://minimal", "[Image]\n" "Filename=image\n" "Template=image.template\n" "Template-MD5Sum=h5FAyHqEsvXSTuGUNdhzJw")); MakeImageDl m("", ""); imgSectLogged.clear(); Child* a = m.childFor("http://minimal"); a->source()->io.addListener(*new JigdoIO(a, "http://minimal")); memData(a)->output(); idle(); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_parsed: http://minimal:4\n" "imgSect_eof: Finished\n"); } // Error: recursive include void testLoop() { msg("---------------------------------------- testLoop"); www.insert(make_pair("http://loop", "\t[ Include http://simple ]\n" "[Include http://loop]\n")); MakeImageDl m("", ""); imgSectLogged.clear(); Child* a = m.childFor("http://loop"); a->source()->io.addListener(*new JigdoIO(a, "http://loop")); memData(a)->output(1); // Feed include simple line idle(); output(m, "http://simple"); idle(); memData(a)->output(); // Feed loop include line idle(); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_newChild: From http://loop:1 to child http://simple\n" "imgSect_parsed: http://simple:17\n" "imgSect_eof:I Now at http://loop:1\n" "imgSect_eof:I Waiting for http://loop to download\n" "generateError: Loop of [Include] directives (line 2 in http://loop)\n"); } // Deeper include tree void testFork() { www.insert(make_pair("http://fork", "[Include http://fork1]\n" "[Include http://fork2]\n" "\n" "[Image]\n" "Filename=image\n" "Template=image.template\n" "Template-MD5Sum=h5FAyHqEsvXSTuGUNdhzJw")); www.insert(make_pair("http://fork1", "# yada\n" "[Servers]\n" "Y=http://hoohoo/\n")); www.insert(make_pair("http://fork2", "[Servers]\n" "X=http://booboo/\n" "\n" "[Include http://fork21]\n" "[Include http://fork22]\n" "\n" "[Comment]\n")); www.insert(make_pair("http://fork21", "# Empty\n")); www.insert(make_pair("http://fork22", "[Jigdo]\n" "Version=1.1\n" "\n")); { msg("---------------------------------------- testFork a"); imgSectLogged.clear(); MakeImageDl m("http://fork", ""); output(m, "http://fork"); output(m, "http://fork2"); output(m, "http://fork21"); output(m, "http://fork22"); output(m, "http://fork1"); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_newChild: From http://fork:1 to child http://fork1\n" "imgSect_eof: Now at http://fork:1\n" "imgSect_eof: Now at http://fork:2, descending\n" "imgSect_eof: Now at http://fork2:0\n" "imgSect_eof: Now at http://fork2:4, descending\n" "imgSect_eof: Now at http://fork21:0\n" "imgSect_eof: Now at end of http://fork21, ascending\n" "imgSect_eof: Now at http://fork2:4\n" "imgSect_eof: Now at http://fork2:5, descending\n" "imgSect_eof: Now at http://fork22:0\n" "imgSect_eof: Now at end of http://fork22, ascending\n" "imgSect_eof: Now at http://fork2:5\n" "imgSect_eof: Now at end of http://fork2, ascending\n" "imgSect_eof: Now at http://fork:2\n" "imgSect_eof: Found after last [Include], if any\n" "imgSect_eof:I Now at end of http://fork, ascending\n" "imgSect_eof: Finished\n"); } { msg("---------------------------------------- testFork b"); imgSectLogged.clear(); MakeImageDl m("http://fork", ""); output(m, "http://fork", 1); output(m, "http://fork1"); output(m, "http://fork", 1); output(m, "http://fork2"); output(m, "http://fork21"); output(m, "http://fork22"); output(m, "http://fork"); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_newChild: From http://fork:1 to child http://fork1\n" "imgSect_eof: Now at http://fork:1\n" "imgSect_eof: Waiting for http://fork to download\n" "imgSect_newChild: From http://fork:2 to child http://fork2\n" "imgSect_newChild: From http://fork2:4 to child http://fork21\n" "imgSect_eof: Now at http://fork2:4\n" "imgSect_eof: Now at http://fork2:5, descending\n" "imgSect_eof: Now at http://fork22:0\n" "imgSect_eof: Waiting for http://fork22 to download\n" "imgSect_eof: Now at http://fork2:5\n" "imgSect_eof: Now at end of http://fork2, ascending\n" "imgSect_eof: Now at http://fork:2\n" "imgSect_eof: Waiting for http://fork to download\n" "imgSect_parsed: http://fork:7\n" "imgSect_eof: Finished\n"); } } // Image between includes void testBetween() { www.insert(make_pair("http://between", "[Include http://fork2]\n" "[Image]\n" "Filename=image\n" "Template=image.template\n" "Template-MD5Sum=h5FAyHqEsvXSTuGUNdhzJw\n" "[Include http://fork1]\n")); msg("---------------------------------------- testBetween"); imgSectLogged.clear(); MakeImageDl m("http://between", ""); output(m, "http://between"); output(m, "http://fork1"); output(m, "http://fork2"); output(m, "http://fork22"); output(m, "http://fork21"); msg("logged: \"%1\"", escapedString(imgSectLogged)); Assert(imgSectLogged == "imgSect_newChild: From http://between:1 to child http://fork2\n" "imgSect_newChild: From http://fork2:4 to child http://fork21\n" "imgSect_eof: Now at http://fork2:4\n" "imgSect_eof: Now at http://fork2:5, descending\n" "imgSect_eof: Now at http://fork22:0\n" "imgSect_eof: Now at end of http://fork22, ascending\n" "imgSect_eof: Now at http://fork2:5\n" "imgSect_eof: Now at end of http://fork2, ascending\n" "imgSect_eof: Now at http://between:1\n" "imgSect_eof: Found before [Include]\n" "imgSect_eof:I Now at http://between:6, descending\n" "imgSect_eof:I Now at http://fork1:0\n" "imgSect_eof:I Now at end of http://fork1, ascending\n" "imgSect_eof:I Now at http://between:6\n" "imgSect_eof:I Now at end of http://between, ascending\n" "imgSect_eof: Finished\n"); } //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); loggerInit(); gLoop = g_main_loop_new(0, FALSE); testSimple(); testNoMD5(); testMinimal(); testLoop(); testFork(); testBetween(); msg("Exit"); return 0; } jigdo-0.7.3/src/job/jigdo-io.cc0000644000175000017500000005404510226253017016006 0ustar richardrichard/* $Id: jigdo-io.cc,v 1.26 2005/04/10 16:36:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. IO object for downloads of .jigdo URLs; download, gunzip, interpret */ #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("jigdo-io") using namespace Job; namespace { inline bool isWhitespace(char x) { return ConfigFile::isWhitespace(x); } inline bool advanceWhitespace(string::const_iterator& x, const string::const_iterator& end) { return ConfigFile::advanceWhitespace(x, end); } inline bool advanceWhitespace(string::iterator& x, const string::const_iterator& end) { return ConfigFile::advanceWhitespace(x, end); } } // Root object JigdoIO::JigdoIO(MakeImageDl::Child* c, const string& url/*, DataSource::IO* frontendIo*/) : childDl(c), urlVal(url),/*frontend(frontendIo),*/parent(0), includeLine(0), firstChild(0), next(0), rootAndImageSectionCandidate(this), line(0), section(), imageSectionLine(0), imageName(), imageInfo(), imageShortInfo(), templateUrls(), templateMd5(0), /*childFailedId(0),*/ gunzip(this) { debug("JigdoIO %1", this); } // Non-root, i.e. [Include]d object JigdoIO::JigdoIO(MakeImageDl::Child* c, const string& url, /*DataSource::IO* frontendIo,*/ JigdoIO* parentJigdo, int inclLine) : childDl(c), urlVal(url), /*frontend(frontendIo),*/ parent(parentJigdo), includeLine(inclLine), firstChild(0), next(0), rootAndImageSectionCandidate(parent->root()), line(0), section(), imageSectionLine(0), imageName(), imageInfo(), imageShortInfo(), templateUrls(), templateMd5(0), /*childFailedId(0),*/ gunzip(this) { debug("JigdoIO %1: Parent of %2 is %3", this, url, parent->urlVal); } //______________________________________________________________________ JigdoIO::~JigdoIO() { debug("~JigdoIO"); // Delete all our children JigdoIO* x = firstChild; while (x != 0) { JigdoIO* y = x->next; debug("~JigdoIO: deleting child %1", x); delete x; x = y; } delete templateMd5; } //______________________________________________________________________ void JigdoIO::job_deleted() { } void JigdoIO::job_succeeded() { if (failed()) return; if (gunzip.nextOut() > gunzipBuf) { debug("job_succeeded: No newline at end"); ++line; const char* lineChars = reinterpret_cast(gunzipBuf); if (g_utf8_validate(lineChars, gunzip.nextOut()-gunzipBuf, NULL) != TRUE) return generateError(_("Input .jigdo data is not valid UTF-8")); string line(lineChars, gunzip.nextOut() - gunzipBuf); jigdoLine(&line); if (failed()) return; } if (sectionEnd().failed()) return; setFinished(); XStatus st = imgSect_eof(); if (st.xfailed()) return; //master()->childSucceeded(childDl, this); if (st.returned(1)) master()->jigdoFinished(); // Causes "delete this" } void JigdoIO::job_failed(const string&) { if (failed()) return; string err = _("Download of .jigdo file failed"); master()->generateError(err); imageName.assign("", 1); Paranoid(failed()); //master()->childFailed(childDl, this); } void JigdoIO::job_message(const string&) { } void JigdoIO::dataSource_dataSize(uint64) { } void JigdoIO::dataSource_data(const byte* data, unsigned size, uint64) { Assert(!finished()); if (/*master()->finalState() ||*/ failed()) { debug("Got %1 bytes, ignoring", size); return; } //Assert(master()->state() == MakeImageDl::DOWNLOADING_JIGDO); debug("Got %1 bytes, processing", size); try { gunzip.inject(data, size); } catch (Error e) { ++line; generateError(e.message); return; } } //______________________________________________________________________ void JigdoIO::gunzip_deleted() { } void JigdoIO::gunzip_needOut(Gunzip*) { /* This is only called once, at the very start - afterwards, we always call setOut() from gunzip_data, so Gunzip won't call this. */ gunzip.setOut(gunzipBuf, GUNZIP_BUF_SIZE); } /* Uncompressed data arrives. "decompressed" points somewhere inside gunzipBuf. Split data apart at \n and interpret line(s), then copy any remaining unfinished line to the start of gunzipBuf. The first byte of gunzipBuf (if it contains valid data) is always the first char of a line in the config file. */ void JigdoIO::gunzip_data(Gunzip*, byte* decompressed, unsigned size) { if (failed()) return; // Look for end of line. byte* p = decompressed; const byte* end = decompressed + size; const byte* stringStart = gunzipBuf; string line; while (p < end) { if (*p == '\n') { // Process new line Paranoid(static_cast(p - stringStart) <= GUNZIP_BUF_SIZE); Paranoid(line.empty()); const char* lineChars = reinterpret_cast(stringStart); if (g_utf8_validate(lineChars, p - stringStart, NULL) != TRUE) throw Error(_("Input .jigdo data is not valid UTF-8")); line.append(lineChars, p - stringStart); jigdoLine(&line); if (failed()) return; ++p; stringStart = p; continue; } if (*p == '\r') *p = ' '; // Allow Windows-style line endings by turning CR into space else if (*p == 127 || (*p < 32 && *p != '\t')) // Check for evil chars throw Error(_("Input .jigdo data contains invalid control characters")); ++p; } if (stringStart == gunzipBuf && p == stringStart + GUNZIP_BUF_SIZE) { // A single line fills the whole buffer. Truncate it at that length. debug("gunzip_data: long line"); Paranoid(line.empty()); const char* lineChars = reinterpret_cast(stringStart); if (g_utf8_validate(lineChars, p - stringStart, NULL) != TRUE) throw Error(_("Input .jigdo data is not valid UTF-8")); line.append(lineChars, p - stringStart); jigdoLine(&line); if (failed()) return; // Trick: To ignore remainder of huge line, prepend a comment char '#' gunzipBuf[0] = '#'; gunzip.setOut(gunzipBuf + 1, GUNZIP_BUF_SIZE - 1); return; } unsigned len = p - stringStart; if (len > 0 && stringStart > gunzipBuf) { // Unprocessed data left somewhere inside the buffer - copy to buf start Assert(len < GUNZIP_BUF_SIZE); // Room must be left in the buffer memmove(gunzipBuf, stringStart, len); } gunzip.setOut(gunzipBuf + len, GUNZIP_BUF_SIZE - len); } void JigdoIO::gunzip_failed(string* message) { throw Error(*message, true); } //______________________________________________________________________ void JigdoIO::generateError(const string& msg) { string err; const char* fmt = (finished() ? _("%1 (at end of %3)") : _("%1 (line %2 in %3)")); err = subst(fmt, msg, line, (source() != 0 ? source()->location().c_str() : "?") ); generateError_plain(err); } void JigdoIO::generateError(const char* msg) { string err; const char* fmt = (finished() ? _("%1 (at end of %3)") : _("%1 (line %2 in %3)")); err = subst(fmt, msg, line, (source() != 0 ? source()->location().c_str() : "?") ); generateError_plain(err); } void JigdoIO::generateError_plain(const string& err) { debug("generateError: %1", err); Paranoid(!failed()); if (failed()) return; IOSOURCE_SEND(Job::DataSource::IO, source()->io, job_failed, (err)); string merr = _("Error processing .jigdo file contents"); master()->generateError(merr); imageName.assign("", 1); Paranoid(failed()); master()->childFailed(childDl); } //______________________________________________________________________ // Finding the first [Image] section /* While scanning the tree of [Include]d .jigdo files, only the first [Image] section is relevant. IOW, we do a depth-first search of the tree. However, the .jigdo files are downloaded in parallel, and we want to pass on the image info as soon as possible. For this reason, we maintain an "image section candidate pointer", one for the whole include tree. If during the scanning of jigdo data we encounter an [Image] section AND imgSectCandidate()==this, then that section is the first such section in depth-first-order in the whole tree. If instead we encounter an [Include], the included file /might/ contain an image section, so we descend by setting imgSectCandidate() to the newly created child download. However, it can turn out the child does not actually contain an image section. In this case, we go back up to its parent. This is where it gets more complicated: Of course, the parent's data continued to be downloaded while we were wasting our time waiting for the last lines of the child, to be sure those last lines didn't contain an image section. After the point where we descended into the child, any number of [Include]s and /maybe/ an [Image] somewhere inbetween the [Include]s could have been downloaded. To find out whether this was the case, a quick depth-first scan of the tree is now necessary, up to the next point where we "hang" again because some .jigdo file has not been downloaded completely. The whole code is also used to find out when all JigdoIOs have finished - this could be done in simpler ways just by counting the active ones, but it comes "for free" with this code. */ // New child created due to [Include] in current .jigdo data void JigdoIO::imgSect_newChild(JigdoIO* child) { if (master()->finalState() || imgSectCandidate() != this) return; debug("imgSect_newChild%1: From %2:%3 to child %4", (master()->haveImageSection() ? "(haveImageSection)" : ""), urlVal, line, child->urlVal); setImgSectCandidate(child); } // An [Image] section just ended - maybe it was the first one? void JigdoIO::imgSect_parsed() { //debug("imgSect_parsed: %1 %2 %3", imgSectCandidate(), this, master()->finalState()); if (master()->finalState() || imgSectCandidate() != this) return; debug("imgSect_parsed%1: %2:%3", (master()->haveImageSection() ? "(haveImageSection)" : ""), urlVal, line - 1); if (master()->haveImageSection()) return; master()->setImageSection(&imageName, &imageInfo, &imageShortInfo, templateUrls.get(), &templateMd5); } #if DEBUG namespace { inline const char* have(MakeImageDl* master) { if (master->haveImageSection()) return "I"; else return " "; } } #endif // The end of the file was hit XStatus JigdoIO::imgSect_eof() { MakeImageDl* m = master(); if (m->finalState() || imgSectCandidate() != this) return OK; JigdoIO* x = parent; // Current position in tree int l = includeLine; // Line number in x, 0 if at start JigdoIO* child = this; // child included at line l of x, null if l==0 while (x != 0) { # if DEBUG const char* indentStr = " "; const char* indent = indentStr + 40; JigdoIO* ii = x; while (ii != 0) { indent -= 2; ii = ii->parent; } if (indent < indentStr) indent = indentStr; debug("imgSect_eof:%1%2Now at %3:%4", have(m), indent, x->urlVal, l); # endif JigdoIO* nextChild; if (l == 0) nextChild = x->firstChild; else nextChild = child->next; if (nextChild != 0) { /* Before moving l to the line of the next [Include], check whether the area of the file that l moves over contains an [Image] */ if (l < x->imageSectionLine && x->imageSectionLine < nextChild->includeLine) { debug("imgSect_eof:%1%2Found before [Include]", have(m), indent); if (!m->haveImageSection()) m->setImageSection(&x->imageName, &x->imageInfo, &x->imageShortInfo, x->templateUrls.get(), &x->templateMd5); } // No [Image] inbetween - move on, descend into [Include] debug("imgSect_eof:%1%2Now at %3:%4, descending", have(m), indent, x->urlVal, nextChild->includeLine); x = nextChild; l = 0; child = 0; continue; } // x has no more children - but maybe an [Image] at the end? if (l < x->imageSectionLine) { debug("imgSect_eof:%1%2Found after last [Include], if any", have(m), indent); if (!m->haveImageSection()) m->setImageSection(&x->imageName, &x->imageInfo, &x->imageShortInfo, x->templateUrls.get(), &x->templateMd5); } // Nothing found. If x not yet fully downloaded, stop here if (!x->finished()) { debug("imgSect_eof:%1%2Waiting for %3 to download", have(m), indent, x->urlVal); setImgSectCandidate(x); return OK; } // Nothing found and finished - go back up in tree debug("imgSect_eof:%1%2Now at end of %3, ascending", have(m), indent, x->urlVal); l = x->includeLine; child = x; x = x->parent; } if (m->haveImageSection()) { debug("imgSect_eof: Finished"); return XStatus(1); } else { generateError(_("No `[Image]' section found in .jigdo data")); return FAILED; } } //______________________________________________________________________ // New line of jigdo data arrived. This is similar to ConfigFile::rescan() void JigdoIO::jigdoLine(string* l) { //debug("\"%1\"", l); string s; s.swap(*l); if (failed()) return; ++line; string::const_iterator x = s.begin(), end = s.end(); // Empty line, or only contains '#' comment if (advanceWhitespace(x, end)) return; bool inComment = (section == "Comment" || section == "comment"); if (*x != '[') { // This is a "Label=Value" line if (inComment) return; string labelName; while (!isWhitespace(*x) && *x != '=') { labelName += *x; ++x; } if (advanceWhitespace(x, end) || *x != '=') return generateError(_("No `=' after first word")); ++x; // Skip '=' advanceWhitespace(x, end); entry(&labelName, &s, x - s.begin()); return; } //____________________ // This is a "[Section]" line if (sectionEnd().failed()) return; ++x; // Advance beyond the '[' if (advanceWhitespace(x, end)) // Skip space after '[' return generateError(_("No closing `]' for section name")); string::const_iterator s1 = x; // s1 points to start of section name while (x != end && *x != ']' && !isWhitespace(*x) && *x != '[' && *x != '=' && *x != '#') ++x; string::const_iterator s2 = x; // s2 points to end of section name if (advanceWhitespace(x, end)) return generateError(_("No closing `]' for section name")); section.assign(s1, s2); //debug("Section `%1'", section); // In special case of "Image", ignore 2nd and subsequent sections if (section == "Image") { if (imageSectionLine == 0) imageSectionLine = line; else section += "(ignored)"; } // In special case of "Include", format differs: URL after section name if (section == "Include") { string url; while (x != end && *x != ']') { url += *x; ++x; } int i = url.size(); while (i > 0 && isWhitespace(url[--i])) { } url.erase(i + 1); include(&url); } if (*x != ']') return generateError(_("Section name invalid")); ++x; // Advance beyond the ']' if (!advanceWhitespace(x, end)) return generateError(_("Invalid characters after closing `]'")); } //______________________________________________________________________ Status JigdoIO::sectionEnd() { if (section != "Image") return OK; // Section that just ended was [Image] const char* valueName = 0; if (templateMd5 == 0) valueName = "Template-MD5Sum"; if (templateUrls.isNull()) valueName = "Template"; if (imageName.empty()) valueName = "Filename"; if (valueName == 0) { imgSect_parsed(); return OK; } // Error: Not all required fields found --line; string s = subst(_("`%1=...' line missing in [Image] section"), valueName); generateError(s); return FAILED; } //______________________________________________________________________ // "[Include url]" found - add void JigdoIO::include(string* url) { string includeUrl; uriJoin(&includeUrl, urlVal, *url); debug("%1:[Include %2]", line, includeUrl); JigdoIO* p = this; do { //debug("include: Parent of %1 is %2", p->urlVal, // (p->parent ? p->parent->urlVal : "none")); if (p->urlVal == includeUrl) return generateError(_("Loop of [Include] directives")); p = p->parent; } while (p != 0); // childDl->source() is the source of the included .jigdo file's data auto_ptr childDl( master()->childFor(includeUrl)); if (childDl.get() != 0) { // ...and jio is the destination of above data JigdoIO* jio = new JigdoIO(childDl.get(), includeUrl, /*frontend.get(),*/ this, line); childDl->source()->io.addListener(*jio); // Add new child JigdoIO** jiop = &firstChild; while (*jiop != 0) jiop = &(*jiop)->next; *jiop = jio; imgSect_newChild(jio); (childDl.release())->source()->run(); } } //______________________________________________________________________ namespace { /** Local class: For Base64In - put decoded bytes into 16-byte array */ struct ArrayOut { typedef ArrayOut& ResultType; ArrayOut() { } void set(byte* array) { cur = array; end = array + 16; } void put(byte b) { if (cur == end) cur = end = 0; else *cur++ = b; } ArrayOut& result() { return *this; } byte* cur; byte* end; }; } //____________________ /* @param label Pointer to word before the '=' @param data Pointer to string containing whole input line @param valueOff Offset of value (part after '=') in data */ void JigdoIO::entry(string* label, string* data, unsigned valueOff) { vector value; ConfigFile::split(value, *data, valueOff); # if DEBUG string s; for (vector::iterator i = value.begin(), e = value.end(); i != e; ++i) { s += '>'; s += *i; s += "< "; } // { s += ConfigFile::quote(*i); s += ' '; } debug("%1:[%2] %3=%4", line, section, label, s); # endif //____________________ if (section == "Include") { return generateError(_("A new section must be started after [Include]")); //____________________ } else if (section == "Jigdo") { if (*label == "Version") { if (value.empty()) return generateError(_("Missing argument")); unsigned ver = 0; string::const_iterator i = value.front().begin(); string::const_iterator e = value.front().end(); while (i != e && *i >= '0' && *i <= '9') { ver = 10 * ver + *i - '0'; ++i; } if (ver > FILEFORMAT_MAJOR) return generateError(_("Upgrade required - this .jigdo file needs " "a newer version of the jigdo program")); } //____________________ } else if (section == "Image") { /* Only called for first [Image] section in file - for further sections, section=="Image(ignored)". Does some sanity checks on the supplied data. */ if (*label == "Filename") { if (!imageName.empty()) return generateError(_("Value redefined")); if (value.empty()) return generateError(_("Missing argument")); // Only use leaf name, ignore dirname delimiters, max 100 chars string::size_type lastSlash = value.front().rfind('/'); string::size_type lastSep = value.front().rfind(DIRSEP); if (lastSlash > lastSep) lastSep = lastSlash; imageName.assign(value.front(), lastSep + 1, 100); if (imageName.empty()) return generateError(_("Invalid image name")); } else if (*label == "Template") { if (value.empty()) return generateError(_("Missing argument")); master()->urlMap.addPart(urlVal, value, &templateUrls); } else if (*label == "Template-MD5Sum") { if (templateMd5 != 0) return generateError(_("Value redefined")); if (value.empty()) return generateError(_("Missing argument")); templateMd5 = new MD5(); // Helper class places decoded bytes into MD5 object Base64In decoder; decoder.result().set(templateMd5->sum); decoder << value.front(); if (decoder.result().cur == 0 || decoder.result().cur != decoder.result().end) { delete templateMd5; templateMd5 = 0; return generateError(_("Invalid Template-MD5Sum argument")); } // For security, double-check the value Base64String b64; b64.write(templateMd5->sum, 16).flush(); if (b64.result() != value.front()) { debug("b64='%1' value='%2'", b64.result(), value.front()); return generateError(_("Invalid Template-MD5Sum argument")); } } else if (*label == "ShortInfo") { // ShortInfo is 200 chars max if(!imageShortInfo.empty()) return generateError(_("Value redefined")); imageShortInfo.assign(*data, valueOff, 200); } else if (*label == "Info") { // ImageInfo is 5000 chars max if (!imageInfo.empty()) return generateError(_("Value redefined")); imageInfo.assign(*data, valueOff, 5000); } //____________________ } else if (section == "Parts") { if (value.empty()) return generateError(_("Missing argument")); MD5 md5; Base64In decoder; decoder.result().set(md5.sum); decoder << *label; if (decoder.result().cur == 0 || decoder.result().cur != decoder.result().end) { return generateError(_("Invalid MD5Sum in Parts section")); } // For security, double-check the value Base64String b64; b64.write(md5.sum, 16).flush(); if (b64.result() != *label) { debug("x b64='%1' value='%2'", b64.result(), *label); return generateError(_("Invalid MD5Sum in Parts section")); } //debug("PART %1 -> %2", md5.toString(), value.front()); master()->urlMap.addPart(urlVal, md5, value); } else if (section == "Servers") { if (value.empty()) return generateError(_("Missing argument")); const char* x = master()->urlMap.addServer(urlVal, *label, value); if (x != 0) return generateError(x); } // endif (section == "Something") } jigdo-0.7.3/src/job/jigdo-io.fh0000644000175000017500000000136007725440315016017 0ustar richardrichard/* $Id: jigdo-io.fh,v 1.3 2003/09/03 19:28:13 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. IO object for .jigdo downloads; download, gunzip, interpret Data (=downloaded bytes, status info) flows as follows: class: Download -> SingleUrl -> JigdoIO -> GtkSingleUrl data member: source this frontend The JigdoIO owns the SingleUrl (and the Download *object* inside it), but it doesn't own the GtkSingleUrl. */ namespace Job { class JigdoIO; } jigdo-0.7.3/src/job/jigdo-io.hh0000644000175000017500000001466210226060300016010 0ustar richardrichard/* $Id: jigdo-io.hh,v 1.16 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file IO object for .jigdo downloads; download, gunzip, interpret */ #ifndef JIGDO_IO_HH #define JIGDO_IO_HH #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ namespace Job { class JigdoIO; struct JigdoIOTest; } /** IO object for .jigdo downloads; download, gunzip, interpret */ class Job::JigdoIO : NoCopy, public Job::DataSource::IO, Gunzip::IO { public: /** Create a new JigdoIO which is owned by m, gets data from download (will register itself with download's IOPtr) and passes it on to childIo. @param c Object which owns us (it is the MakeImageDl's child, but our master) @param url URL of the .jigdo file */ JigdoIO(MakeImageDl::Child* c, const string& url/* IOPtr, DataSource::IO* frontendIo*/); ~JigdoIO(); inline MakeImageDl* master() const; inline DataSource* source() const; private: friend struct Job::JigdoIOTest; /* Create object for an [Include]d file */ JigdoIO(MakeImageDl::Child* c, const string& url, /*DataSource::IO* frontendIo,*/ JigdoIO* parentJigdo, int inclLine); /** @return Root object of the include tree */ inline JigdoIO* root(); inline const JigdoIO* root() const; /** @return true iff this object is the root of the include tree. */ inline bool isRoot() const; /** Return the ptr to the image section candidate object; the JigdoIO which might or might not contain the first [Image] section. If new data is received for that object and that new data contains an [Image], we know it's the first [Image]. If all data is recvd without any [Image] turning up, we continue walking the include tree depth-first. */ inline JigdoIO* imgSectCandidate() const; /** Set the ptr to the image section candidate object */ inline void setImgSectCandidate(JigdoIO* c); // The methods below are called in various places to find 1st image section inline void imgSect_newChild(JigdoIO* child); // Child created after [Incl. inline void imgSect_parsed(); // [Image] occurred in current .jigdo data /* End of current file without any [Image]. Returns 1 if OK and all JigdoIOs finished */ inline XStatus imgSect_eof(); // Create error message with URL and line number void generateError(const string& msg); void generateError(const char* msg); // As above, but directly pass on error string, do not add URL/line void generateError_plain(const string& err); // True after above was called inline bool failed() const; // Called by gunzip_data(): New .jigdo line ready. Arg is empty on exit. void jigdoLine(string* l); void include(string* url); // "[Include http://xxx]" found void entry(string* label, string* data, unsigned valueOff); /* Called at the end of a [Section] (=start of another section or EOF) Returns FAILURE if there is an error. */ Status sectionEnd(); // Virtual methods from DataSource::IO virtual void job_deleted(); virtual void job_succeeded(); virtual void job_failed(const string& message); virtual void job_message(const string& message); virtual void dataSource_dataSize(uint64 n); virtual void dataSource_data(const byte* data, unsigned size, uint64 currentSize); // Virtual methods from Gunzip::IO virtual void gunzip_deleted(); virtual void gunzip_data(Gunzip*, byte* decompressed, unsigned size); virtual void gunzip_needOut(Gunzip*); virtual void gunzip_failed(string* message); MakeImageDl::Child* childDl; string urlVal; // Absolute URL of this .jigdo file /* Representation of the tree of [Include] directives. Most of the time, the order of data in the .jigdo files is not relevant, with one exception: We must interpret the first [Image] section only, and ignore all following ones. */ JigdoIO* parent; // .jigdo file which [Include]d us, or null if top-level int includeLine; // If parent!=null, line num of [Include] in parent JigdoIO* firstChild; // First file we [Include], or null if none JigdoIO* next; // Right sibling, or null if none /* For the root object, contains imgSectCandidate, else ptr to root object. Don't access directly, use accessor methods. */ JigdoIO* rootAndImageSectionCandidate; int line; // Line number, for errors. 0 if no data yet, -1 if finished bool finished() { return line < 0; } void setFinished() { line = -1; } string section; // Current section name, empty if none yet // Info about first image section of this .jigdo, if any int imageSectionLine; // 0 if no [Image] found yet string imageName; string imageInfo, imageShortInfo; SmartPtr templateUrls; // Can contain a list of altern. URLs MD5* templateMd5; /* Transparent gunzipping of .jigdo file. GUNZIP_BUF_SIZE is also the max size a single line in the .jigdo is allowed to have */ static const unsigned GUNZIP_BUF_SIZE = 16384; Gunzip gunzip; byte gunzipBuf[GUNZIP_BUF_SIZE]; }; //______________________________________________________________________ Job::JigdoIO* Job::JigdoIO::root() { if (isRoot()) return this; else return rootAndImageSectionCandidate; } const Job::JigdoIO* Job::JigdoIO::root() const { if (isRoot()) return this; else return rootAndImageSectionCandidate; } bool Job::JigdoIO::isRoot() const { return parent == 0; } Job::JigdoIO* Job::JigdoIO::imgSectCandidate() const { if (isRoot()) return rootAndImageSectionCandidate; else return rootAndImageSectionCandidate->rootAndImageSectionCandidate; } void Job::JigdoIO::setImgSectCandidate(JigdoIO* c) { if (isRoot()) rootAndImageSectionCandidate = c; else rootAndImageSectionCandidate->rootAndImageSectionCandidate = c; } Job::MakeImageDl* Job::JigdoIO::master() const { return childDl->master(); } Job::DataSource* Job::JigdoIO::source() const { return childDl->source(); } bool Job::JigdoIO::failed() const { return (imageName.length() == 1 && imageName[0] == '\0'); //return (childFailedId != 0); } #endif jigdo-0.7.3/src/job/jigdodownload.cc0000644000175000017500000001352110120704646017125 0ustar richardrichard/* $Id: jigdodownload.cc,v 1.9 2004/09/11 23:26:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. JigdoDownload is a private nested class of MakeImageDl. Download .jigdo file. If the data that is fetched from the primary .jigdo URL contains [Include] directives, additional JigdoDownloads will be started for each one. */ #include #include #include #include using namespace Job; //______________________________________________________________________ DEBUG_UNIT("jigdodownload") MakeImageDl::JigdoDownload::JigdoDownload(MakeImageDl* m, JigdoDownload* p, const string& jigdoUrl, ConfigFile::iterator destPos) : SingleUrl(this, jigdoUrl), master(m), parent(p), ioVal(0), gunzipBuf(), gunzip(this), insertPos(destPos) { ioVal.set(master->io->makeImageDl_new(this)); } MakeImageDl::JigdoDownload::~JigdoDownload() { master->io->makeImageDl_finished(this); if (master->jigdo == this) master->jigdo = 0; SingleUrl::io().set(0); } void MakeImageDl::JigdoDownload::run() { SingleUrl::run(0, 0, 0, 0, false); } //______________________________________________________________________ IOPtr& MakeImageDl::JigdoDownload::io() { return ioVal; } const IOPtr& MakeImageDl::JigdoDownload::io() const { return ioVal; } //______________________________________________________________________ void MakeImageDl::JigdoDownload::job_deleted() { if (ioVal) ioVal->job_deleted(); } void MakeImageDl::JigdoDownload::job_succeeded() { if (ioVal) ioVal->job_succeeded(); Assert(master->state() == DOWNLOADING_JIGDO); master->stateVal = DOWNLOADING_TEMPLATE; delete this; } void MakeImageDl::JigdoDownload::job_failed(string* message) { if (ioVal) ioVal->job_failed(message); } void MakeImageDl::JigdoDownload::job_message(string* message) { if (ioVal) ioVal->job_message(message); } void MakeImageDl::JigdoDownload::dataSource_dataSize(uint64 n) { if (ioVal) ioVal->dataSource_dataSize(n); } void MakeImageDl::JigdoDownload::dataSource_data(const byte* data, unsigned size, uint64 currentSize) { if (master->state() == ERROR) return; Assert(master->state() == DOWNLOADING_JIGDO); debug("Got %1 bytes", size); try { gunzip.inject(data, size); } catch (Error e) { master->io->job_failed(&e.message); master->stateVal = ERROR; return; } if (ioVal) ioVal->dataSource_data(data, size, currentSize); } //______________________________________________________________________ void MakeImageDl::JigdoDownload::gunzip_deleted() { } void MakeImageDl::JigdoDownload::gunzip_needOut(Gunzip*) { /* This is only called once, at the very start - afterwards, we always call setOut() from gunzip_data, so Gunzip won't call this. */ gunzip.setOut(gunzipBuf, GUNZIP_BUF_SIZE); } /* Uncompressed data arrives. "decompressed" points somewhere inside gunzipBuf. Split data apart at \n and add lines to the ConfigFile, then copy any remaining unfinished line to the start of gunzipBuf. The first byte of gunzipBuf (if it contains valid data) is always the first char of a line in the config file. */ void MakeImageDl::JigdoDownload::gunzip_data(Gunzip*, byte* decompressed, unsigned size) { // // If an error happened earlier, ignore this call to gunzip_data() // if (gunzipBuf == 0) return; // Look for end of line. byte* p = decompressed; const byte* end = decompressed + size; const byte* stringStart = gunzipBuf; string line; while (p < end) { if (*p == '\n') { // Add new line to ConfigFile Paranoid(static_cast(p - stringStart) <= GUNZIP_BUF_SIZE); Paranoid(line.empty()); const char* lineChars = reinterpret_cast(stringStart); if (g_utf8_validate(lineChars, p - stringStart, NULL) != TRUE) throw Error(_("Input .jigdo data is not valid UTF-8")); line.append(lineChars, p - stringStart); debug("jigdo line: `%1'", line); configFile().push_back(); swap(configFile().back(), line); ++p; stringStart = p; continue; } if (*p == '\r') *p = ' '; // Allow Windows-style line endings by turning CR into space else if (*p == 127 || (*p < 32 && *p != '\t')) // Check for evil chars throw Error(_("Input .jigdo data contains invalid control characters")); ++p; } if (stringStart == gunzipBuf && p == stringStart + GUNZIP_BUF_SIZE) { // A single line fills the whole buffer. Truncate it at that length. debug("gunzip_data: long line"); Paranoid(line.empty()); const char* lineChars = reinterpret_cast(stringStart); if (g_utf8_validate(lineChars, p - stringStart, NULL) != TRUE) throw Error(_("Input .jigdo data is not valid UTF-8")); line.append(lineChars, p - stringStart); debug("jigdo line: \"%1\"", line); configFile().push_back(); swap(configFile().back(), line); // Trick: To ignore remainder of huge line, prepend a comment char '#' gunzipBuf[0] = '#'; gunzip.setOut(gunzipBuf + 1, GUNZIP_BUF_SIZE - 1); return; } unsigned len = p - stringStart; if (len > 0 && stringStart > gunzipBuf) { // Unprocessed data left somewhere inside the buffer - copy to buf start Assert(len < GUNZIP_BUF_SIZE); // Room must be left in the buffer memmove(gunzipBuf, stringStart, len); } gunzip.setOut(gunzipBuf + len, GUNZIP_BUF_SIZE - len); } void MakeImageDl::JigdoDownload::gunzip_failed(string* message) { throw Error(*message, true); } jigdo-0.7.3/src/job/jigdodownload.hh0000644000175000017500000000626110262207206017137 0ustar richardrichard/* $Id: jigdodownload.hh,v 1.11 2005/07/04 10:25:10 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Download .jigdo data, take care of handling [Include] directives. */ #ifndef JIGDODOWNLOAD_HH #define JIGDODOWNLOAD_HH #include #include //______________________________________________________________________ // PRIVATE stuff, internal to MakeImageDl /** Private class of MakeImageDl: Object extending a SingleUrl and used to retrieve the data of the .jigdo file. NB: The underlying Download's io pointer is set up to point to this object. Once this receives the calls, this object passes them on to its own io pointer, which will point to the corresponding front-end object, e.g. a GtkSingleUrl. This sandwiching is in contrast to "normal" single-file downloads, where the Download directly calls the GtkSingleUrl. */ class Job::MakeImageDl::JigdoDownload : SingleUrl, // This object is a special kind of SingleUrl DataSource::IO, Gunzip::IO { public: /** @param m Master which owns us @param p Parent JigdoDownload which [Include]d us, or null @param jigdoUrl Where to download .jigdo data @param destPos Where in config file to insert downloaded data */ JigdoDownload(MakeImageDl* m, JigdoDownload* p, const string& jigdoUrl, ConfigFile::iterator destPos); virtual ~JigdoDownload(); void run(); /** Access the correct io member, i.e. for the derived class. */ virtual IOPtr& io(); virtual const IOPtr& io() const; private: /** @name Methods from SingleUrl::IO */ //@{ virtual void job_deleted(); virtual void job_succeeded(); virtual void job_failed(string* message); virtual void job_message(string* message); virtual void dataSource_dataSize(uint64 n); virtual void dataSource_data(const byte* data, unsigned size, uint64 currentSize); //@} // Virtual methods from Gunzip::IO virtual void gunzip_deleted(); virtual void gunzip_data(Gunzip*, byte* decompressed, unsigned size); virtual void gunzip_needOut(Gunzip*); virtual void gunzip_failed(string* message); // Convenience helper function inline ConfigFile& configFile() const; MakeImageDl* master; // Ptr to the MakeImageDl which owns us JigdoDownload* parent; // Ptr to the download which [Include]d us, or null // IO for this SingleUrl, given by master. Points to e.g. a GtkSingleUrl IOPtr ioVal; /* Transparent gunzipping of .jigdo file. GUNZIP_BUF_SIZE is also the max size a single line in the .jigdo is allowed to have */ static const unsigned GUNZIP_BUF_SIZE = 16384; byte gunzipBuf[GUNZIP_BUF_SIZE]; Gunzip gunzip; // Where to put .jigdo data. Points somewhere inside configFile() ConfigFile::iterator insertPos; }; ConfigFile& Job::MakeImageDl::JigdoDownload::configFile() const { return master->mi.configFile(); } #endif jigdo-0.7.3/src/job/job.hh0000644000175000017500000001360110226060300015051 0ustar richardrichard/* $Id: job.hh,v 1.9 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file A job is a certain task - just the application logic, *no* user interface. All interaction with the rest of the system (input/output, user interaction) happens via an IO object. */ #ifndef JOB_HH #define JOB_HH #include #include #include #include //______________________________________________________________________ namespace Job { class IO; } //______________________________________________________________________ //#define _DEPRECATED __attribute__((deprecated)) //#define _DEPRECATED /** Base class for interaction between the outside world and the job. For example, depending on the IO object you register with a job, you can control the job via a gtk app or from within a command line utility. An IO class is implemented by anyone interested in the information, and an instance registered with IOSource::addListener(), which appends a pointer to the instance to its list of listening objects. If the listener is deleted, it is *automatically* removed from the list it is on. The messages are always in valid UTF-8. Their text is *never* "quoted", e.g. "<" is not replaced with "<". Neither do they contain any markup. The names of all methods here start with "job_". If a child class Job::SomeClass::IO adds any further methods, their name starts with "someClass_". This makes it easy to see which methods are introduced where. */ class Job::IO : public IListBase { public: virtual ~IO() { } /** Remove yourself from the IOSource you are listening to, if any. */ void removeListener() { iList_remove(); } /** Called by the IOSource when it is deleted or when a different IO object is registered with it. If the IO object considers itself owned by its job, it can delete itself. */ virtual void job_deleted() = 0; /** Called when the job has successfully completed its task. */ virtual void job_succeeded() = 0; /** Called when the job fails. The only remaining sensible action after getting this is probably to delete the job object. */ virtual void job_failed(const string& message) = 0; /** Informational message. */ virtual void job_message(const string& message) = 0; }; //______________________________________________________________________ // For IOSource io, use // IOSOURCE_SEND(SomeIO, io, job_failed, ("it failed")); // It is OK if any called object deletes itself in response to the call #define IOSOURCE_SEND(_ioClass, _ioObj, _functionName, _args) \ do { \ IList<_ioClass>& _listeners = (_ioObj).listeners(); \ IList<_ioClass>::iterator _i = _listeners.begin(), _e = _listeners.end(); \ while (_i != _e) { \ _ioClass* _listObj = &*_i; ++_i; _listObj->_functionName _args; \ } \ } while (false) // Same thing, but const: #define IOSOURCE_SENDc(_ioClass, _ioObj, _functionName, _args) \ do { \ const IList<_ioClass>& _listeners = (_ioObj).listeners(); \ IList<_ioClass>::const_iterator _i = _listeners.begin(), \ _e = _listeners.end(); \ while (_i != _e) { \ _ioClass* _listObj = &*_i; ++_i; _listObj->_functionName _args; \ } \ } while (false) // Same thing, but for template function #define IOSOURCE_SENDt(_ioClass, _ioObj, _functionName, _args) \ do { \ IList<_ioClass>& _listeners = (_ioObj).listeners(); \ typename IList<_ioClass>::iterator _i = _listeners.begin(), \ _e = _listeners.end(); \ while (_i != _e) { \ _ioClass* _listObj = &*_i; ++_i; _listObj->_functionName _args; \ } \ } while (false) //________________________________________ /** In your job class, use the template to generate a public member:


    class MyJob {
    public:
      class IO { ... };
      IOSource io;
      MyJob(IO* ioPtr, ...) : io(), ... { io.addListener(ioPtr); ... }
    };

    
*/ template class IOSource : NoCopy { public: IOSource() : list() { } /** Does not delete the listeners. */ ~IOSource() { IOSOURCE_SENDt(SomeIO, *this, job_deleted, ()); } /** Add an IO object which listens to calls going via this IOSource. NB, this adds l to the *front* of the list, so it will be called before listeners that were registered earlier. */ void addListener(SomeIO& l) { Assert(&l != 0); list.push_front(l); } const IList& listeners() const { return list; }; IList& listeners() { return list; }; bool empty() const { return list.empty(); } private: IList list; }; //______________________________________________________________________ #if 0 template class IOPtr { public: inline IOPtr(SomeIO* io) _DEPRECATED; ~IOPtr() { if (ptr != 0) ptr->job_deleted(); } SomeIO& operator*() { return *ptr; } SomeIO* operator->() { return ptr; } operator bool() { return ptr != 0; } SomeIO* get() { return ptr; } /** Set up pointer to IO object. Must not already have been set up. */ void set(SomeIO* io) { Paranoid(ptr == 0); ptr = io; } /** Remove pointer from chain of IO objects. */ void remove(SomeIO* rmIo) { Paranoid(ptr != 0); Job::IO* newPtr = ptr->job_removeIo(rmIo); // Upcast is OK if the job_removeIo() implementation plays by the rules Paranoid(newPtr == 0 || dynamic_cast(newPtr) != 0); ptr = static_cast(newPtr); } /** Like set(0), but doesn't call the IO object's job_deleted() method */ void release() { ptr = 0; } private: SomeIO* ptr; }; template IOPtr::IOPtr(SomeIO* io) : ptr(io) { } #endif #endif jigdo-0.7.3/src/job/makeimage.cc0000644000175000017500000000113107701377740016227 0ustar richardrichard/* $Id: makeimage.cc,v 1.1.1.1 2003/07/04 22:29:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download & interpret .jigdo, download parts, assemble image */ #include #include #include #include #include //______________________________________________________________________ jigdo-0.7.3/src/job/makeimage.hh0000644000175000017500000001276110226060300016225 0ustar richardrichard/* $Id: makeimage.hh,v 1.10 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Download (via SingleURL) & interpret .jigdo data, download parts, assemble image */ #ifndef MAKEIMAGE_HH #define MAKEIMAGE_HH #include #include #include #include //______________________________________________________________________ /** Download & interpret .jigdo, download parts, assemble image. MakeImage is the "core" class, other components use it for part of the work, e.g. MakeImageDl uses SingleUrls for file downloads, then passes the data to MakeImage. Below, arrow "A->B" means "A uses B"

    GtkMakeImage(GTK GUI)  CursesMakeImage(curses GUI)[0]
                   |        |
                   V        V
                  MakeImageDl  "jigdo-file make-image"[1]
                           |     |
                           V     V
                          MakeImage

    [0] Curses GUI non-existent so far
    [1] ATM, jigdo-file uses its own implementation of MakeImage's
        functionality, TODO fix that.
    
MakeImage: Everything for turning many data sources into one final image
  • Maintains .jigdo file contents, but does not download the .jigdo data - someone else must do this and pass the file info to MakeImage.
  • Stores name of .template file once present, opens & reads it.
  • Access to list of MD5 sums which are still needed to complete the image, i.e. list of parts left to download.
  • Lookup of MD5 sum, gives list of URLs for file, with indication WRT which server is preferred by the user.
  • Stores name of output image, creates image and writes to it.
  • You can give it chunks of downloaded data from *any* part, it'll write the output image as far as necessary (with zero areas for as yet non-downloaded data) and write out the downloaded data.
  • For resuming partial downloads of files in the image, can query how many bytes of the data for a part have been passed to MakeImage, and request some of those bytes for the checks of an overlapping resume.
  • Non-blocking operation for .template unpacking: If it needs to write lots of data to disc, will not do this during one long-lasting call to it, but will queue the disc-intensive request and expect you to call it back whenever it should do the next chunk of work.
  • While a disc request is active, downloaded data from any part can still be given to MakeImage. If the respective section of the image has already been written out (filled with zeroes), writes the data to disc. Otherwise, buffers the data. It'll indicate when the amount of buffered data exceeds a certain limit (=> the downloader *should* pause downloading), but will continue to accept and buffer further data.
  • Blocking operation when writing downloaded data: Usually, downloaded data of parts is not buffered in memory. In this case, will always write out the complete supplied data to disc in one go, regardless of the size of the downloaded chunk of data.
MakeImageDl: Everything related to downloads
  • Downloads and interprets .jigdo file contents
  • Downloads .template via SingleURL, notifies MakeImage once done
  • "Owner" of the layout of the temporary directory, only component that directly makes modifications to this dir. (MakeImage only writes to the image file.)
  • Does simple cache management; if requested file already downloaded, immediately returns its data, or does an If-Modified-Since request; if partially downloaded, resumes.
  • Starts further SingleURLs for download of individual parts.
  • Automatic server selection: For servers which were rated equally acceptable by the user, measures their speed, then prefers the faster ones (but does not completely stop using the slower ones).
GtkMakeImage: Is notified by MakeImageDl when anything interesting happens, updates GTK+ widgets. Also pauses, continues etc. the MakeImageDl. */ class MakeImage : NoCopy { public: /** jigdoFile argument is only used for displaying error messages when scanning the .jigdo file contents. TODO param destDir destination directory that the final image should be written to. Initially, MakeImage will create a temporary dir (name based on jigdoFile leafname) to store administrative data in. */ inline explicit MakeImage(); inline ~MakeImage(); /** Set where to report JigdoConfig errors, overwriting any value passed as jigdoErrors to the ctor. */ // inline void setReporter(JigdoConfig::ProgressReporter* jigdoErrors); /** Public data member: Contents of .jigdo file. The component using this MakeImage should retrieve the .jigdo data and add it here with configFile().push_back(line) for each line of the file. Call config.rescan() when done. */ // JigdoConfig config; // ConfigFile& configFile() { return config.configFile(); } }; //______________________________________________________________________ MakeImage::MakeImage() { } MakeImage::~MakeImage() { } #endif jigdo-0.7.3/src/job/makeimagedl-info-test.cc0000644000175000017500000001335510120704646020454 0ustar richardrichard/* $Id: makeimagedl-info-test.cc,v 1.7 2004/09/11 23:26:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. #test-deps job/makeimagedl-info.o net/uri.o */ #include #include #include #include #include #include //______________________________________________________________________ using namespace Job; MakeImageDl::MakeImageDl(/*IO* ioPtr,*/ const string& jigdoUri, const string& destination) : io(/*ioPtr*/), stateVal(DOWNLOADING_JIGDO), jigdoUrl(jigdoUri), childrenVal(), dest(destination), tmpDirVal(), mi(), imageNameVal(), imageInfoVal(), imageShortInfoVal(), templateUrls(0), templateMd5Val(0) { } Job::MakeImageDl::~MakeImageDl() { } void MakeImageDl::setImageSection(string* imageName, string* imageInfo, string* imageShortInfo, PartUrlMapping* templateUrls, MD5** templateMd5) { msg("setImageSection"); Paranoid(!haveImageSection()); imageNameVal.swap(*imageName); imageInfoVal.swap(*imageInfo); imageShortInfoVal.swap(*imageShortInfo); this->templateUrls = templateUrls; templateMd5Val = *templateMd5; *templateMd5 = 0; IOSOURCE_SEND(IO, io, makeImageDl_haveImageSection, ()); } //====================================================================== namespace { const char* const hexDigits = "0123456789abcdef"; void escapedChar(string* o, byte c) { switch (c) { case 0: *o += "\\0"; break; case '\n': *o += "\\n"; break; case '\t': *o += "\\t"; break; case '"': case '\\': *o += '\\'; *o += c; break; default: if (c >= ' ' && c <= '~') { *o += c; } else { *o += "\\x"; *o += hexDigits[unsigned(c) >> 4]; *o += hexDigits[unsigned(c) & 0xfU]; } } } inline string escapedString(const string& s) { string result; for (unsigned i = 0; i < s.length(); ++i) escapedChar(&result, s[i]); return result; } } //______________________________________________________________________ string testImageInfo(const char* subst[], bool escapedText, const char* text, const char* expected) { string imageShortInfo; PartUrlMapping* templateUrls = 0; MD5* templateMd5 = 0; MakeImageDl m("http://url/", ""); string imageName = "image.iso"; string imageInfo = text; m.setImageSection(&imageName, &imageInfo, &imageShortInfo, templateUrls, &templateMd5); string info = " "; m.imageInfo(&info, escapedText, subst); msg("\"%1\"", escapedString(info)); Assert(info == expected); return info; } //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); const char* gtk[] = { "", "", // , "", "", // , "", "", // , "", "", // , "", "", // , "", "", // , "\n" //
}; const char* tex[] = { "\\textbf{", "}", // , "\\textit{", "}", // , "\\texttt{", "}", // , "\\underline{", "}", // , "\\large{", "}", // , "\\small{", "}", // , "\\\\" //
}; testImageInfo(gtk, true, "'Sun, <16> \"Mar 2003 & 04:45:40 -0700'", " 'Sun, <16> \"Mar 2003 & 04:45:40 -0700'"); testImageInfo(tex, false, "'Sun, <16> \"Mar 2003 & 04:45:40 -0700'", " 'Sun, <16> \"Mar 2003 & 04:45:40 -0700'"); testImageInfo(gtk, true, "Let him who


" "hath understanding reckon", " Let him " "who\n\nhath " "understanding reckon"); testImageInfo(tex, false, "Let him who


" "hath understanding reckon", " \\textbf{Let} \\large{him} \\small{who}\\\\\\\\" "\\textit{hath} \\texttt{understanding} \\underline{reckon}"); testImageInfo(gtk, true, "br must be empty

", " br must be empty <br> </br>"); testImageInfo(tex, false, "Ugh, does no good", " Ugh, does no good"); testImageInfo(gtk, true, "Now let mee
ee
entertain
you
", " Now let mee\nee " "entertain you"); testImageInfo(tex, false, "x", " x"); testImageInfo(gtk, true, "nobold", " <b x=\"\">nobold</b>"); testImageInfo(gtk, true, "nocomment", " nocomment<!-- x -->"); /* CDATA sections are supported by glib. However, our parseComment() creates an error if it sees one, because glib doesn't strip the "" strings - not useful! */ testImageInfo(gtk, true, "is & quoted]]>", " <b><![CDATA[This <i>is</i> & " "quoted]]></b>"); // " This <i>is</i> & quoted" return 0; } jigdo-0.7.3/src/job/makeimagedl-info.cc0000644000175000017500000001424710010210420017454 0ustar richardrichard/* $Id: makeimagedl-info.cc,v 1.4 2004/02/04 15:34:40 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download .jigdo/.template and file URLs */ #include #include #include #include //______________________________________________________________________ using namespace Job; DEBUG_UNIT("makeimagedl-info") //______________________________________________________________________ namespace { struct ImageInfoParse { ImageInfoParse(string* o, bool e, const char** s) : output(o), escapedText(e), subst(s), depth(0) { } string* output; bool escapedText; const char** subst; int depth; }; // For ImageInfo - verify that the XML is correct void parseStartElem(GMarkupParseContext* /*context*/, const gchar* elem, const gchar** attrNames, const gchar** /*attrValues*/, gpointer user_data, GError** error) { debug("imageInfo parse: <%1>", elem); ImageInfoParse* x = static_cast(user_data); ++x->depth; if (attrNames != 0 && attrNames[0] != 0) { g_set_error(error, g_markup_error_quark(), G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, " "); return; } if (strcmp("x", elem) == 0 && x->depth == 1) return; // Ignore dummy outer element const char* s; if (strcmp("b", elem) == 0) s = x->subst[MakeImageDl::B]; else if (strcmp("i", elem) == 0) s = x->subst[MakeImageDl::I]; else if (strcmp("tt", elem) == 0) s = x->subst[MakeImageDl::TT]; else if (strcmp("u", elem) == 0) s = x->subst[MakeImageDl::U]; else if (strcmp("big", elem) == 0) s = x->subst[MakeImageDl::BIG]; else if (strcmp("small", elem) == 0) s = x->subst[MakeImageDl::SMALL]; else if (strcmp("br", elem) == 0) s = x->subst[MakeImageDl::BR]; else { g_set_error(error, g_markup_error_quark(), G_MARKUP_ERROR_UNKNOWN_ELEMENT, " "); return; } *x->output += s; } void parseEndElem(GMarkupParseContext* /*context*/, const gchar* elem, gpointer user_data, GError** error) { debug("imageInfo parse: ", elem); ImageInfoParse* x = static_cast(user_data); --x->depth; if (strcmp("x", elem) == 0 && x->depth == 0) return; // Ignore dummy outer element if (strcmp("br", elem) == 0) return; const char* s; if (strcmp("b", elem) == 0) s = x->subst[MakeImageDl::B_]; else if (strcmp("i", elem) == 0) s = x->subst[MakeImageDl::I_]; else if (strcmp("tt", elem) == 0) s = x->subst[MakeImageDl::TT_]; else if (strcmp("u", elem) == 0) s = x->subst[MakeImageDl::U_]; else if (strcmp("big", elem) == 0) s = x->subst[MakeImageDl::BIG_]; else if (strcmp("small", elem) == 0) s = x->subst[MakeImageDl::SMALL_]; else { g_set_error(error, g_markup_error_quark(), G_MARKUP_ERROR_UNKNOWN_ELEMENT, " "); return; } *x->output += s; } void parseText(GMarkupParseContext* context, const gchar* text, gsize textLen, gpointer user_data, GError** error) { ImageInfoParse* x = static_cast(user_data); debug("imageInfo parseText: \"%1\"", string(text, textLen)); if (textLen == 0) return; if (strcmp(g_markup_parse_context_get_element(context), "br") == 0) { //
element must be empty g_set_error(error, g_markup_error_quark(), G_MARKUP_ERROR_INVALID_CONTENT, " "); return; } if (x->escapedText) { x->output->append(text, textLen); // Leave escaped } else { // Unescape. NB UTF-8 const char* p = text; const char* end = text + textLen; while (p < end) { if (*p != '&') { *x->output += *p; ++p; continue; } if (p + 4 <= end && strncmp(p + 1, "lt;", 3) == 0) { *x->output += '<'; p += 4; continue; } if (p + 4 <= end && strncmp(p + 1, "gt;", 3) == 0) { *x->output += '>'; p += 4; continue; } if (p + 5 <= end && strncmp(p + 1, "amp;", 4) == 0) { *x->output += '&'; p += 5; continue; } *x->output += *p; ++p; } } } // Comments, processing instructions and CDATA not allowed void parseComment(GMarkupParseContext* /*context*/, const gchar* /*passthrough_text*/, gsize /*textLen*/, gpointer /*user_data*/, GError** error) { debug("imageInfo parseComment"); g_set_error(error, g_markup_error_quark(), G_MARKUP_ERROR_INVALID_CONTENT, " "); } } // namespace void MakeImageDl::imageInfo(string* output, bool escapedText, const char* subst[13]) const { Paranoid(output != 0 && subst != 0); unsigned outputLen = output->length(); ImageInfoParse myData(output, escapedText, subst); static GMarkupParser p = { &parseStartElem, &parseEndElem, &parseText, &parseComment, 0 }; GMarkupParseContext* context = g_markup_parse_context_new(&p, (GMarkupParseFlags)0, &myData, 0); GError* err = 0; if (g_markup_parse_context_parse(context, "", 3, &err) && g_markup_parse_context_parse(context, imageInfoVal.data(), imageInfoVal.length(), &err) && g_markup_parse_context_parse(context, "", 4, &err) && g_markup_parse_context_end_parse(context, &err)) { } g_markup_parse_context_free(context); if (err != 0) { g_error_free(err); output->resize(outputLen); if (escapedText) { debug("imageInfo: error, escaping whole ImageInfo (%1)", err->code); for (string::const_iterator i = imageInfoVal.begin(), e = imageInfoVal.end(); i != e; ++i) { if (*i == '<') *output += "<"; else if (*i == '>') *output += ">"; else if (*i == '&') *output += "&"; else *output += *i; } } else { *output += imageInfoVal; } } } jigdo-0.7.3/src/job/makeimagedl.cc0000644000175000017500000004745510263052752016560 0ustar richardrichard/* $Id: makeimagedl.cc,v 1.30 2005/07/06 22:06:34 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download .jigdo/.template and file URLs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ using namespace Job; DEBUG_UNIT("makeimagedl") namespace { /* Normally, when an URL is already in the cache, we send out an If-Modified-Since request to check whether it has changed on the server. Exception: If cache entry was created no longer than this many seconds ago, do *not* send any request, just assume it has not changed on the server. */ const time_t CACHE_ENTRY_AGE_NOIFMODSINCE = 5; /* Name prefix to use when creating temporary directory. A checksum of the source .jigdo URL will be appended. */ const char* const TMPDIR_PREFIX = "jigdo-"; } MakeImageDl::MakeImageDl(/*IO* ioPtr,*/ const string& jigdoUri, const string& destination) : io(/*ioPtr*/), stateVal(DOWNLOADING_JIGDO), jigdoUrl(jigdoUri), jigdoIo(0), childrenVal(), dest(destination), tmpDirVal(), mi(), imageNameVal(), imageInfoVal(), imageShortInfoVal(), templateUrls(0), templateMd5Val(0), callbackId(0) { // Remove all trailing '/' from dest dir, even if result empty unsigned destLen = dest.length(); while (destLen > 0 && dest[destLen - 1] == DIRSEP) --destLen; dest.resize(destLen); /* Create name of temporary dir. Directory name: "jigdo-" followed by md5sum of .jigdo URL */ MD5Sum md; md.update(reinterpret_cast(jigdoUri.data()), jigdoUri.length()).finish(); Base64String b64; b64.result() = dest; b64.result() += DIRSEP; b64.result() += TMPDIR_PREFIX; // Halve number of bits by XORing bytes. (Would a real CRC64 be better??) byte cksum[8]; const byte* digest = md.digest(); for (int i = 0; i < 8; ++i) cksum[i] = digest[i] ^ digest[i + 8]; b64.write(cksum, 8).flush(); tmpDirVal.swap(b64.result()); } //______________________________________________________________________ Job::MakeImageDl::~MakeImageDl() { debug("~MakeImageDl"); if (callbackId != 0) g_source_remove(callbackId); killAllChildren(); delete jigdoIo; delete templateMd5Val; } //______________________________________________________________________ void Job::MakeImageDl::killAllChildren() { // Delete all our children. NB ~Child will remove child from the list while (!childrenVal.empty()) { Child* x = childrenVal.front().get(); # if DEBUG /* childFailed() or childSucceeded() MUST always be called for a Child. Exception: May delete the MakeImageDl without calling this for its children. */ if (!x->childSuccFail) debug("childFailed()/Succeeded() not called for %1", x->source() ? x->source()->location() : "[deleted source]"); x->childSuccFail = true; // Avoid failed assert # endif debug("~MakeImageDl: delete %1", x); delete x; } } //______________________________________________________________________ void MakeImageDl::run() { Assert(!finalState()); // Now create tmpdir int status = compat_mkdir(tmpDir().c_str()); if (status != 0) { if (errno == EEXIST) { // FIXME: Resume if EEXIST msg("RESUME NOT YET SUPPORTED, SORRY"); } else { string error = subst(_("Could not create temporary directory: " "%L1"), strerror(errno)); generateError(error); return; } } writeReadMe(); createJigdoDownload(); } //______________________________________________________________________ void MakeImageDl::writeReadMe() { string readmeName = tmpDir(); readmeName += DIRSEP; readmeName += "ReadMe.txt"; ofstream f(readmeName.c_str()); f << subst(_( "Jigsaw Download - half-finished download\n" "\n" "This directory contains the data for a half-finished download of a\n" ".jigdo file. Do not change or delete any of the files in this\n" "directory! (Of course you can delete the entire directory if you do\n" "not want to continue with the download.)\n" "\n" "If the jigdo application was stopped and you want it to resume this\n" "download, simply enter again the same values you used the first time.\n" "\n" "In the \"URL\" field, enter:\n" " %1\n" "\n" "In the \"Save to\" field, enter the parent directory of the directory\n" "containing this file. Unless you have moved it around, the correct\n" "value is:\n" " %2\n" "\n" "\n" "[Download]\n" "URL=%1\n" "Destination=%2\n" ), jigdoUri(), dest); } //______________________________________________________________________ /* Return filename for content md5sum cache entry: "/home/x/jigdo-blasejfwe/c-nGJ2hQpUNCIZ0fafwQxZmQ" */ string MakeImageDl::cachePathnameContent(const MD5& md, string* leafnameOut, bool isFinished, bool isC) { Base64String x; x.result() = tmpDir(); x.result() += DIRSEP; if (isC) x.result() += 'c'; else x.result() += 'u'; if (isFinished) x.result() += '-'; else x.result() += '~'; x.write(md, 16); x.flush(); if (leafnameOut != 0) leafnameOut->assign(x.result(), tmpDir().length() + 1, string::npos); return x.result(); } /* Return filename for URL cache entry: "/home/x/jigdo-blasejfwe/u-nGJ2hQpUNCIZ0fafwQxZmQ" */ string MakeImageDl::cachePathnameUrl(const string& url, string* leafnameOut, bool isFinished) { Base64String x; x.result() = tmpDir(); x.result() += DIRSEP; x.result() += 'u'; if (isFinished) x.result() += '-'; else x.result() += '~'; static MD5Sum nameMd; nameMd.reset() .update(reinterpret_cast(url.data()), url.length()) .finishForReuse(); x.write(nameMd.digest(), 16); x.flush(); if (leafnameOut != 0) leafnameOut->assign(x.result(), tmpDir().length() + 1, string::npos); return x.result(); } // void MakeImageDl::appendLeafname(string* s, bool contentMd, const MD5& md) { // Base64String x; // *s += (contentMd ? "c-" : "u-"); // (*s).swap(x.result()); // x.write(md, 16).flush(); // (*s).swap(x.result()); // } //______________________________________________________________________ MakeImageDl::Child* MakeImageDl::childFor(const string& url, const MD5* md, string* leafnameOut, Child* reuseChild) { debug("childFor: %L1, md %2", url, (md == 0 ? string("unknown") : md->toString())); msg("TODO: Maintain cache of active children; if URL requested 2nd time, " "add child which waits for 1st to finish"); string leafname; if (leafnameOut == 0) leafnameOut = &leafname; if (md != 0) { // "c-": Check whether data with that _checksum_ already finished in cache string filename = cachePathnameContent(*md, leafnameOut); string destDesc = subst(_("Cache entry %1"), *leafnameOut); struct stat fileInfo; if (stat(filename.c_str(), &fileInfo) == 0) { Child* c = childForCompletedContent(fileInfo, filename, md, reuseChild); if (c != 0) IOSOURCE_SEND(IO, io, makeImageDl_new, (c->source(), url, destDesc)); return c; } } // "u-": Check whether data with that _source_URL_ already finished in cache string filename = cachePathnameUrl(url, leafnameOut); string destDesc = subst(_("Cache entry %1"), *leafnameOut); struct stat fileInfo; if (stat(filename.c_str(), &fileInfo) == 0) { Child* c = childForCompletedUrl(fileInfo, filename, md, reuseChild); if (c != 0) IOSOURCE_SEND(IO, io, makeImageDl_new, (c->source(), url, destDesc)); return c; } /* stat result != 0, we assume this means "no such file or directory". "u~": Now check whether a download is already under way, or if a half-finished download was aborted earlier. */ toggleLeafname(&filename); if (stat(filename.c_str(), &fileInfo) == 0) { Child* c = childForSemiCompleted(fileInfo, filename, reuseChild); if (c != 0) IOSOURCE_SEND(IO, io, makeImageDl_new, (c->source(), url, destDesc)); return c; } /* Neither the complete nor the partial data is in the cache, so start a new download. */ debug("childFor: New download to %L1", filename); BfstreamCounted* f = new BfstreamCounted(filename.c_str(), ios::binary|ios::in|ios::out|ios::trunc); if (!*f) { string err = subst(_("Could not open `%L1' for output: %L2"), filename, strerror(errno)); generateError(err); return 0; } auto_ptr dl(new SingleUrl(url)); dl->setDestination(f, 0, 0); Child* c; if (reuseChild) c = reuseChild->init(this, &childrenVal, dl.get(), md); else c = new Child(this, &childrenVal, dl.get(), md); IOSOURCE_SEND(IO, io, makeImageDl_new, (dl.get(), url, destDesc)); dl.release(); return c; } //______________________________________________________________________ // Cache contains already completed download for requested URL/md5sum MakeImageDl::Child* MakeImageDl::childForCompletedUrl( const struct stat& fileInfo, const string& filename, const MD5* md, Child* reuseChild) { if (!S_ISREG(fileInfo.st_mode)) { // Something messed with the cache dir string err = subst(_("Invalid cache entry: `%L1' is not a file"), filename); generateError(err); return 0; } int cacheEntryAge = time(0) - fileInfo.st_mtime; debug("childFor: cache entry is %1 secs old", cacheEntryAge); cacheEntryAge = 0; // TODO - FIXME - remove this once if-mod-since impl. if (cacheEntryAge < CACHE_ENTRY_AGE_NOIFMODSINCE) { // Data fetched very recently - no need to go on the net, imm. return it debug("childFor: already have %L1", filename); auto_ptr dl(new CachedUrl(filename, 0)); Child* c; if (reuseChild) c = reuseChild->init(this, &childrenVal, dl.get(), md); else c = new Child(this, &childrenVal, dl.get(), md); dl.release(); return c; } else { /* Data for URL fetched before. If less than IF_MOD_SINCE seconds ago, just return it, else do an If-Modified-Since request. */ debug("childFor: if-mod-since %L1", filename); Assert(false); return 0; } } //____________________ // Cache contains already completed download for requested URL/md5sum MakeImageDl::Child* MakeImageDl::childForCompletedContent( const struct stat& fileInfo, const string& filename, const MD5* md, Child* reuseChild) { if (!S_ISREG(fileInfo.st_mode)) { // Something messed with the cache dir string err = subst(_("Invalid cache entry: `%L1' is not a file"), filename); generateError(err); return 0; } // Data with that MD5 known - no need to go on the net, imm. return it debug("childFor: already have md %L1", filename); auto_ptr dl(new CachedUrl(filename, 0)); Child* c; if (reuseChild) c = reuseChild->init(this, &childrenVal, dl.get(), md); else c = new Child(this, &childrenVal, dl.get(), md); dl.release(); return c; } //______________________________________________________________________ MakeImageDl::Child* MakeImageDl::childForSemiCompleted( const struct stat& fileInfo, const string& filename, Child*/*reuseChild*/){ if (!S_ISREG(fileInfo.st_mode)) { // Something messed with the cache dir string err = subst(_("Invalid cache entry: `%L1' is not a file"), filename); generateError(err); return 0; } debug("childFor: already have partial %L1", filename); Assert(false); return 0; #if 0 if (anotherdownloadunderway) { return cloneofotherdownload; // What if other d/l is aborted by user } else { // Do a resume + If-Modified-Since return x; } #endif } //______________________________________________________________________ /* Called by Child when the download has succeeded: Rename a '~' download to '-' to indicate that it is complete. */ void MakeImageDl::singleUrlFinished(Child* c) { # if DEBUG c->childSuccFail = true; # endif Paranoid(dynamic_cast(c->source()) != 0); debug("singleUrlFinished: %1", c->source()->location()); string srcName = cachePathnameUrl(c->source()->location(), 0, false); // "u~" string destName; if (c->checkContent) { // Compare desired md to actual mdCheck c->mdCheck.finish(); if (c->md == c->mdCheck) { debug("singleUrlFinished: Checksum OK: %1", c->md.toString()); destName = cachePathnameContent(c->md); // Rename to "c-" } else { debug("singleUrlFinished: Checksum mismatch, computed=%1 expected=%2", c->mdCheck.toString(), c->md.toString()); // In this case, let code below rename to "u-" } } if (destName.empty()) destName = cachePathnameUrl(c->source()->location()); // Rename to "u-" debug("singleUrlFinished: mv %1 %2", srcName, destName); // On Windows, cannot rename open file, so ensure it is closed c->deleteSource(); int status = rename(srcName.c_str(), destName.c_str()); if (status != 0) { string destName2(destName, tmpDir().length() + 1, string::npos); string err = subst(_("Could not rename `%L1' to `%L2': %L3"), srcName, destName2, strerror(errno)); generateError(err); return; } } //______________________________________________________________________ void MakeImageDl::childFailed(Child* c) { # if DEBUG c->childSuccFail = true; # endif IOSOURCE_SEND(IO, io, makeImageDl_finished, (c->source())); // Delete partial output file if it is empty if (dynamic_cast(c) != 0) { string filename = cachePathnameUrl(c->source()->location(), 0, false); c->deleteSource(); debug("rm -f %1", filename); remove(filename.c_str()); } // If there are alternative URLs for the data that we want, try them now if (c->urls.get() != 0) { while (true) { c->deleteSource(); // TODO: Punish servers in lastUrl for failure, by decreasing their score /* TODO: If old download was just interrupted, retry connecting a couple of further times and resume, but (really?) only do this after trying other download locations first. */ string url = c->urls->enumerate(c->lastUrl); if (url.empty()) break; // Reuse this Child object for the new URL debug("childFailed: Trying next URL %1", url); if (childFor(url, (c->checkContent ? &c->md : 0), 0, c) != 0) { Paranoid(c->source() != 0); c->source()->run(); return; } debug("childFailed: %1 failed immediately", url); // Null returned, i.e. error, so try next URL } } // Delete child. Will also auto-remove child from list of our children() delete c; // All alternatives tried for this child, so the entire MakeImageDl fails string err = _("Failed – see error of child download"); IOSOURCE_SEND(IO, io, job_failed, (err)); // FIXME: Uncomment if() once if-mod-since resume implemented // struct stat fileInfo; // if (stat(name.c_str(), &fileInfo) == 0 && fileInfo.st_size == 0) { // debug("rm -f %1", name); // remove(name.c_str()); // } else { // debug("NO rm -f %1", name); // } } //______________________________________________________________________ // Info from first [Image] in include tree available void MakeImageDl::setImageSection(string* imageName, string* imageInfo, string* imageShortInfo, PartUrlMapping* templateUrls, MD5** templateMd5) { debug("setImageSection"); Paranoid(!haveImageSection()); imageNameVal.swap(*imageName); imageInfoVal.swap(*imageInfo); imageShortInfoVal.swap(*imageShortInfo); this->templateUrls = templateUrls; templateMd5Val = *templateMd5; *templateMd5 = 0; IOSOURCE_SEND(IO, io, makeImageDl_haveImageSection, ()); } //______________________________________________________________________ void MakeImageDl::Child::job_deleted() { } void MakeImageDl::Child::job_succeeded() { debug("Child::job_succeeded: %1", source()->location()); # if DEBUG childSuccFail = true; # endif IOSOURCE_SEND(MakeImageDl::IO, master()->io, makeImageDl_finished, (source())); // For SingleUrls, maybe rename cache entry if (dynamic_cast(source()) != 0) master()->singleUrlFinished(this); // singleUrlSucceeded() calls this - also call it for other sources deleteSource(); if (master()->state() == DOWNLOADING_TEMPLATE) master()->templateFinished(); } void MakeImageDl::Child::job_failed(const string& /*error*/) { //debug("Child::job_failed %1", error); master()->childFailed(this); // Causes "delete this" } void MakeImageDl::Child::job_message(const string&) { } void MakeImageDl::Child::dataSource_dataSize(uint64) { } void MakeImageDl::Child::dataSource_data(const byte* data, unsigned size, uint64) { // Desired checksum is in md; calculate actual checksum in mdCheck if (checkContent) mdCheck.update(data, size); } //====================================================================== /* Below is the code for the "big picture" of a download: Download .jigdo, download .template, download individual files. */ /** Run initial .jigdo download, will start further downloads of [Include]d .jigdo files as necessary. */ void MakeImageDl::createJigdoDownload() { auto_ptr childDl(childFor(jigdoUrl)); if (childDl.get() != 0) { debug("jigdo download child %1", childDl.get()); string info = _("Retrieving .jigdo"); jigdoIo = new JigdoIO(childDl.get(), jigdoUrl/*, frontend.get()*/); IOSOURCE_SEND(IO, io, job_message, (info)); childDl->source()->io.addListener(*jigdoIo); (childDl.release())->source()->run(); } } //______________________________________________________________________ /** To be called by JigdoIO only. Called to notify the MakeImageDl that the last JigdoIO has successfully finished. Called just after childSucceeded() for that JigdoIO. */ void MakeImageDl::jigdoFinished() { debug("jigdoFinished"); callbackId = g_idle_add_full(G_PRIORITY_HIGH_IDLE, &jigdoFinished_callback, (gpointer)this, NULL); } gboolean MakeImageDl::jigdoFinished_callback(gpointer mi) { MakeImageDl* self = static_cast(mi); self->callbackId = 0; self->jigdoFinished2(); return FALSE; // "Don't call me again" } /* All .jigdo data available now. At this point, the only children that we ever started were all SingleUrls with JigdoIOs attached to them, so we can delete all jigdo-downloading children simply by deleting all children. */ void MakeImageDl::jigdoFinished2() { debug("jigdoFinished2"); if (finalState()) return; // I.e. there was an error Paranoid(stateVal == DOWNLOADING_JIGDO); stateVal = DOWNLOADING_TEMPLATE; // Start template download auto_ptr childDl(childFor(templateUrls.get(), templateMd5Val)); if (childDl.get() != 0) { string info = _("Retrieving .template"); IOSOURCE_SEND(IO, io, job_message, (info)); (childDl.release())->source()->run(); } } //______________________________________________________________________ /* Template download finished. This just means that the data was downloaded, need to verify its md5sum if appropriate. */ void MakeImageDl::templateFinished() { debug("templateFinished"); if (finalState()) return; // I.e. there was an error Paranoid(stateVal == DOWNLOADING_TEMPLATE); #warning "todo: mi.templateFinished();" //mi.templateFinished(); // stateVal = DOWNLOADING____; } jigdo-0.7.3/src/job/makeimagedl.fh0000644000175000017500000000067207717722167016574 0ustar richardrichard/* $Id: makeimagedl.fh,v 1.1 2003/08/17 15:51:19 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download .jigdo/.template and file URLs. */ namespace Job { class MakeImageDl; } jigdo-0.7.3/src/job/makeimagedl.hh0000644000175000017500000005311610324272744016564 0ustar richardrichard/* $Id: makeimagedl.hh,v 1.27 2005/10/15 21:34:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Download .jigdo/.template and file URLs. MakeImageDl takes care of downloading the data from the appropriate places and then passes it to its private MakeImage member. See also makeimage.hh. */ #ifndef MAKEIMAGEDL_HH #define MAKEIMAGEDL_HH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ /** MakeImageDl: Everything related to downloads
  • Downloads and interprets .jigdo file contents
  • Downloads .template via SingleURL, notifies MakeImage once done
  • "Owner" of the layout of the temporary directory, only component that directly makes modifications to this dir. (MakeImage only writes to the image file.)
  • Does simple cache management; if requested file already downloaded, immediately returns its data, or does an If-Modified-Since request; if partially downloaded, resumes.
  • Starts further SingleURLs for download of individual parts.
  • Automatic server selection: For servers which were rated equally acceptable by the user, measures their speed, then prefers the faster ones (but does not completely stop using the slower ones).
See comments in makeimage.hh for the big picture. */ class Job::MakeImageDl : NoCopy /*, public JigdoConfig::ProgressReporter*/ { public: class Child; friend class Child; class ChildListBase; class IO; IOSource io; // Points to e.g. a GtkMakeImage //____________________ /** Maximum allowed [Include] directives in a .jigdo file and the files it includes. Once exceeded, io->job_failed() is called. */ static const int MAX_INCLUDES = 100; enum State { DOWNLOADING_JIGDO, DOWNLOADING_TEMPLATE, FINAL_STATE, // Value isn't actually used; all below are final states: ERROR }; /** Return the string "Cache entry %1 -- %2" or an equivalent localized string. This is used for the destination description of children. */ //inline static const char* destDescTemplate(); /** Prepare object. No download is started yet. @param jigdoUri URL of top-level .jigdo @param destination Name of directory to create tmp directory in, for storing data during the download. */ MakeImageDl(/*IO* ioPtr,*/const string& jigdoUri, const string& destination); /** Destroy this MakeImageDl and all its children. */ ~MakeImageDl(); /** Destroy all children. */ void killAllChildren(); /** Start downloading. First creates a new download for the .jigdo data, then the .template data, etc. */ void run(); inline const string& jigdoUri() const; /** Return current state of object. */ inline State state() const; /** Return name of temporary directory. This is a subdir of "destination" (ctor arg), contains a hash of the jigdoUri. Never ends in '/'. */ inline const string& tmpDir() const; /** Set state to ERROR and call io->job_failed */ inline void generateError(const string& message, State newState = ERROR); /** Return true if current state is final */ inline bool finalState() const; #if 0 /** To be called by implementers of DataSource::IO only: Notify this object that the download is complete and that all bytes have been received. Will also call io->makeImageDl_finished(). Either this or childFailed() below MUST eventually be called by the IO object. @param child The SingleUrl or similar whose IO pointer points to childIo @param childIo The "this" pointer of the DataSource::IO implementer. @param frontendIo The IO object to which childIo forwards calls; e.g. GtkSingleUrl. */ void childSucceeded(Child* childDl, DataSource::IO* childIo/*, DataSource::IO* frontend*/); #endif /** As above, but notify this object that the download has failed: Not all bytes have been received, or another error (e.g. while gunzipping the .jigdo file) occurred. If there are several alternative download URLs for this child, the next one will be tried, otherwise the entire MakeImageDl fails. */ void childFailed(Child* childDl); /** Is called by a child JigdoIO once the [Image] section has been seen. The arguments are modified! */ void setImageSection(string* imageName, string* imageInfo, string* imageShortInfo, PartUrlMapping* templateUrls, MD5** templateMd5); /** Has setImageSection() already been called? (=>is imageName non-empty?) */ inline bool haveImageSection() const; /** Graph structure describing the contents of the .jigdo file. */ UrlMap urlMap; /** Return child download object which contains a DataSource which produces the data of the requested URL. That returned object is usually a newly started download, except if the file (or its beginning) was already downloaded. The filename is based on either the base64ified md checksum, or (if that is 0), the b64ied md5 checksum of the url. This method will call makeImageDl_new() for all listeners to the io object, to make them register their own child listeners with the new Child object. @param url URL to download @param md Expected checksum of downloaded data, null if unknown @param leafnameOut If non-null, string is overwritten with file's leafname in cache on exit. @param reuseChild For internal use only, should usually be set to null. If non-null, no new Child is allocated, instead the passed object is reused. However, some data members are not reset - this is used by the second childFor() below to try out all alternatives. @return New object, or null if error (and io->job_failed was called). If non-null, returned object will be deleted from this MakeImageDl's dtor (unless it is deleted earlier). */ Child* childFor(const string& url, const MD5* md = 0, string* leafnameOut = 0, Child* reuseChild = 0); /** As above, but instead of providing a single URL, a number of alternative URLs is given. The Child will only fail after all of them have been tried unsuccessfully. */ inline Child* childFor(PartUrlMapping* urls, const MD5* md = 0); typedef IList ChildList; /** Return the list of Child objects owned by this MakeImageDl. */ inline const ChildList& children() const; /** Return info about the first image section, or empty strings if none encountered so far */ inline const string& imageName() const { return imageNameVal; } inline const string& imageShortInfo() const { return imageShortInfoVal; } //inline const string& templateUrl() const { return templateUrlVal; } inline const MD5* templateMd5() const { return templateMd5Val; } /** This one is special: The contents of ImageInfo are an XML-style string, with markup containing tags named: b i tt u big small br p. When getting the string, the frontend must specify what strings the respective begin and end tags should be replaced with. The default args turn the string into plain UTF-8 without markup. This call is fairly expensive, you may want to cache the returned string. If there in an error parsing the XML, the string from the ImageInfo entry is either returned unchanged (if !escapedText), or all "dangerous" characters are escaped (if escapedText == true); If there is no error, escapedText==true means that any "<" in the normal text should be kept escaped as "<". escapedText=false will unescape the "<" to "<". @param subst The subst argument is of the form: { "", "", // <b>, </b> "", "", // <i>, </i> "", "", // <tt>, </tt> "", "", // <u>, </u> "", "", // <big>, </big> "", "", // <small>, </small> "", "", // <br/> }; @param output String to append image info to @param escapedText true if the characters <>& should be escaped as <, > & */ void imageInfo(string* output, bool escapedText, const char* subst[13]) const; /** Helper enum for the offsets above. E.g. TT is <tt>, TT_ is </tt> */ enum { B, B_, I, I_, TT, TT_, U, U_, BIG, BIG_, SMALL, SMALL_, BR }; /** Return ImageInfo as it appears in the .jigdo file. The value has *not* been checked for validity (correct tag nesting etc). */ inline const string& imageInfoOrig() const { return imageInfoVal; } /** To be called by JigdoIO only. Called to notify the MakeImageDl that the last JigdoIO has successfully finished. Called just after childSucceeded() for that JigdoIO. */ void jigdoFinished(); private: // To be called by Child only: /* Called by Child when the download has succeeded: Rename a '~' download to '-' to indicate that it is complete. */ void singleUrlFinished(Child* c); /* Called by Child when the download has succeeded, but the expected content checksum was wrong: Rename from c~ContentChecksum to u-UrlChecksum */ void singleUrlWrongContent(Child* c); // Called from Child::job_succeeded() when the template d/l has finished void templateFinished(); private: // Really private // Write a ReadMe.txt to the download dir; fails silently void writeReadMe(); // Starts the initial .jigdo download void createJigdoDownload(); /* Return filename for content md5sum cache entry: "/home/x/jigdo-blasejfwe/c-nGJ2hQpUNCIZ0fafwQxZmQ" @param leafnameOut String to assign cache entry leafname, or null @param isFinished true to use "-", false to use "~" @param isC true to use "c", false to use "u" */ string cachePathnameContent(const MD5& md, string* leafnameOut = 0, bool isFinished = true, bool isC = true); /* Return filename for URL cache entry: "/home/x/jigdo-blasejfwe/u-nGJ2hQpUNCIZ0fafwQxZmQ" @param leafnameOut String to assign cache entry leafname, or null @param isFinished true to use "-", false to use "~" */ string cachePathnameUrl(const string& url, string* leafnameOut = 0, bool isFinished = true); // Add leafname for object to arg string, e.g. "u-nGJ2hQpUNCIZ0fafwQxZmQ" //static void appendLeafname(string* s, bool contentMd, const MD5& md); /* Turn the '-' in string created by above function into a '~' or v.v. Works even if leafname is preceded by dirname or similar in s */ static inline void toggleLeafname(string* s); // Helper methods for childFor() Child* childForCompletedUrl(const struct stat& fileInfo, const string& filename, const MD5* md, Child* reuseChild); Child* childForCompletedContent(const struct stat& fileInfo, const string& filename, const MD5* md, Child* reuseChild); Child* childForSemiCompleted(const struct stat& fileInfo, const string& filename, Child* reuseChild); //static const char* destDescTemplateVal; State stateVal; // State, e.g. "downloading jigdo file", "error" string jigdoUrl; // URL of .jigdo file Job::JigdoIO* jigdoIo; // toplevel JigdoIO, while state == DOWNLOADING_JIGDO ChildList childrenVal; // Child downloads string dest; // Destination dir. No trailing '/', empty string for root dir string tmpDirVal; // Temporary dir, a subdir of dest // Workhorse which actually generates the image from the data we feed it MakeImage mi; // Info about first image section of this .jigdo, if any string imageNameVal; string imageInfoVal, imageShortInfoVal; SmartPtr templateUrls; // Can contain a list of altern. URLs MD5* templateMd5Val; static gboolean jigdoFinished_callback(gpointer); void jigdoFinished2(); int callbackId; // glib callback function ID }; //______________________________________________________________________ /** User interaction for MakeImageDl. */ class Job::MakeImageDl::IO : public Job::IO { public: /** Called by the job when it is deleted or when a different IO object is registered with it. If the IO object considers itself owned by its job, it can delete itself. */ virtual void job_deleted() = 0; /** Called when the job has successfully completed its task. */ virtual void job_succeeded() = 0; /** Called when the job fails. The only remaining action after getting this is to delete the job object. */ virtual void job_failed(const string& message) = 0; /** Informational message. */ virtual void job_message(const string& message) = 0; #if 0 /** Called by MakeImageDl after it has started a new download; either the download of the .jigdo file or the .template file, or the download of a part. The GTK+ GUI uses this to display the new SingleUrl as a "child" of the MakeImageDl. @param childDownload For example a SingleUrl, but could also be an object which just outputs existing cache contents. @param destDesc A descriptive string like "/foo/bar/image, offset 3453", NOT a filename! Supplied for information only, to be displayed to the user. @return IO object to associate with this child download. Anything happening to the SingleUrl child will be passed on to the object you return here. Can return null if nothing should be called, but this won't prevent the child download from being created. */ // virtual Job::DataSource::IO* makeImageDl_new( // Job::DataSource* childDownload, const string& uri, // const string& destDesc) = 0; #endif /** Called by MakeImageDl after it has started a new download; either the download of the .jigdo file or the .template file, or the download of a part. The GTK+ GUI uses this to display the new SingleUrl as a "child" of the MakeImageDl. @param uri URL of download that was started @param childDownload For example a SingleUrl, but could also be an object which just outputs existing cache contents. If the method implementer is interested in displaying progress info for this download, it should call childDownload->io.addListener(). @param destDesc A descriptive string like "/foo/bar/image, offset 3453", NOT a filename! Supplied for information only, to be displayed to the user. */ virtual void makeImageDl_new(Job::DataSource* childDownload, const string& uri, const string& destDesc) = 0; /** Usually called when the child has (successfully or not) finished, i.e. just after yourIo->job_succeeded/failed() was called. */ virtual void makeImageDl_finished(Job::DataSource* childDownload) = 0; /** Called as soon as the first [Image] section in the .jigdo data has been parsed. Call MakeImageDl::imageName() etc to get the info from the image section. */ virtual void makeImageDl_haveImageSection() = 0; }; //______________________________________________________________________ /** Child objects are in two lists, need this to disambiguate them. All instances of ChildListBase *must* actually be Child objects, which is the only class allowed to derive from it. */ class Job::MakeImageDl::ChildListBase : public IListBase { friend class Job::MakeImageDl::Child; explicit ChildListBase() { } // Private ctor, only usable by Child public: /** Allow ChildListBase objects to be used as Child objects - convenient, but slightly dangerous - *must* be sure that the ChildListBase is really a Child. */ inline Child* get(); inline const Child* get() const; }; //____________________ /** Each Child object stands for one DataSource (i.e.\ SingleUrl/CachedUrl) which the MakeImageDl starts as a "child download" of itself. The Child maintains a private pointer to a DataSource. It also listens to what happens to the DataSource and informs the parent MakeImageDl e.g. of succeeded downloads. Used to store additional information which the MakeImageDl needs, e.g. the filename in the cache. The ctor and dtor automatically add/remove the Child in its master MakeImageDl's list of children. Careful, this class derives twice from IListBase, once via ChildListBase, once via Job::DataSource::IO. */ class Job::MakeImageDl::Child : NoCopy, public ChildListBase, public Job::DataSource::IO { friend class MakeImageDl; public: inline ~Child(); /** @return The MakeImageDl which owns this object. */ MakeImageDl* master() const { return masterVal; } /** @return The SingleUrl/CachedUrl owned by this object. */ inline DataSource* source() const; /** Delete source object; subsequently, source()==0 */ inline void deleteSource(); /** @return The JigdoIO or similar owned by this object. null after init */ //inline DataSource::IO* childIo() const; /** Only to be called by MakeImageDl and its helper classes (JigdoIO) @param m Master MakeImageDl @param listHead Pointer to master->children @param src SingleUrl or other source of downloaded data @param expectedContent null if no md5sum known, or non-null ptr to expected checksum; is copied away. */ inline Child(MakeImageDl* m, ChildList* listHead, DataSource* src, const MD5* expectedContent); /** Only to be called by MakeImageDl and its helper classes (JigdoIO) */ //inline void setChildIo(DataSource::IO* c); # if DEBUG // childFailed() or childSucceeded() MUST always be called for a Child bool childSuccFail; # endif private: // (Re)initialize data members, except for urls and lastUrl. Returns this inline Child* init(MakeImageDl* m, ChildList* list, DataSource* src, const MD5* expectedContent); // Virtual methods from DataSource::IO. virtual void job_deleted(); virtual void job_succeeded(); virtual void job_failed(const string& message); virtual void job_message(const string& message); virtual void dataSource_dataSize(uint64 n); virtual void dataSource_data(const byte* data, unsigned size, uint64 currentSize); MakeImageDl* masterVal; DataSource* sourceVal; bool checkContent; // True iff checksum of downloaded data is to be verified MD5 md; // Only if contentMd==true, EXPECTED checksum of the download data MD5Sum mdCheck; // Only if contentMd==true, used to calculate actual checksum SmartPtr urls; // Null if only a single URL (.jigdo d/l) vector* lastUrl; // To record last URL output by templateUrls }; //====================================================================== // const char* Job::MakeImageDl::destDescTemplate() { // return destDescTemplateVal; // } const string& Job::MakeImageDl::jigdoUri() const { return jigdoUrl; } Job::MakeImageDl::State Job::MakeImageDl::state() const { return stateVal; } void Job::MakeImageDl::generateError(const string& message, State newState) { if (finalState()) return; stateVal = newState; IOSOURCE_SEND(IO, io, job_failed, (message)); } bool Job::MakeImageDl::finalState() const { return state() > FINAL_STATE; } const string& Job::MakeImageDl::tmpDir() const { return tmpDirVal; } void Job::MakeImageDl::toggleLeafname(string* s) { int off = s->length() - 23; Paranoid((*s)[off] == '-' || (*s)[off] == '~'); (*s)[off] ^= ('-' ^ '~'); } bool Job::MakeImageDl::haveImageSection() const { return !imageNameVal.empty(); } const Job::MakeImageDl::ChildList& Job::MakeImageDl::children() const { return childrenVal; } Job::MakeImageDl::Child* Job::MakeImageDl::childFor(PartUrlMapping* urls, const MD5* md) { auto_ptr > lastUrl(new vector); string url = urls->enumerate(lastUrl.get()); Child* c = childFor(url, md); if (c != 0) { c->urls = urls; c->lastUrl = lastUrl.release(); } return c; } //____________________ /** These methods use upcasts. */ Job::MakeImageDl::Child* Job::MakeImageDl::ChildListBase::get() { return static_cast(this); } const Job::MakeImageDl::Child* Job::MakeImageDl::ChildListBase::get() const { return static_cast(this); } Job::MakeImageDl::Child* Job::MakeImageDl::Child::init(MakeImageDl* m, ChildList* /*list*/, DataSource* src, const MD5* expectedContent) { masterVal = m; sourceVal = src; checkContent = (expectedContent != 0); if (expectedContent != 0) md = *expectedContent; else md.clear(); # if DEBUG childSuccFail = false; # endif // Add ourself as listener to DataSource src->io.addListener(*implicit_cast(this)); return this; } Job::MakeImageDl::Child::Child(MakeImageDl* m, ChildList* list, DataSource* src, const MD5* expectedContent) : ChildListBase(), Job::DataSource::IO(), md(), mdCheck(), urls(), lastUrl(0) { Paranoid(list != 0); init(m, list, src, expectedContent); // Add ourself to parent's list of children list->push_back(*implicit_cast(this)); //msg("Child %1", this); } Job::MakeImageDl::Child::~Child() { # if DEBUG //msg("~Child %1", this); Paranoid(childSuccFail); # endif deleteSource(); delete lastUrl; } Job::DataSource* Job::MakeImageDl::Child::source() const { return sourceVal; } void Job::MakeImageDl::Child::deleteSource() { delete sourceVal; sourceVal = 0; } #endif jigdo-0.7.3/src/job/single-url.cc0000644000175000017500000002035110226053701016355 0ustar richardrichard/* $Id: single-url.cc,v 1.17 2005/04/09 22:31:29 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2002-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Single HTTP or FTP retrievals */ #include #include #include #include #include #include #include #include #include #include #include using namespace Job; //______________________________________________________________________ DEBUG_UNIT("single-url") SingleUrl::SingleUrl(/*IOPtr DataSource::IO* ioPtr, */const string& uri) : DataSource(/*ioPtr*/), download(uri, this), progressVal(), destStreamVal(0), destOff(0), destEndOff(0), resumeLeft(0), haveResumeOffset(false), haveDestination(false), /*havePragmaNoCache(false),*/ tries(0) { debug("SingleUrl %1", this); } //________________________________________ SingleUrl::~SingleUrl() { debug("~SingleUrl %1", this); } const Progress* SingleUrl::progress() const { return &progressVal; } const string& SingleUrl::location() const { return download.uri(); } //______________________________________________________________________ void SingleUrl::setResumeOffset(uint64 resumeOffset) { if (resumeOffset < RESUME_SIZE) resumeLeft = resumeOffset; else resumeLeft = RESUME_SIZE; // Not "resumeOffset - resumeLeft", won't be calling io for a while: progressVal.setCurrentSize(resumeOffset); download.setResumeOffset(resumeOffset - resumeLeft); haveResumeOffset = true; } void SingleUrl::setDestination(BfstreamCounted* destStream, uint64 destOffset, uint64 destEndOffset) { destStreamVal = destStream; destOff = destOffset; destEndOff = destEndOffset; haveDestination = true; } void SingleUrl::run() { debug("SingleUrl %1 run()", this); if (!haveResumeOffset) setResumeOffset(0); haveResumeOffset = false; if (!haveDestination) setDestination(0, 0, 0); haveDestination = false; // if (!havePragmaNoCache) setPragmaNoCache(false); // havePragmaNoCache = false; if (destEndOff > destOff) progressVal.setDataSize(destEndOff - destOff); else progressVal.setDataSize(0); ++tries; Assert(resumeLeft == 0 || destStreamVal != 0); progressVal.reset(); download.run(); } //______________________________________________________________________ void SingleUrl::resumeFailed() { debug("resumeFailed"); setNoResumePossible(); string error(_("Resume failed")); IOSOURCE_SEND(DataSource::IO, io, job_failed, (error)); download.stop(); progressVal.setAutoTick(false); return; } //______________________________________________________________________ void SingleUrl::download_dataSize(uint64 n) { if (progressVal.dataSize() == 0) { progressVal.setDataSize(n); } else { // Error if reported size of object does not match expected one if (n > 0 && n != progressVal.dataSize()) resumeFailed(); } if (!resuming()) { IOSOURCE_SEND(DataSource::IO, io, dataSource_dataSize, (n)); return; } } //______________________________________________________________________ bool SingleUrl::writeToDestStream(uint64 off, const byte* data, unsigned size) { if (destStream() == 0 /*|| stopLaterId != 0*/) return SUCCESS; //debug("writeToDestStream %1 %2 bytes at offset %3", // destStream(), size, off); // Never go beyond destEndOff unsigned realSize = size; if (destOff < destEndOff && off + size > destEndOff) realSize = destEndOff - off; destStream()->seekp(off, ios::beg); writeBytes(*destStream(), data, realSize); if (!*destStream()) { string error = subst("%L1", strerror(errno)); IOSOURCE_SEND(DataSource::IO, io, job_failed, (error)); download.stop(); //stopLater(); progressVal.setAutoTick(false); return FAILURE; } if (realSize < size) { // Server sent more than we expected; error string error = _("Server sent more data than expected"); IOSOURCE_SEND(DataSource::IO, io, job_failed, (error)); download.stop(); //stopLater(); progressVal.setAutoTick(false); return FAILURE; } return SUCCESS; } //______________________________________________________________________ void SingleUrl::download_data(const byte* data, unsigned size, uint64 currentSize) { # if DEBUG Paranoid(resuming() || progressVal.currentSize() == currentSize - size); //g_usleep(10000); string s; unsigned limit = (size < 60 ? size : 60); for (unsigned i = 0; i < limit; ++i) if (data[i] >= 32 && data[i] < 127) s += data[i]; else s += '.'; debug("%5 Got %1 currentSize=%2, realoffset=%3: %4", size, progressVal.currentSize(), currentSize - size, s, this); # endif // && !download.pausedSoon()) if (!progressVal.autoTick() // <-- extra check for efficiency only && !download.paused()) progressVal.setAutoTick(true); if (!resuming()) { // Normal case: Just write it to file and forward it downstream progressVal.setCurrentSize(currentSize); if (writeToDestStream(destOff + currentSize - size, data, size) == FAILURE) return; IOSOURCE_SEND(DataSource::IO, io, dataSource_data, (data, size, currentSize)); return; } //____________________ /* We're in the middle of a resume - compare downloaded bytes with bytes from destStream, don't pass them on. Note that progressVal.currentSize() is "stuck" at the value currentSize+resumeLeft, i.e. the offset of the end of the overlap area. */ debug("RESUME left=%1 off=%2 fileoff=%3", resumeLeft, destOff + currentSize - size, destOff + currentSize - size); // Read from file unsigned toRead = min(resumeLeft, size); byte buf[toRead]; byte* bufEnd = buf + toRead; byte* b = buf; destStream()->seekg(destOff + currentSize - size, ios::beg); while (*destStream() && toRead > 0) { readBytes(*destStream(), b, toRead); size_t n = destStream()->gcount(); //debug("during resume: read %1", n); b += n; toRead -= n; } if (toRead > 0 && !*destStream()) { debug(" Error toRead=%1 `%L2'", toRead, strerror(errno)); resumeFailed(); return; } // Compare b = buf; while (size > 0 && b < bufEnd) { if (*data != *b) { resumeFailed(); debug(" fromfile=%1 fromnet=%2", int(*b), int(*data)); return; } ++data; ++b; --size; --resumeLeft; } string info = subst(_("Resuming... %1kB"), resumeLeft / 1024); IOSOURCE_SEND(DataSource::IO, io, job_message, (info)); if (resumeLeft > 0) return; /* Success: End of buf reached and all bytes matched. Pass remaining bytes from this chunk to IO. */ progressVal.reset(); if (size > 0) { debug(" End, currentSize now %1", currentSize); progressVal.setCurrentSize(currentSize); if (writeToDestStream(destOff + currentSize - size, data, size) == FAILURE) return; IOSOURCE_SEND(DataSource::IO, io, dataSource_data, (data, size, currentSize)); } } //______________________________________________________________________ void SingleUrl::download_succeeded() { progressVal.setAutoTick(false); IOSOURCE_SEND(DataSource::IO, io, job_succeeded, ()); } //______________________________________________________________________ void SingleUrl::download_failed(string* message) { progressVal.setAutoTick(false); IOSOURCE_SEND(DataSource::IO, io, job_failed, (*message)); } //______________________________________________________________________ void SingleUrl::download_message(string* message) { IOSOURCE_SEND(DataSource::IO, io, job_message, (*message)); } //______________________________________________________________________ bool SingleUrl::paused() const { //return download.pausedSoon(); return download.paused(); } void SingleUrl::pause() { Paranoid(!paused()); download.pause(); progressVal.setAutoTick(false); } void SingleUrl::cont() { Paranoid(paused()); progressVal.reset(); download.cont(); // progressVal.setAutoTick(true) called from download_data() later } //______________________________________________________________________ jigdo-0.7.3/src/job/single-url.hh0000644000175000017500000002152110226060300016360 0ustar richardrichard/* $Id: single-url.hh,v 1.15 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2002-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Single HTTP or FTP retrievals. More low-level code which interfaces with libwww is in download.cc Higher-level GUI code is in gtk-single-url.cc */ #ifndef SINGLE_URL_HH #define SINGLE_URL_HH #include #include #include #include #include //______________________________________________________________________ namespace Job { class SingleUrl; } /** Class which handles downloading a HTTP or FTP URL. It is a layer on top of Download and provides some additional functionality:
  • Maintains a Progress object (for "time remaining", "kB/sec")
  • Does clever resumes of downloads (with partial overlap to check that the correct file is being resumed)
  • Does pause/continue by registering a callback function with glib. This is necessary because Download::pause() must not be called from within download_data()
  • Contains a state machine which handles resuming the download a certain number of times if the connection is dropped.
This one will forever remain single since there are no single parties around here and it's rather shy. TODO: If you pity it too much, implement a MarriedUrl. */ class Job::SingleUrl : public Job::DataSource, private Download::Output { public: /** Number of bytes to download again when resuming a download. These bytes will be compared with the old data. */ static const unsigned RESUME_SIZE = 16*1024; /** Number of times + 1 a download for the same URL will be resumed. A resume is necessary if the connection drops unexpectedly. */ static const int MAX_TRIES = 20; /** Delay (millisec) before a download is resumed automatically, and during which a "resuming..." message or similar can be displayed. This value is never read by SingleUrl itself, it's just a hint for code using SingleUrl. */ static const int RESUME_DELAY = 3000; /** Create object, but don't start the download yet - use run() to do that. @param uri URI to download */ SingleUrl(/*IOPtr DataSource::IO* ioPtr, */const string& uri); virtual ~SingleUrl(); /** Set offset to resume from - download must not yet have been started, call before run(). By default, the offset is 0 after object creation and after a download has finished. If resumeOffset>0, SingleUrl will first read RESUME_SIZE bytes from destStream at offset destOffset+resumeOffset-RESUME_SIZE (or less if the first read byte would be 0 if downloaded data is to be written somewhere inside a bigger file. @param destEndOffset Offset of first byte in destStream (>destOffset) which the SingleUrl is not allowed to overwrite. 0 means don't care, no limit. */ void setDestination(BfstreamCounted* destStream, uint64 destOffset, uint64 destEndOffset); /** Behaviour as above. Defaults if not called before run() is false, i.e. don't add "Pragma: no-cache" header. @param pragmaNoCache If true, perform a "reload", discarding anything cached e.g. in a proxy. */ // inline void setPragmaNoCache(bool pragmaNoCache); /** Start download or resume it All following bytes are written to destStream as well as passed to the IO object. We seek to the correct position each time when writing, so several parallel downloads for the same destStream are possible. An error is raised if you want to resume but the server doesn't support it, or if the server wants to send more data than expected, i.e. the byte at destEndOffset inside destStream would be overwritten, or if destEndOffset is set and the size of the data as reported by the server is not destEndOffset-destOffset. */ virtual void run(); /** Current try - possible values are 1..MAX_TRIES (inclusive) */ inline int currentTry() const; /** Is the download currently paused? From DataSource. */ virtual bool paused() const; /** Pause the download. From DataSource. */ virtual void pause(); /** Continue downloading. From DataSource. */ virtual void cont(); /** Stop download. */ inline void stop(); /** Are we in the process of resuming, i.e. are we currently downloading data before the resume offset and comparing it? */ inline bool resuming() const; /** Did the download fail with an error? */ inline bool failed() const; /** Is download finished? (Also returns true if FTP/HTTP1.0 connection dropped) */ inline bool succeeded() const; /** Return the internal progress object. From DataSource. */ virtual const Progress* progress() const; /** Return the URL used to download the data. From DataSource. */ virtual const string& location() const; /** Return the registered destination stream, or null */ inline BfstreamCounted* destStream() const; /** Set destination stream, can be null */ //inline void setDestStream(bfstream* destStream); /** Call this after the download has failed (and your job_failed() has been called), to check whether resuming the download is possible. Returns true if resume is possible, i.e. error was not permanent and maximum number of tries not exceeded. */ inline bool resumePossible() const; /** "Manually" cause resumePossible() to return false from now on. Useful e.g. if the user of this class wanted to write to a file and there was an error doing so. */ inline void setNoResumePossible(); private: // Virtual methods from Download::Output virtual void download_dataSize(uint64 n); virtual void download_data(const byte* data, unsigned size, uint64 currentSize); virtual void download_succeeded(); virtual void download_failed(string* message); virtual void download_message(string* message); /** Stop download from idle callback. */ //void stopLater(); /* Write bytes at specified offset. Return FAILURE and call io->job_failed() if error during writing or if written data would exceed destEndOff. */ inline bool writeToDestStream(uint64 off, const byte* data, unsigned size); Download download; Progress progressVal; // Call io->job_failed(), then stopLater() inline void resumeFailed(); SmartPtr destStreamVal; uint64 destOff, destEndOff; unsigned resumeLeft; // >0: Nr of bytes of resume overlap left /* Was setResumeOffset()/setDestination()/setPragmaNoCache() called before run()? If false, run() will call it with default values. */ bool haveResumeOffset, haveDestination; //, havePragmaNoCache; int tries; // Nr of tries resuming after interrupted connection }; //====================================================================== // void Job::SingleUrl::setPragmaNoCache(bool pragmaNoCache) { // download.setPragmaNoCache(pragmaNoCache); // havePragmaNoCache = true; // } int Job::SingleUrl::currentTry() const { return tries; } bool Job::SingleUrl::resuming() const { return resumeLeft > 0; } bool Job::SingleUrl::failed() const { return download.failed(); } bool Job::SingleUrl::succeeded() const { return download.succeeded(); } BfstreamCounted* Job::SingleUrl::destStream() const { return destStreamVal.get(); } bool Job::SingleUrl::resumePossible() const { // msg("Job::SingleUrl::resumePossible tries=%1 interr=%2 curSiz=%3", // tries, download.interrupted(), progressVal.currentSize()); if (tries >= MAX_TRIES || !download.interrupted()) return false; if (progressVal.currentSize() == 0 || progressVal.dataSize() == 0) return true; return (progressVal.dataSize() > 0 && progressVal.currentSize() < progressVal.dataSize()); } void Job::SingleUrl::setNoResumePossible() { tries = MAX_TRIES; // Download failed permanently, do not resume } void Job::SingleUrl::stop() { download.stop(); } #endif jigdo-0.7.3/src/job/url-mapping-test.cc0000644000175000017500000001754710226256204017524 0ustar richardrichard/* $Id: url-mapping-test.cc,v 1.5 2005/04/10 17:04:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Test for addServer(), addPart() #test-deps job/url-mapping.o util/configfile.o util/md5sum.o util/glibc-md5.o #test-deps net/uri.o */ #include #include #include #include #include #include //______________________________________________________________________ namespace { // Record certain lines of the log const char* const UNIT = "url-mapping"; string logged; bool unitEnabled = false; // Was the unit enabled with --debug? void loggerPut(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]) { if (unitName == UNIT) { if (strncmp(format, "add", 6) != 0) { logged += Subst::subst(format, args, arg); logged += '\n'; } if (!unitEnabled) return; } Logger::defaultPut(unitName, unitNameLen, format, args, arg); } void loggerInit() { Logger::setOutputFunction(&loggerPut); Logger* l = Logger::enumerate(); while (l != 0 && strcmp(l->name(), UNIT) == 0) l = Logger::enumerate(l); Assert(l != 0); // Unit not found? Probably wasn't compiled with DEBUG unitEnabled = l->enabled(); if (!unitEnabled) Logger::setEnabled(UNIT); } // Compare actual/expected log output void expect(const char* s) { if (logged != s) { msg("Error: Expected \"%1\", but got \"%2\"", s, logged); Assert(logged == s); } logged.erase(); } const string base = "http://baseurl/"; // Add part void ap(UrlMap& m, const MD5& md, const char* s) { vector value; ConfigFile::split(value, s, 0); const char* result = m.addPart(base, md, value); if (result != 0) msg("addPart: %1", result); Assert(result == 0); } // Add server void as(UrlMap& m, const char* label, const char* s, bool expectFailure = false) { vector value; ConfigFile::split(value, s, 0); const char* result = m.addServer(base, label, value); if (result != 0) msg("addServer: %1", result); Assert(expectFailure == (result != 0)); } MD5 md[10]; } // namespace //______________________________________________________________________ void test1() { UrlMap m; as(m, "LabelA", "http://myserver.org/"); as(m, "LabelA", "ftp://mirror.myserver.org/"); as(m, "LabelB", "LabelC:subdirectory/"); as(m, "LabelC", "http://some.where.com/jigdo/"); ap(m, md[0], "X:part0"); ap(m, md[1], "X:part1"); ap(m, md[0], "LabelB:some/path/part2"); as(m, "X", "X=http://localhost:8000/~richard/ironmaiden/"); ap(m, md[2], "X:part2"); logged.erase(); m.dumpJigdoInfo(); expect("Part AQIDBAUGBwgJCgsMDQ4PEA: X + `part0'\n" "Part AQIDBAUGBwgJCgsMDQ4PEA: LabelB + `some/path/part2'\n" "Part ERITFBUWFxgZGhscHR4fIA: X + `part1'\n" "Part ISIjJCUmJygpKissLS4vMA: X + `part2'\n" "Server LabelA: http + `//myserver.org/'\n" "Server LabelA: ftp + `//mirror.myserver.org/'\n" "Server LabelB: LabelC + `subdirectory/'\n" "Server LabelC: http + `//some.where.com/jigdo/'\n" "Server X: X=http + `//localhost:8000/~richard/ironmaiden/'\n" "Server X=http: + `X=http:'\n" "Server ftp: + `ftp:'\n" "Server http: + `http:'\n"); } void test2() { UrlMap m; as(m, "A", "B"); as(m, "A", "C:"); as(m, "A", "D:"); as(m, "C", "foobar"); logged.erase(); m.dumpJigdoInfo(); expect("Server A: http + `//baseurl/B'\n" "Server A: D + `'\n" "Server A: C + `'\n" "Server C: http + `//baseurl/foobar'\n" "Server D: + `D:'\n" "Server http: + `http:'\n"); } void test3() { // Loops disallowed UrlMap m; as(m, "asdf", "foo:x"); as(m, "foo", "asdf:y", true); logged.erase(); m.dumpJigdoInfo(); expect("Server asdf: foo + `x'\n" "Server foo: + `y'\n"); // Not useful, avoids loop } void test4() { // Loops disallowed UrlMap m; as(m, "a", "b:"); as(m, "b", "c:"); as(m, "c", "d:"); as(m, "d", "foo:"); as(m, "d", "bar:"); as(m, "d", "a:", true); logged.erase(); m.dumpJigdoInfo(); expect("Server a: b + `'\n" "Server b: c + `'\n" "Server bar: + `bar:'\n" "Server c: d + `'\n" "Server d: foo + `'\n" "Server d: + `'\n" // Not useful, avoids loop "Server d: bar + `'\n" "Server foo: + `foo:'\n"); } //______________________________________________________________________ void expectEnum(PartUrlMapping* m, const char* expected) { vector best; string result; while (true) { string s = m->enumerate(&best); if (s.empty()) break; if (!result.empty()) result += ' '; result += s; } if (result != expected) { msg("Error: Expected \"%1\", but got \"%2\"", expected, result); Assert(result == expected); } msg("OK, got \"%1\"", result); } /* NB: In the following tests, all mappings must have different weights. Otherwise, different rounding errors in different implementations will lead to a different output order, and make the tests fail. */ void score1() { // Single leaf object UrlMap m; vector v; v.push_back("fooboo/bar/baz"); m.addPart("", md[0], v); m.dumpJigdoInfo(); expectEnum(m[md[0]], "fooboo/bar/baz"); } void score2() { // Simple chain, 2 objects long UrlMap m; ap(m, md[0], "fooboo/bar/baz"); m.dumpJigdoInfo(); expectEnum(m[md[0]], "http://baseurl/fooboo/bar/baz"); } void score3() { // Diamond-shaped graph UrlMap m; as(m, "Label", "Server:x/ --try-first=", true); ap(m, md[0], "Label:some/path"); as(m, "Label", "Server:y/ --try-first"); as(m, "Server", "ftp://server.org:80/"); m.dumpJigdoInfo(); expectEnum(m[md[0]], "ftp://server.org:80/y/some/path " "ftp://server.org:80/x/some/path"); } void score4() { // Full mesh, many leaf possibilities UrlMap m; ap(m, md[1], "A:a --try-first"); ap(m, md[1], "A:b"); ap(m, md[1], "A:c --try-last"); ap(m, md[1], "A:d --try-first=3.0"); as(m, "A", "S:l --try-first=-13"); as(m, "A", "S:m --try-last=.222"); as(m, "A", "S:n --try-first=3.4"); as(m, "A", "S:o --try-first --try-first=6.5"); as(m, "S", "p://x/"); m.dumpJigdoInfo(); expectEnum(m[md[1]], "p://x/od p://x/oa p://x/ob p://x/oc p://x/nd p://x/na " "p://x/nb p://x/md p://x/nc p://x/ma p://x/mb p://x/mc p://x/ld " "p://x/la p://x/lb p://x/lc"); } void score5() { // Full mesh, one leaf possibility UrlMap m; ap(m, md[1], "A:."); as(m, "A", "S:l --try-first=.1"); as(m, "A", "S:m --try-first=.21"); as(m, "A", "S:n --try-first=.32"); as(m, "A", "S:o --try-last=.222"); as(m, "S", "T:a --try-first=.4"); as(m, "S", "T:b --try-first=.5"); as(m, "S", "T:c --try-first=.6"); as(m, "S", "T:d --try-first=3.4"); as(m, "T", "http://x/"); m.dumpJigdoInfo(); expectEnum(m[md[1]], "http://x/dn. http://x/dm. http://x/dl. http://x/do. " "http://x/cn. http://x/bn. http://x/cm. http://x/an. " "http://x/bm. http://x/cl. http://x/am. http://x/bl. " "http://x/al. http://x/co. http://x/bo. http://x/ao."); } //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); int n = 0; for (int i = 0; i < 10; ++i) for (int j = 0; j < 16; ++j) md[i].sum[j] = ++n; msg("Score tests"); UrlMapping::setNoRandomInitialWeight(); score1(); score2(); score3(); score4(); score5(); msg("Graph build tests"); loggerInit(); test1(); test2(); test3(); test4(); return 0; } jigdo-0.7.3/src/job/url-mapping.cc0000644000175000017500000002617010262770355016547 0ustar richardrichard/* $Id: url-mapping.cc,v 1.8 2005/07/06 14:55:41 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Representation of a "=Label:someurl" line in a .jigdo file */ #include #include #include #include #include #include #include #include //______________________________________________________________________ // "make test" needs the logging built in even if DEBUG=0 #undef debug namespace { Logger debug("url-mapping"); } namespace { struct RandSingleton { GRand* r; RandSingleton() : r(g_rand_new()) { } ~RandSingleton() { g_rand_free(r); } }; RandSingleton r; } const double UrlMapping::RANDOM_INIT_RANGE = 0.03125; namespace { bool randomInit = true; } void UrlMapping::setNoRandomInitialWeight() { randomInit = false; } UrlMapping::UrlMapping() : urlVal(), prepVal(0), nextVal(0)/*, tries(0), triesFailed(0)*/ { if (randomInit) weight = g_rand_double_range(r.r, -RANDOM_INIT_RANGE, RANDOM_INIT_RANGE); else weight = 0.0; } UrlMapping::~UrlMapping() { } //______________________________________________________________________ // map > parts; /* Given an URL-like string of the form "Label:some/path" or "http://foo/bar", return the ServerUrlMapping for "Label"/"http". If none exists, create one. @param url URL-like string @param colon Offset of ':' in url, must be >0 @return new or existent mapping */ ServerUrlMapping* UrlMap::findOrCreateServerUrlMapping( const string& url, unsigned colon) { string label(url, 0, colon); ServerMap::iterator i = serversVal.lower_bound(label); if (i != serversVal.end() && i->first == label) return i->second.get(); // "Label" entry present, just return it // No entry for "Label" yet, need to create a dummy ServerUrlMapping ServerUrlMapping* s = new ServerUrlMapping(); /* Initialize the url for label "http" with "http:"; addServer() below will recognize this special case. */ SmartPtr ss(s); serversVal.insert(i, make_pair(label, ss)); label += ':'; s->setUrl(label); return s; } //____________________ const char* UrlMap::addPart(const string& baseUrl, const MD5& md, const vector& value) { string url; if (findLabelColon(value.front()) != 0) url = value.front(); else uriJoin(&url, baseUrl, value.front()); //debug("addPart %1 -> %2", md.toString(), url); PartUrlMapping* p = new PartUrlMapping(); unsigned colon = findLabelColon(url); if (colon == 0) { p->setUrl(url); } else { p->setPrepend(findOrCreateServerUrlMapping(url, colon)); p->setUrl(url, colon + 1); } // Insert entry in "parts" SmartPtr pp(p); pair x = partsVal.insert(make_pair(md, pp)); Paranoid(x.first->first == md); if (!x.second) { // entry for md already present in partsVal, add p to its linked list x.first->second->insertNext(p); } return p->parseOptions(value); } //____________________ const char* UrlMap::addPart(const string& baseUrl, const vector& value, SmartPtr* oldList) { Assert(oldList != 0); string url; if (findLabelColon(value.front()) != 0) url = value.front(); else uriJoin(&url, baseUrl, value.front()); PartUrlMapping* p = new PartUrlMapping(); unsigned colon = findLabelColon(url); if (colon == 0) { p->setUrl(url); } else { p->setPrepend(findOrCreateServerUrlMapping(url, colon)); p->setUrl(url, colon + 1); } if (*oldList == 0) *oldList = p; else (*oldList)->insertNext(p); return p->parseOptions(value); } //____________________ /* For a line "Foobar=Label:some/path" in the [Servers] section: @param label == "Foobar" @param value arguments; value.front()=="Label:some/path" */ const char* UrlMap::addServer(const string& baseUrl, const string& label, const vector& value) { string url; if (findLabelColon(value.front()) != 0) url = value.front(); else uriJoin(&url, baseUrl, value.front()); debug("addServer %1 -> %2", label, url); /* Create entry for "Foobar". We usually create a new ServerUrlMapping, except in the case where findOrCreateServerUrlMapping() has created a dummy entry during previous processing of a [Parts] section. */ ServerUrlMapping* s; ServerUrlMapping* mappingList; // Ptr to head of linked list for label ServerMap::iterator i = serversVal.lower_bound(label); if (i == serversVal.end() || i->first != label) { // Create object and start a new linked list; add list head to "servers" s = mappingList = new ServerUrlMapping(); SmartPtr ss(s); serversVal.insert(i, make_pair(label, ss)); } else { const string& somepath = i->second->url(); // Dummy mapping recognizable by trailing ':' if (!somepath.empty() && somepath[somepath.length() - 1] == ':') { // List head is dummy; use it directly s = mappingList = i->second.get(); } else { // Create object and add it to existing linked list mappingList = i->second.get(); s = new ServerUrlMapping(); i->second->insertNext(s); } } /* Process the "Label:some/path" string, maybe adding a dummy ServerUrlMapping for "Label". */ unsigned colon = findLabelColon(url); if (colon == 0) { s->setUrl(url); } else { ServerUrlMapping* prep = findOrCreateServerUrlMapping(url, colon); s->setPrepend(prep); s->setUrl(url, colon + 1); // Check whether this is a recursive definition UrlMapping* i = prep; do { if (i == mappingList) { // Cycle detected // Break cycle, leave s in nonsensical state. Maybe also delete prep s->setPrepend(0); return _("Recursive label definition"); } i = i->prepend(); } while (i != 0); } return s->parseOptions(value); } //______________________________________________________________________ void UrlMap::dumpJigdoInfo() { // Build reverse map from UrlMapping* to its label map names; for (ServerMap::iterator i = serversVal.begin(), e = serversVal.end(); i != e; ++i) { names.insert(make_pair(i->second.get(), i->first)); } for (PartMap::iterator i = partsVal.begin(), e = partsVal.end(); i != e; ++i) { UrlMapping* p = i->second.get(); while (p != 0) { debug("Part %1: %2 + `%3'", i->first.toString(), names[p->prepend()], p->url()); p = p->next(); } } for (ServerMap::iterator i = serversVal.begin(), e = serversVal.end(); i != e; ++i) { UrlMapping* p = i->second.get(); while (p != 0) { debug("Server %1: %2 + `%3'", i->first, names[p->prepend()], p->url()); p = p->next(); } } } //______________________________________________________________________ const char* UrlMapping::parseOptions(const vector& value) { for (vector::const_iterator i = value.begin(), e = value.end(); i != e; ++i) { if (compat_compare(*i, 0, 11, "--try-first", 0, 11) == 0) { if (i->length() == 11) { weight += 1.0; } else if ((*i)[11] == '=') { double d = 0.0; if (sscanf(i->c_str() + 12, "%lf", &d) != 1) return _("Invalid argument to --try-first"); weight += d; } } else if (compat_compare(*i, 0, 10, "--try-last", 0, 10) == 0) { if (i->length() == 10) { weight -= 1.0; } else if ((*i)[10] == '=') { double d = 0.0; if (sscanf(i->c_str() + 11, "%lf", &d) != 1) return _("Invalid argument to --try-last"); weight -= d; } } } return 0; } //______________________________________________________________________ string PartUrlMapping::enumerate(vector* bestPath) { if (seen.get() == 0) seen.reset(new set()); string result; double bestScore = -FLT_MAX; unsigned serialNr = 0; unsigned bestSerialNr = 0; bestPath->clear(); UrlMapping* mapping = this; do { //debug("enumerate: at top-level: %1", mapping->url()); enumerate(0, mapping, 0.0, 0, &serialNr, &bestScore, bestPath, &bestSerialNr); // Recurse mapping = mapping->next(); // Walk through list of peers } while (mapping != 0); if (bestSerialNr != 0) { seen->insert(bestSerialNr); // Ensure this URL is only output once for (vector::iterator i = bestPath->begin(), e = bestPath->end(); i != e; ++i) result += (*i)->url(); // Construct URL debug("enumerate: \"%1\" with score %2", result, bestScore); } else { debug("enumerate: end"); } return result; } /* @param stackPtr For recording how we reached this "mapping". @param mapping Current node in graph @param score Accumulated scores of objects through which we came here @param pathLen Nr of objects through which we reached "mapping" @param serialNr Nr of leaves encountered so far during recursion. One leaf may be reached through >1 paths in the graph; in that case, it counts >1 times. @param bestScore Highest score found so far @param bestPath Path through graph corresponding to bestScore @param bestSerialNr Value of serialNr for this leaf obj */ void PartUrlMapping::enumerate(StackEntry* stackPtr, UrlMapping* mapping, double score, unsigned pathLen, unsigned* serialNr, double* bestScore, vector* bestPath, unsigned* bestSerialNr) { // debug("enumerate: pathLen=%1 serialNr=%2 url=%3", pathLen, *serialNr, // mapping->url()); // Update score to include "mapping" object score += mapping->weight; ++pathLen; if (mapping->prepend() == 0) { /* Landed at leaf of acyclic graph, i.e. mapping has no further "Label:" prepended to it. (In practice, mapping->url() is "http:" or "ftp:".). Check whether this path's score is a new maximum. */ //debug("enumerate: Leaf"); ++*serialNr; if (*serialNr == 0) { --*serialNr; return; // Whoa, overflow! Should Not Happen(tm) } // Score of path = SUM(scores_of_path_elements) / length_of_path double pathScore = score / implicit_cast(pathLen); if (pathScore > *bestScore && seen->find(*serialNr) == seen->end()) { debug("enumerate: New best score %1", pathScore); // New best score found *bestScore = pathScore; *bestSerialNr = *serialNr; bestPath->clear(); bestPath->push_back(mapping); StackEntry* s = stackPtr; while (s != 0) { bestPath->push_back(s->mapping); s = s->up; } } return; } // Not at leaf object - continue recursion StackEntry stack; stack.mapping = mapping; stack.up = stackPtr; mapping = mapping->prepend(); // Descend do { enumerate(&stack, mapping, score, pathLen, serialNr, bestScore, bestPath, bestSerialNr); // Recurse mapping = mapping->next(); // Walk through list of peers } while (mapping != 0); } jigdo-0.7.3/src/job/url-mapping.fh0000644000175000017500000000066610105633471016552 0ustar richardrichard/* $Id: url-mapping.fh,v 1.2 2004/08/09 08:35:05 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ class UrlMapping; class ServerUrlMapping; class PartUrlMapping; class UrlMap; jigdo-0.7.3/src/job/url-mapping.hh0000644000175000017500000002371410226256204016552 0ustar richardrichard/* $Id: url-mapping.hh,v 1.9 2005/04/10 17:04:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Representation of the directed, acyclic graph implied by the [Parts] and [Servers] lines in a .jigdo file. For each .jigdo file, we have one map > which allows to create a set of URLs for each MD5 sum. Cf MakeImageDl::parts. */ #ifndef URL_MAPPING_HH #define URL_MAPPING_HH #ifndef INLINE # ifdef NOINLINE # define INLINE # else # define INLINE inline # endif #endif #include #include #include #include #include #include #include #include #include #include #include #include /* for findLabelColon() */ #include //______________________________________________________________________ /** Object which represents a "Label:some/path" mapping, abstract base class. The "some/path" string is stored in the object, and the Label is represented with a pointer to another UrlMapping, whose output URL(s) need to be prepended to "some/path". UrlMappings can be chained into a linked list - that list is a list of alternative mappings for the same label. */ class UrlMapping : public SmartPtrBase, public NoCopy { friend class ServerUrlMapping; friend class PartUrlMapping; public: /** For url-mapping-test: Do not init weight randomly. */ static void setNoRandomInitialWeight(); UrlMapping(); virtual ~UrlMapping() = 0; /** Set value of URL part, starting with offset url[pos], up to n characters. */ inline void setUrl(const string& url, string::size_type pos = 0, string::size_type n = string::npos); /** Get URL value */ inline const string& url() const { return urlVal; } /** Parse options from .jigdo file: --try-first[=..], --try-last[=..]. Unrecognized parameters are ignored, to make extensions of the .jigdo file format easier. @return null on success, else error message. */ const char* parseOptions(const vector& value); /** If this UrlMapping is based on the string "Label:some/path" in a .jigdo file, then url()=="some/path" and prepend() points to the mapping(s) for "Label". */ inline void setPrepend(UrlMapping* um) { prepVal = um; } inline UrlMapping* prepend() const { return prepVal.get(); } /** Insert a mapping into the singly linked list, it will be returned by the next call to next(). */ inline void insertNext(UrlMapping* um); /** Return right peer of this object, or null. */ inline UrlMapping* next() const { return nextVal.get(); } /** Return true iff url().empty() && prepend() == 0 */ bool empty() const { return url().empty() && prepend() == 0; } /** Various knobs for the scoring algorithm */ /** If two servers are rated equal by the scoring algorithm, the order in which the servers are tried should be random. Otherwise, if gazillions of people try to download the same thing using default settings (e.g. no country preference), the first server in its list shouldn't be hit too hard. In practice, we achieve randomisation by initializing the weight with a small random value, in the range [-RANDOM_INIT_RANGE,RANDOM_INIT_RANGE) */ static const double RANDOM_INIT_RANGE; private: string urlVal; // Part of URL SmartPtr prepVal; // URL(s) to prepend to this one, or null SmartPtr nextVal; // Alt. to this mapping; singly linked list //LineInJigdoFilePointer def; // Definition of this mapping in .jigdo file // Statistics, for server selection // Nr of times we tried to download an URL generated using this mapping //unsigned tries; // 0 or 1 for PartUrlMapping, more possible for servers // Number of above tries which failed (404 not found, checksum error etc) //unsigned triesFailed; /* Mapping-specific weight, includes user's global country preference, preference for this jigdo download's servers, global server preference. Does not change throughout the whole jigdo download. Can get <0. The higher the value, the higher the preference that will be given to this mapping. */ double weight; }; //______________________________________________________________________ /** If the .jigdo file contains [Servers] entries like "Foo=x" and "Foo=y", there will be one object for the "Foo=x" entry, its "next" pointer points to the "Foo=y" object. Special case: The .jigdo data will contain URLs starting with any of "http: ftp: https: ftps: gopher: file:", those protocol labels also get their own ServerUrlMapping objects. */ class ServerUrlMapping : public UrlMapping { // server-specific options: Supports resume, ... // server-specific availability counts }; //______________________________________________________________________ /** Object to enumerate all URLs for "Label:some/path". The order of preference for the enumeration can change dynamically, i.e. if the score of a server entry is decreased after several failed attempts to download from it, future weight calculations involving that server will take the decreased weight into account. */ class PartUrlMapping : public UrlMapping { public: /** You must not call the addServer() method of this object's UrlMap after calling enumerate(). Otherwise, future results returned by this method will be complete nonsense. Enumerates all possible URLs, sorted by weight. Returns the empty string if all URLs enumerated. Internally, scans through the whole UrlMap each time, which can potentially take a long time. */ string enumerate(vector* best); private: /* Because the UrlMapping data structure is not a tree, but an acyclic directed graph (i.e. tree with some branches coming together again), need to record the path through the structure while we recurse. */ struct StackEntry { UrlMapping* mapping; StackEntry* up; }; void enumerate(StackEntry* stackPtr, UrlMapping* mapping, double score, unsigned pathLen, unsigned* serialNr, double* bestScore, vector* bestPath, unsigned* bestSerialNr); /* Set of URLs that were already returned by bestUnvisitedUrl(). Each URL is represented by a unique number, which is assigned to it by a depth-first scan of the tree-like structure in the UrlMap. */ auto_ptr > seen; }; //______________________________________________________________________ /** Object containing list of all Part and Server mappings in a .jigdo file */ class UrlMap : public NoCopy { public: inline UrlMap(); /** Add info about a mapping line inside one of the [Parts] sections in the .jigdo sections. The first entry of "value" is the URL (absolute, relative to baseUrl or in "Label:some/path form). The remaining "value" entries are assumed to be options. @return null if success, else error message */ const char* addPart(const string& baseUrl, const MD5& md, const vector& value); /** Like addPart(), but intended for maintaining lists of PartUrlMapping objects where the checksum is not known. Used to maintain lists of URLs for .template files. While it does not alter the checksum=>PartUrlMapping mappings of this UrlMap, it /may/ alter the Label=>ServerUrlMapping mappings, and the returned/appended PartUrlMapping will reference the server mappings of this UrlMap. @param baseUrl Base URL, in case value.front() is a relative URL @param value value.front() is the URL, followed by args for the part @param oldList Pointer to 0 SmartPtr for first call, will create list head, or pointer to SmartPtr to list head, will then add to list. */ const char* addPart(const string& baseUrl, const vector& value, SmartPtr* oldList); /** Add info about a [Servers] line, cf addPart(). For a line "Foobar=Label:some/path" in the [Servers] section: @param baseUrl Base URL, in case value.front() is a relative URL @param label == "Foobar" @param value arguments; value.front()=="Label:some/path" @return null if success, else error message */ const char* addServer(const string& baseUrl, const string& label, const vector& value); /** Output the graph built up by addPart()/addServer() to the log. */ void dumpJigdoInfo(); /* [Parts] lines in .jigdo data; for each md5sum, there's a linked list of PartUrlMappings */ typedef map > PartMap; /* [Servers] lines in .jigdo data; for each label string, there's a linked list of ServerUrlMappings */ typedef map > ServerMap; const PartMap& parts() const { return partsVal; } const ServerMap& servers() const { return serversVal; } /** Make lookups */ inline PartUrlMapping* operator[](const MD5& m) const; private: ServerUrlMapping* findOrCreateServerUrlMapping(const string& url, unsigned colon); PartMap partsVal; ServerMap serversVal; }; //====================================================================== void UrlMapping::insertNext(UrlMapping* um) { Paranoid(um != 0 && um->nextVal.isNull()); um->nextVal = nextVal; nextVal = um; } void UrlMapping::setUrl(const string& url, string::size_type pos, string::size_type n) { urlVal.assign(url, pos, n); } UrlMap::UrlMap() : partsVal(), serversVal() { } PartUrlMapping* UrlMap::operator[](const MD5& m) const { PartUrlMapping* result; PartMap::const_iterator i = parts().find(m); if (i != parts().end()) result = i->second.get(); else result = 0; return result; } #endif jigdo-0.7.3/src/mkimage.cc0000755000175000017500000011071210264021046015137 0ustar richardrichard/* $Id: mkimage.cc,v 1.15 2005/07/09 19:14:46 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Create image from template / merge new files into image.tmp */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("make-image") namespace { typedef JigdoDesc::ProgressReporter ProgressReporter; // memset() is not portable enough... void memClear(byte* buf, size_t size) { while (size > 8) { *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0; size -= 8; } while (size > 0) { *buf++ = 0; --size; } } } // local namespace //______________________________________________________________________ JigdoDesc::~JigdoDesc() { } //______________________________________________________________________ bool JigdoDesc::isTemplate(bistream& file) { if (!file.seekg(0, ios::beg)) return false; string l; getline(file, l); // "JigsawDownload template 1.0 jigdo-file/0.0.1" string templHdr = TEMPLATE_HDR; if (compat_compare(l, 0, templHdr.length(), templHdr) != 0) return false; getline(file, l); // Ignore comment line getline(file, l); // Empty line, except for CR if (l != "\r") return false; return true; } //______________________________________________________________________ void JigdoDesc::seekFromEnd(bistream& file) throw(JigdoDescError) { file.seekg(-6, ios::end); debug("JigdoDesc::seekFromEnd0: now at file offset %1", static_cast(file.tellg())); uint64 descLen; SerialIstreamIterator f(file); unserialize6(descLen, f); if (static_cast(file.tellg()) < descLen) { // Is this cast correct? debug("JigdoDesc::seekFromEnd1 descLen=%1", descLen); throw JigdoDescError(_("Invalid template data - corrupted file?")); } file.seekg(-descLen, ios::end); debug("JigdoDesc::seekFromEnd2: now at file offset %1", static_cast(file.tellg())); size_t toRead = 4; byte buf[4]; buf[3] = '\0'; byte* b = buf; do { readBytes(file, b, toRead); size_t n = file.gcount(); debug("JigdoDesc::seekFromEnd3: read %1, now at file offset %2", n, static_cast(file.tellg())); //cerr<<"read "< 0); if (buf[0] != 'D' || buf[1] != 'E' || buf[2] != 'S' || buf[3] != 'C') { debug("JigdoDesc::seekFromEnd4 %1 %2 %3 %4", int(buf[0]), int(buf[1]), int(buf[2]), int(buf[3])); throw JigdoDescError(_("Invalid template data - corrupted file?")); } } //______________________________________________________________________ bistream& JigdoDescVec::get(bistream& file) throw(JigdoDescError, bad_alloc) { /* Need auto_ptr: If we did a direct push_back(new JigdoDesc), the "new" might succeed, but the push_back() fail with bad_alloc => mem leak */ auto_ptr desc; clear(); SerialIstreamIterator f(file); uint64 len; unserialize6(len, f); // descLen - 16, i.e. length of entries if (len < 45 || len > 256*1024*1024) { debug("JigdoDescVec::get: DESC section too small/large"); throw JigdoDescError(_("Invalid template data - corrupted file?")); } len -= 16; //____________________ uint64 off = 0; // Offset in image uint64 read = 0; // Nr of bytes read MD5 entryMd5; uint64 entryLen; RsyncSum64 rsum; size_t blockLength; while (file && read < len) { byte type = *f; ++f; switch (type) { case JigdoDesc::IMAGE_INFO: unserialize6(entryLen, f); unserialize(entryMd5, f); unserialize4(blockLength, f); if (!file) break; debug("JigdoDesc::read: ImageInfo %1 %2", entryLen, entryMd5.toString()); desc.reset(new JigdoDesc::ImageInfo(entryLen, entryMd5, blockLength)); push_back(desc.release()); read += 1 + 6 + entryMd5.serialSizeOf() + 4; break; case JigdoDesc::UNMATCHED_DATA: unserialize6(entryLen, f); if (!file) break; debug("JigdoDesc::read: %1 UnmatchedData %2", off, entryLen); desc.reset(new JigdoDesc::UnmatchedData(off, entryLen)); push_back(desc.release()); read += 1 + 6; off += entryLen; break; case JigdoDesc::MATCHED_FILE: case JigdoDesc::WRITTEN_FILE: unserialize6(entryLen, f); unserialize(rsum, f); unserialize(entryMd5, f); if (!file) break; debug("JigdoDesc::read: %1 %2File %3 %4", off, (type == JigdoDesc::MATCHED_FILE ? "Matched" : "Written"), entryLen, entryMd5.toString()); if (type == JigdoDesc::MATCHED_FILE) desc.reset(new JigdoDesc::MatchedFile(off, entryLen, rsum,entryMd5)); else desc.reset(new JigdoDesc::WrittenFile(off, entryLen, rsum,entryMd5)); push_back(desc.release()); read += 1 + 6 + rsum.serialSizeOf() + entryMd5.serialSizeOf(); off += entryLen; break; // Template entry types that were obsoleted with version 0.6.3: case JigdoDesc::OBSOLETE_IMAGE_INFO: unserialize6(entryLen, f); unserialize(entryMd5, f); if (!file) break; debug("JigdoDesc::read: old ImageInfo %1 %2", entryLen, entryMd5.toString()); // Special case: passing blockLength==0, which is otherwise impossible desc.reset(new JigdoDesc::ImageInfo(entryLen, entryMd5, 0)); push_back(desc.release()); read += 1 + 6 + entryMd5.serialSizeOf(); break; case JigdoDesc::OBSOLETE_MATCHED_FILE: case JigdoDesc::OBSOLETE_WRITTEN_FILE: unserialize6(entryLen, f); unserialize(entryMd5, f); if (!file) break; debug("JigdoDesc::read: %1 old %2File %3 %4", off, (type == JigdoDesc::OBSOLETE_MATCHED_FILE ? "Matched" : "Written"), entryLen, entryMd5.toString()); /* Value of rsum is "don't care" because the ImageInfo's blockLength will be zero. */ rsum.reset(); if (type == JigdoDesc::OBSOLETE_MATCHED_FILE) desc.reset(new JigdoDesc::MatchedFile(off, entryLen, rsum,entryMd5)); else desc.reset(new JigdoDesc::WrittenFile(off, entryLen, rsum,entryMd5)); push_back(desc.release()); read += 1 + 6 + entryMd5.serialSizeOf(); off += entryLen; break; default: debug("JigdoDesc::read: unknown type %1", type); throw JigdoDescError(_("Invalid template data - corrupted file?")); } } //____________________ if (read < len) { string err = subst(_("Error reading template data (%1)"), strerror(errno)); throw JigdoDescError(err); } if (empty()) throw JigdoDescError(_("Invalid template data - corrupted file?")); JigdoDesc::ImageInfo* ii = dynamic_cast(back()); if (ii == 0 || ii->size() != off) { if (ii != 0) debug("JigdoDesc::read4: %1 != %2", ii->size(), off); throw JigdoDescError(_("Invalid template data - corrupted file?")); } return file; } //______________________________________________________________________ bostream& JigdoDescVec::put(bostream& file, MD5Sum* md) const { // Pass 1: Accumulate sizes of entries, calculate descLen // 4 for DESC, 6 each for length of part at start & end uint64 descLen = 4 + 6*2; // Length of DESC part unsigned bufLen = 4 + 6; for (const_iterator i = begin(), e = end(); i != e; ++i) { unsigned s = (*i)->serialSizeOf(); bufLen = max(bufLen, s); descLen += s; } if (DEBUG) bufLen += 1; // Pass 2: Write DESC part byte buf[bufLen]; if (DEBUG) buf[bufLen - 1] = 0xa5; byte* p; p = serialize4(0x43534544, buf); // "DESC" in little-endian order p = serialize6(descLen, p); writeBytes(file, buf, 4 + 6); if (md != 0) md->update(buf, 4 + 6); for (const_iterator i = begin(), e = end(); i != e; ++i) { JigdoDesc::ImageInfo* info; JigdoDesc::UnmatchedData* unm; JigdoDesc::MatchedFile* matched; JigdoDesc::WrittenFile* written; /* NB we must first try to cast to WrittenFile, then to MatchedFile, because WrittenFile derives from MatchedFile. */ if ((info = dynamic_cast(*i)) != 0) p = info->serialize(buf); else if ((unm = dynamic_cast(*i)) != 0) p = unm->serialize(buf); else if ((written = dynamic_cast(*i)) != 0) p = written->serialize(buf); else if ((matched = dynamic_cast(*i)) != 0) p = matched->serialize(buf); else { Assert(false); continue; } writeBytes(file, buf, p - buf); if (md != 0) md->update(buf, p - buf); } p = serialize6(descLen, buf); writeBytes(file, buf, 6); if (md != 0) md->update(buf, 6); if (DEBUG) { Assert(buf[bufLen - 1] == 0xa5); } return file; } //______________________________________________________________________ namespace { const int SIZE_WIDTH = 12; } ostream& JigdoDesc::ImageInfo::put(ostream& s) const { s << "image-info " << setw(SIZE_WIDTH) << size() << " " << md5() << ' ' << blockLength() << '\n'; return s; } ostream& JigdoDesc::UnmatchedData::put(ostream& s) const { s << "in-template " << setw(SIZE_WIDTH) << offset() << ' ' << setw(SIZE_WIDTH) << size() << '\n'; return s; } ostream& JigdoDesc::MatchedFile::put(ostream& s) const { s << "need-file " << setw(SIZE_WIDTH) << offset() << ' ' << setw(SIZE_WIDTH) << size() << ' ' << md5() << ' ' << rsync() << '\n'; return s; } ostream& JigdoDesc::WrittenFile::put(ostream& s) const { s << "have-file " << setw(SIZE_WIDTH) << offset() << ' ' << setw(SIZE_WIDTH) << size() << ' ' << md5() << ' ' << rsync() << '\n'; return s; } void JigdoDescVec::list(ostream& s) throw() { for (const_iterator i = begin(), e = end(); i != e; ++i) s << (**i); s << flush; } //______________________________________________________________________ namespace { /* Helper functions for makeImage below, declared inline if only used once */ /// Type of operation when recreating image data enum Task { CREATE_TMP, // Create a new .tmp file and copy some files into it, // maybe rename at end MERGE_TMP, // .tmp exists, copy over more files, maybe rename at end SINGLE_PASS // single-pass, all or nothing; writing to stdout }; //______________________________ inline void reportBytesWritten(const uint64 n, uint64& off, uint64& nextReport, const uint64 totalBytes, ProgressReporter& reporter) { off += n; if (off >= nextReport) { // Keep user entertained reporter.writingImage(off, totalBytes, off, totalBytes); nextReport += REPORT_INTERVAL; } } //______________________________ /* Read up to file.size() of bytes from file, write it to image stream. Check MD5/rsync sum if requested. Take care not to write more than specified amount to image, even if file is longer. */ int fileToImage(bostream* img, FilePart& file, const JigdoDesc::MatchedFile& matched, bool checkMD5, size_t rsyncLen, ProgressReporter& reporter, byte* buf, size_t readAmount, uint64& off, uint64& nextReport, const uint64 totalBytes) { uint64 toWrite = file.size(); MD5Sum md; RsyncSum64 rs; size_t rl = 0; // Length covered by rs so far string fileName(file.getPath()); fileName += file.leafName(); bifstream f(fileName.c_str(), ios::binary); string err; // !err.empty() => error occurred // Read from file, write to image // First couple of k: Calculate RsyncSum rs and MD5Sum md if (checkMD5 && rsyncLen > 0) { while (*img && f && !f.eof() && toWrite > 0) { size_t n = (toWrite < readAmount ? toWrite : readAmount); readBytes(f, buf, n); n = f.gcount(); writeBytes(*img, buf, n); reportBytesWritten(n, off, nextReport, totalBytes, reporter); toWrite -= n; md.update(buf, n); // Update RsyncSum Paranoid(rl < rsyncLen); size_t rsyncToAdd = rsyncLen - rl; if (rsyncToAdd > n) rsyncToAdd = n; rs.addBack(buf, rsyncToAdd); rl += rsyncToAdd; Paranoid(rl <= rsyncLen); if (rl >= rsyncLen) break; } } // Rest of file: Only calculate MD5Sum md while (*img && f && !f.eof() && toWrite > 0) { size_t n = (toWrite < readAmount ? toWrite : readAmount); readBytes(f, buf, n); n = f.gcount(); writeBytes(*img, buf, n); reportBytesWritten(n, off, nextReport, totalBytes, reporter); toWrite -= n; if (checkMD5) md.update(buf, n); } if (toWrite > 0 && (!f || f.eof())) { const char* errDetail = ""; if (errno != 0) errDetail = strerror(errno); else if (f.eof()) errDetail = _("file is too short"); err = subst(_("Error reading from `%1' (%2)"), fileName, errDetail); // Even if there was an error - always try to write right amount memClear(buf, readAmount); while (*img && toWrite > 0) { size_t n = (toWrite < readAmount ? toWrite : readAmount); writeBytes(*img, buf, n); reportBytesWritten(n, off, nextReport, totalBytes, reporter); toWrite -= n; } } else if (checkMD5 && (md.finish() != matched.md5() || (rsyncLen > 0 && rs != matched.rsync()))) { err = subst(_("Error: `%1' does not match checksum in template data"), fileName); } if (err.empty()) return 0; // Success reporter.error(err); if (toWrite == 0) return 2; // "May have to fix something before you can continue" else return 3; // Yaargh, disaster! Please delete the .tmp file for me } //______________________________ /* Write all bytes of the image data, i.e. both UnmatchedData and MatchedFiles. If any UnmatchedFiles are present in 'files', write zeroes instead of the file content and also append a DESC section after the actual data. Why does this write zeroes, and not simply seek() forward the appropriate amount of bytes? - Because when seek() is used, a sparse file might be generated. This could result in "No room on device" later on - but we'd rather like that error as early as possible. @param name Filename corresponding to img @param totalBytes length of image if img==0, write to cout. If 0 is returned and not writing to cout, caller should rename file to remove .tmp extension. */ inline int writeAll(const Task& task, JigdoDescVec& files, queue& toCopy, bistream* templ, const size_t readAmount, bostream* img, const char* name, bool checkMD5, ProgressReporter& reporter, JigdoCache* cache, const uint64 totalBytes) { bool isTemplate = JigdoDesc::isTemplate(*templ); // seek to 1st DATA part Assert(isTemplate); int result = 0; uint64 off = 0; // Current offset in image uint64 nextReport = 0; // At what value of off to call reporter vector bufVec(readAmount); byte* buf = &bufVec[0]; /* Use an additional 8k of zip buffer. This is good if the unmatched image data is already compressed, which means that when it is compressed again by jigdo, it will get slightly larger. */ auto_ptr data(new Zibstream(*templ, readAmount + 8*1024)); # if HAVE_WORKING_FSTREAM if (img == 0) img = &cout; // EEEEEK! # else if (img == 0) img = &bcout; # endif JigdoDesc::ImageInfo& imageInfo = dynamic_cast(*files.back()); try { for (JigdoDescVec::iterator i = files.begin(), e = files.end(); i != e; ++i) { //____________________ /* Write all data for this part to img stream. In case of MatchedFile, write the appropriate number of bytes (of junk data) even if file not present. [Using switch(type()) not nice, but using virtual methods looks even worse.] */ switch ((*i)->type()) { case JigdoDesc::IMAGE_INFO: break; case JigdoDesc::UNMATCHED_DATA: { // Copy data from Zibstream to image. JigdoDesc::UnmatchedData& self = dynamic_cast(**i); uint64 toWrite = self.size(); debug("mkimage writeAll(): %1 of unmatched data", toWrite); memClear(buf, readAmount); while (*img && toWrite > 0) { if (!*data) { reporter.error(_("Premature end of template data")); return 3; } data->read(buf, (toWrite < readAmount ? toWrite : readAmount)); size_t n = data->gcount(); writeBytes(*img, buf, n); reportBytesWritten(n, off, nextReport, totalBytes, reporter); toWrite -= n; } break; } case JigdoDesc::MATCHED_FILE: { /* If file present in cache, copy its data to image, if not, copy zeroes. if check==true, verify MD sum match. If successful, turn MatchedFile into WrittenFile. */ JigdoDesc::MatchedFile* self = dynamic_cast(*i); uint64 toWrite = self->size(); FilePart* mfile = 0; if (!toCopy.empty()) mfile = toCopy.front(); debug("mkimage writeAll(): FilePart@%1, %2 of matched file `%3'," " toCopy size %4", mfile, toWrite, (mfile != 0 ? mfile->leafName() : ""), toCopy.size()); if (mfile == 0 || self->md5() != *(mfile->getMD5Sum(cache))) { // Write right amount of zeroes memClear(buf, readAmount); while (*img && toWrite > 0) { size_t n = (toWrite < readAmount ? toWrite : readAmount); writeBytes(*img, buf, n); reportBytesWritten(n, off, nextReport, totalBytes, reporter); toWrite -= n; } if (result == 0) result = 1; // Soft failure } else { /* Copy data from file to image, taking care not to write beyond toWrite. */ int status = fileToImage(img, *mfile, *self, checkMD5, imageInfo.blockLength(), reporter, buf, readAmount, off, nextReport, totalBytes); toCopy.pop(); if (result < status) result = status; if (status == 0) { // Mark file as written to image *i = new JigdoDesc::WrittenFile(self->offset(), self->size(), self->rsync(), self->md5()); delete self; } else if (*img && (status > 2 || task == SINGLE_PASS)) { // If !*img, exit after error msg below /* If status <= 2 and task == {CREATE_TMP,MERGE_TMP}, we can continue; there has been an error copying this individual file, but the right *amount* of data has been written to the .tmp output file, and the user may retry the failed one later. */ return result; } } break; } case JigdoDesc::WRITTEN_FILE: // These are never present in memory, cannot occur: case JigdoDesc::OBSOLETE_IMAGE_INFO: case JigdoDesc::OBSOLETE_MATCHED_FILE: case JigdoDesc::OBSOLETE_WRITTEN_FILE: debug("mkimage writeAll(): invalid type %1", (*i)->type()); reporter.error( _("Error - template data's DESC section invalid")); Assert(false); // A WrittenFile cannot occur here return 3; break; } //____________________ // Error while writing to image? if (!*img) { string err = subst(_("Error while writing to `%1' (%2)"), name, strerror(errno)); reporter.error(err); return 3; } //____________________ } // end iterating over 'files' } catch (Zerror e) { // Error while unpacking template data reporter.error(e.message); return 3; } // If we created a new tmp file, append DESC info if (task == CREATE_TMP && result > 0) { *img << files; if (!*img) return 3; } // Must have "used up" all the parts that we found earlier Assert(toCopy.empty()); return result; // 0 or 1 } //______________________________ /* A temporary file already exists. Write the files listed in toCopy to this temporary file. If image is now completed, truncate it to its final length (removing the DESC section at the end), otherwise update the DESC section (turn some need-file/ MatchedFile entries into have-file/WrittenFile entries). If 0 is returned, caller should rename file to remove .tmp extension. */ inline int writeMerge(JigdoDescVec& files, queue& toCopy, const int missing, const size_t readAmount, bfstream* img, const string& imageTmpFile, bool checkMD5, ProgressReporter& reporter, JigdoCache* cache, const uint64 totalBytes) { vector bufVec(readAmount); byte* buf = &bufVec[0]; int result = (missing == 0 ? 0 : 1); uint64 bytesWritten = 0; // For 'x% done' calls to reporter uint64 nextReport = 0; // At what value of bytesWritten to call reporter JigdoDesc::ImageInfo& imageInfo = dynamic_cast(*files.back()); if (toCopy.empty() && missing > 0) return 1; for (JigdoDescVec::iterator i = files.begin(), e = files.end(); i != e; ++i) { // Compare to 'case JigdoDesc::MATCHED_FILE:' clause in writeAll() JigdoDesc::MatchedFile* self = dynamic_cast(*i); if (self == 0) continue; FilePart* mfile = 0; if (!toCopy.empty()) mfile = toCopy.front(); debug("mkimage writeMerge(): FilePart@%1, %2 of matched file `%3', " "toCopy size %4", mfile, self->size(), (mfile != 0 ? mfile->leafName() : ""), toCopy.size()); if (mfile == 0 || self->md5() != *(mfile->getMD5Sum(cache))) continue; /* Copy data from file to image, taking care not to write beyond self->size(). */ img->seekp(self->offset(), ios::beg); if (!*img) { reporter.error(_("Error - could not access temporary file")); result = 2; break; } int status = fileToImage(img, *mfile, *self, checkMD5, imageInfo.blockLength(), reporter, buf, readAmount, bytesWritten, nextReport, totalBytes); toCopy.pop(); if (result < status) result = status; if (status == 0) { // Mark file as written to image *i = new JigdoDesc::WrittenFile(self->offset(), self->size(), self->rsync(), self->md5()); delete self; } else if (status > 2) { break; } } // end iterating over 'files' uint64 imageSize = imageInfo.size(); if (missing == 0 && result == 0) { img->close(); // Necessary on Windows before truncating is possible // Truncate to final image size const char* tmpName = imageTmpFile.c_str(); if (compat_truncate(tmpName, imageSize) != 0) { string err = subst(_("Could not truncate `%1' (%2)"), imageTmpFile, strerror(errno)); reporter.error(err); return 3; } return 0; } else { // Update DESC section at end of temporary file img->seekp(imageSize); // No need to truncate here because DESC section never changes size *img << files; if (!*img) return 3; return result; } } //______________________________ int info_NeedMoreFiles(ProgressReporter& reporter, const string& tmpName) { string info = subst(_( "Copied input files to temporary file `%1' - " "repeat command and supply more files to continue"), tmpName); reporter.info(info); return 1; // Soft failure } int error_CouldntRename(ProgressReporter& reporter, const char* name, const char* finalName) { string err = subst(_( "Could not move finished image from `%1' to `%2' (%3)"), name, finalName, strerror(errno)); reporter.error(err); return 3; } } // end local namespace //______________________________________________________________________ namespace { /// Read template data from templ (name in templFile) into files void readTemplate(JigdoDescVec& files, const string& templFile, bistream* templ) { if (JigdoDesc::isTemplate(*templ) == false) { // Check for template hdr string err = subst(_("`%1' is not a template file"), templFile); throw JigdoDescError(err); } /* Read info at end of template data. NB: Exceptions are not caught here, but e.g. in ::makeImage() (cf. jigdo-file.cc) */ JigdoDesc::seekFromEnd(*templ); *templ >> files; } //________________________________________ /** Read data from end of temporary file imageTmp, output it to filesTmp. Next, compare it to template data in "files". If tmp file is OK for re-using return NULL - this means that the DESC entries match *exactly* - the only difference allowed is MatchedFile turning into WrittenFile. Otherwise, return a pointer to an error message describing the reason why the tmpfile data does not match the template data. */ const char* readTmpFile(bistream& imageTmp, JigdoDescVec& filesTmp, const JigdoDescVec& files) { try { JigdoDesc::seekFromEnd(imageTmp); imageTmp >> filesTmp; } catch (JigdoDescError e) { return _("it was not created by jigdo-file, or is corrupted."); } if (*files.back() != *filesTmp.back()) return _("it corresponds to a different image/template."); if (files.size() != filesTmp.size()) return _("since its creation, the template was regenerated."); for (size_t i = 0; i < files.size() - 1; ++i) { //cerr << "cmp " << i << '/' << (files.size() - 1) << endl; if (*files[i] != *filesTmp[i]) return _("since its creation, the template was regenerated."); } return 0; } } //________________________________________ /* If imageTmpFile.empty(), must either write whole image or nothing at all. image and temporary file are created as needed, ditto for renaming of temporary to image. The cache must not throw errors. */ int JigdoDesc::makeImage(JigdoCache* cache, const string& imageFile, const string& imageTmpFile, const string& templFile, bistream* templ, const bool optForce, ProgressReporter& reporter, const size_t readAmount, const bool optMkImageCheck) { Task task = CREATE_TMP; if (imageFile == "-" || imageTmpFile.empty()) task = SINGLE_PASS; //____________________ // Read info from template JigdoDescVec files; readTemplate(files, templFile, templ); //____________________ // Do we need to add new stuff to an existing tmp file? bfstream* img = 0; // Non-null => tmp file exists auto_ptr imgDel(img); struct stat fileInfo; if (task != SINGLE_PASS && stat(imageTmpFile.c_str(), &fileInfo) == 0) { /* A tmp file already exists. We'll only reuse it if the DESC entries match exactly. Otherwise, if --force enabled, overwrite it, else error. */ const char* wontReuse = 0; // non-NULL means: will not reuse tmp file JigdoDescVec filesTmp; imgDel.reset(new bfstream(imageTmpFile.c_str(), ios::binary|ios::in|ios::out)); img = imgDel.get(); if (!*img) wontReuse = strerror(errno); else wontReuse = readTmpFile(*img, filesTmp, files); if (wontReuse != 0) { // Print out message string msg = subst(_("Will not reuse existing temporary file `%1' - " "%2"), imageTmpFile, wontReuse); // Return error if not allowed to overwrite tmp file if (!optForce) { reporter.error(msg); throw Error(_("Delete/rename the file or use --force")); } // Open a new tmp file later (imgDel will close() this one for us) reporter.info(msg); img = 0; Paranoid(task == CREATE_TMP); } else { // Reuse temporary file task = MERGE_TMP; files.swap(filesTmp); Assert(!filesTmp.empty() && img != 0); } } // endif (tmp file exists) Paranoid((task == MERGE_TMP) == (img != 0)); //____________________ /* Variables now in use: enum task: Mode of operation (CREATE_TMP/MERGE_TMP/SINGLE_PASS) JigdoDescVec files: Contents of image, maybe with some WrittenFiles if MERGEing istream* templ: Template data (stream pointer at end of file) fstream* img: Temporary file if MERGE_TMP, else null */ /* Create queue of files that need to be copied to the image. Later on, we will be pop()ing to get to the actual filenames in order. Referenced FileParts are owned by the JigdoCache - never delete them. */ queue toCopy; int missing = 0; // Nr of files that were not found JigdoCache::iterator ci, ce = cache->end(); uint64 totalBytes = 0; // Total amount of data to be written, for "x% done" for (vector::iterator i = files.begin(), e = files.end(); i != e; ++i) { // Need this extra test because we do *not* want the WrittenFiles if ((*i)->type() != MATCHED_FILE) continue; MatchedFile* m = dynamic_cast(*i); Paranoid(m != 0); //totalBytes += m->size(); // Search for file with matching MD5 sum ci = cache->begin(); bool found = false; while (ci != ce) { // The call to getMD5Sum() may cause the whole file to be read! const MD5Sum* md = ci->getMD5Sum(cache); if (md != 0 && *md == m->md5()) { toCopy.push(&*ci); // Found matching file totalBytes += m->size(); debug("%1 found, pushed %2", m->md5().toString(), &*ci); found = true; break; } ++ci; } if (!found) ++missing; } //____________________ debug("JigdoDesc::mkImage: %1 missing, %2 found for copying to image, " "%3 entries in template", missing, toCopy.size(), files.size()); // Files appearing >1 times are counted >1 times for the message string missingInfo = subst( _("Found %1 of the %2 files required by the template"), toCopy.size(), toCopy.size() + missing); reporter.info(missingInfo); //____________________ /* There used to be the following here: | If possible (i.e. all files present, tmp file not yet created), | avoid creating any tmp file at all. | if (task == CREATE_TMP && missing == 0) task = SINGLE_PASS; We do not do this because even though it says "missing==0" *now*, there could be a read error from one of the files when we actually access it, in which case we should be able to ignore the error for the moment, and leave behind a partially complete .tmp file. */ /* Do nothing at all if a) no tmp file created yet, and b) *none* of the supplied files matched one of the missing parts, and c) the template actually contains at least one MatchedFile (i.e. *do* write if template consists entirely of UnmatchedData). */ # ifndef MKIMAGE_ALWAYS_CREATE_TMPFILE if (task == CREATE_TMP && toCopy.size() == 0 && missing != 0) { const char* m = _("Will not create image or temporary file - try again " "with different input files"); reporter.info(m); return 1; // Return value: "Soft failure - may retry with more files" } // Give error if unable to create image in one pass if (task == SINGLE_PASS && missing > 0) { reporter.error(_("Cannot create image because of missing files")); return 3; // Permanent failure } # endif //____________________ if (task == MERGE_TMP) { // If MERGEing, img was already set up above int result = writeMerge(files, toCopy, missing, readAmount, img, imageTmpFile, optMkImageCheck, reporter, cache, totalBytes); if (missing != 0 && result < 3) info_NeedMoreFiles(reporter, imageTmpFile); if (result == 0) { if (compat_rename(imageTmpFile.c_str(), imageFile.c_str()) != 0) return error_CouldntRename(reporter, imageTmpFile.c_str(), imageFile.c_str()); string info = subst(_("Successfully created `%1'"), imageFile); reporter.info(info); } return result; } // task == CREATE_TMP || task == SINGLE_PASS // Assign a stream to img which we're going to write image data to // If necessary, create a new temporary/output file const char* name; const char* finalName = 0; if (task == CREATE_TMP) { // CREATE new tmp file name = imageTmpFile.c_str(); finalName = imageFile.c_str(); imgDel.reset(new bfstream(name, ios::binary|ios::out|ios::trunc)); img = imgDel.get(); } else if (imageFile != "-") { // SINGLE_PASS; create output file name = imageFile.c_str(); imgDel.reset(new bfstream(name, ios::binary|ios::out|ios::trunc)); img = imgDel.get(); } else { // SINGLE_PASS, outputting to stdout name = "-"; imgDel.reset(0); img = 0; // Cannot do "img = &cout", so img==0 is special case: stdout } if (img != 0 && !*img) { string err = subst(_("Could not open `%1' for output: %2"), name, strerror(errno)); reporter.error(err); return 3; // Permanent failure } /* Above, totalBytes was calculated for the case of a MERGE_TMP. If we're not merging, we need to write everything. */ Assert(files.back()->type() == IMAGE_INFO); uint64 imageSize = files.back()->size(); totalBytes = imageSize; # if 0 /* # if WINDOWS */ /* The C++ STL of the MinGW 1.1 gcc port for Windows doesn't support files >2GB. Fail early and with a clear error message... */ if (imageSize >= (1U<<31)) throw Error(_("Sorry, at the moment the Windows port of jigdo cannot " "create files bigger than 2 GB. Use the Linux version.")); # endif int result = writeAll(task, files, toCopy, templ, readAmount, img, name, optMkImageCheck, reporter, cache, totalBytes); if (result >= 3) return result; if (task == CREATE_TMP && result == 1) { info_NeedMoreFiles(reporter, imageTmpFile); } else if (result == 0) { if (img != 0) img->close(); // Necessary on Windows before renaming is possible if (finalName != 0 && compat_rename(name, finalName) != 0) return error_CouldntRename(reporter, name, finalName); string info = subst(_("Successfully created `%1'"), imageFile); reporter.info(info); } return result; } //______________________________________________________________________ int JigdoDesc::listMissing(set& result, const string& imageTmpFile, const string& templFile, bistream* templ, ProgressReporter& reporter) { result.clear(); // Read info from template JigdoDescVec contents; readTemplate(contents, templFile, templ); // Read info from temporary file, if any if (!imageTmpFile.empty()) { bifstream imageTmp(imageTmpFile.c_str(), ios::binary); if (imageTmp) { JigdoDescVec contentsTmp; const char* wontReuse = readTmpFile(imageTmp, contentsTmp, contents); if (wontReuse != 0) { string msg = subst(_("Ignoring existing temporary file `%1' - %2"), imageTmpFile, wontReuse); reporter.info(msg); } else { // tmp file present & valid - use *it* below to output missing parts swap(contents, contentsTmp); } } } // Output MD5 sums of MatchedFile (but not WrittenFile) entries for (size_t i = 0; i < contents.size() - 1; ++i) { MatchedFile* mf = dynamic_cast(contents[i]); if (mf != 0 && mf->type() == MATCHED_FILE) result.insert(mf->md5()); } return 0; } //______________________________________________________________________ void JigdoDesc::ProgressReporter::error(const string& message) { cerr << message << endl; } void JigdoDesc::ProgressReporter::info(const string& message) { cerr << message << endl; } void JigdoDesc::ProgressReporter::writingImage(uint64, uint64, uint64, uint64) { } JigdoDesc::ProgressReporter JigdoDesc::noReport; jigdo-0.7.3/src/mkimage.hh0000644000175000017500000003037310121135314015146 0ustar richardrichard/* $Id: mkimage.hh,v 1.3 2004/09/12 21:08:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Create image from template / merge new files into image.tmp */ #ifndef MKIMAGE_HH #define MKIMAGE_HH #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ /** Errors thrown by the JigdoDesc code */ struct JigdoDescError : Error { explicit JigdoDescError(const string& m) : Error(m) { } explicit JigdoDescError(const char* m) : Error(m) { } }; /** Entry in a DESC section of a jigdo template. This definition is used both for an abstract base class and as a namespace for child classes. */ class JigdoDesc { public: /** Types of entries in a description section */ enum Type { IMAGE_INFO = 5, UNMATCHED_DATA = 2, MATCHED_FILE = 6, WRITTEN_FILE = 7, OBSOLETE_IMAGE_INFO = 1, OBSOLETE_MATCHED_FILE = 3, OBSOLETE_WRITTEN_FILE = 4 }; class ProgressReporter; //____________________ virtual bool operator==(const JigdoDesc& x) const = 0; inline bool operator!=(const JigdoDesc& x) const { return !(*this == x); } virtual ~JigdoDesc() = 0; /** Entry type of JigdoDesc child class */ virtual Type type() const = 0; /** Output human-readable summary */ virtual ostream& put(ostream& s) const = 0; /** Size of image area or whole image */ virtual uint64 size() const = 0; /** There are no virtual templates, so this wouldn't work: template virtual Iterator serialize(Iterator i) const; */ virtual size_t serialSizeOf() const = 0; /** Check whether the file is a .template file, i.e. has the appropriate ASCII header */ static bool isTemplate(bistream& file); /** Assuming that a DESC section is at the end of a file, set the file pointer to the start of the section, allowing you to call read() immediately afterwards. */ static void seekFromEnd(bistream& file) throw(JigdoDescError); /** Create image file from template and files (via JigdoCache) */ static int makeImage(JigdoCache* cache, const string& imageFile, const string& imageTmpFile, const string& templFile, bistream* templ, const bool optForce, ProgressReporter& pr = noReport, size_t readAmnt = 128U*1024, const bool optMkImageCheck = true); /** Return list of MD5sums of files that still need to be copied to the image to complete it. Reads info from tmp file or (if imageTmpFile.empty() or error opening tmp file) outputs complete list from template. */ static int listMissing(set& result, const string& imageTmpFile, const string& templFile, bistream* templ, ProgressReporter& reporter); class ImageInfo; class UnmatchedData; class MatchedFile; class WrittenFile; private: static ProgressReporter noReport; }; inline ostream& operator<<(ostream& s, JigdoDesc& jd) { return jd.put(s); } //______________________________________________________________________ /** Information about the image file */ class JigdoDesc::ImageInfo : public JigdoDesc { public: inline ImageInfo(uint64 s, const MD5& m, size_t b); inline ImageInfo(uint64 s, const MD5Sum& m, size_t b); inline bool operator==(const JigdoDesc& x) const; Type type() const { return IMAGE_INFO; } uint64 size() const { return sizeVal; } const MD5& md5() const { return md5Val; } size_t blockLength() const { return blockLengthVal; } // Default dtor, operator== virtual ostream& put(ostream& s) const; template inline Iterator serialize(Iterator i) const; inline size_t serialSizeOf() const; private: uint64 sizeVal; MD5 md5Val; size_t blockLengthVal; }; //________________________________________ /** Info about data that was not matched by any input file, i.e. that is included in the template data verbatim */ class JigdoDesc::UnmatchedData : public JigdoDesc { public: UnmatchedData(uint64 o, uint64 s) : offsetVal(o), sizeVal(s) { } inline bool operator==(const JigdoDesc& x) const; Type type() const { return UNMATCHED_DATA; } uint64 offset() const { return offsetVal; } uint64 size() const { return sizeVal; } void resize(uint64 s) { sizeVal = s; } // Default dtor, operator== virtual ostream& put(ostream& s) const; template inline Iterator serialize(Iterator i) const; inline size_t serialSizeOf() const; private: uint64 offsetVal; // Offset in image uint64 sizeVal; }; //________________________________________ /** Info about data that *was* matched by an input file */ class JigdoDesc::MatchedFile : public JigdoDesc { public: inline MatchedFile(uint64 o, uint64 s, const RsyncSum64& r, const MD5& m); inline MatchedFile(uint64 o, uint64 s, const RsyncSum64& r, const MD5Sum& m); inline bool operator==(const JigdoDesc& x) const; Type type() const { return MATCHED_FILE; } uint64 offset() const { return offsetVal; } uint64 size() const { return sizeVal; } const MD5& md5() const { return md5Val; } const RsyncSum64& rsync() const { return rsyncVal; } // Default dtor, operator== virtual ostream& put(ostream& s) const; template inline Iterator serialize(Iterator i) const; inline size_t serialSizeOf() const; private: uint64 offsetVal; // Offset in image uint64 sizeVal; RsyncSum64 rsyncVal; MD5 md5Val; }; //________________________________________ /** Like MatchedFile - used only in .tmp files to express that the file data was successfully written to the image. NB: Because this derives from MatchedFile and because of the implementation of JigdoDesc::operator==, MatchedFile's and WrittenFile's will compare equal if their data fields are identical. */ class JigdoDesc::WrittenFile : public MatchedFile { public: WrittenFile(uint64 o, uint64 s, const RsyncSum64& r, const MD5& m) : MatchedFile(o, s, r, m) { } // Implicit cast to allow MatchedFile and WrittenFile to compare equal inline bool operator==(const JigdoDesc& x) const; Type type() const { return WRITTEN_FILE; } virtual ostream& put(ostream& s) const; template inline Iterator serialize(Iterator i) const; inline size_t serialSizeOf() const; }; //______________________________________________________________________ /** Class allowing JigdoDesc to convey information back to the caller. The default versions of the methods do nothing at all (except for error(), which prints the error to cerr) - you need to supply an object of a derived class to functions to get called back. */ class JigdoDesc::ProgressReporter { public: virtual ~ProgressReporter() { } /** General-purpose error reporting. */ virtual void error(const string& message); /** Like error(), but for purely informational messages. */ virtual void info(const string& message); /** Called when the output image (or a temporary file) is being written to. It holds that written==imgOff and totalToWrite==imgSize, *except* when additional files are merged into an already existing temporary file. @param written Number of bytes written so far @param totalToWrite Value of 'written' at end of write operation @param imgOff Current offset in image @param imgSize Total size of output image */ virtual void writingImage(uint64 written, uint64 totalToWrite, uint64 imgOff, uint64 imgSize); }; //______________________________________________________________________ /** Container for JidoDesc objects. Is mostly a vector, but deletes elements when destroyed. However, when removing elements, resizing the JigdoDescVec etc., the elements are *not* deleted automatically. */ class JigdoDescVec : public vector { public: JigdoDescVec() : vector() { } inline ~JigdoDescVec(); /** Read JigdoDescs from a template file into *this. *this is clear()ed first. File pointer must be at start of first entry; the "DESC" must have been read already. If error is thrown, position of file pointer is undefined. A type 1 (IMAGE_INFO) will end up at this->back(). */ bistream& get(bistream& file) throw(JigdoDescError, bad_alloc); /** Write a DESC section to a binary stream. Note that there should not be two contiguous Unmatched regions - this is not checked. Similarly, the length of the ImageInfo part must match the accumulated lengths of the other parts. */ bostream& put(bostream& file, MD5Sum* md = 0) const; /** List contents of a JigdoDescVec to a stream in human-readable format. */ void list(ostream& s) throw(); private: // Disallow copying (too inefficient). Use swap() instead. inline JigdoDescVec& operator=(const JigdoDescVec&); }; inline void swap(JigdoDescVec& x, JigdoDescVec& y) { x.swap(y); } //====================================================================== JigdoDesc::ImageInfo::ImageInfo(uint64 s, const MD5& m, size_t b) : sizeVal(s), md5Val(m), blockLengthVal(b) { } JigdoDesc::ImageInfo::ImageInfo(uint64 s, const MD5Sum& m, size_t b) : sizeVal(s), md5Val(m), blockLengthVal(b) { } JigdoDesc::MatchedFile::MatchedFile(uint64 o, uint64 s, const RsyncSum64& r, const MD5& m) : offsetVal(o), sizeVal(s), rsyncVal(r), md5Val(m) { } JigdoDesc::MatchedFile::MatchedFile(uint64 o, uint64 s, const RsyncSum64& r, const MD5Sum& m) : offsetVal(o), sizeVal(s), rsyncVal(r), md5Val(m) { } //________________________________________ bool JigdoDesc::ImageInfo::operator==(const JigdoDesc& x) const { const ImageInfo* i = dynamic_cast(&x); if (i == 0) return false; else return size() == i->size() && md5() == i->md5(); } bool JigdoDesc::UnmatchedData::operator==(const JigdoDesc& x) const { const UnmatchedData* u = dynamic_cast(&x); if (u == 0) return false; else return size() == u->size(); } bool JigdoDesc::MatchedFile::operator==(const JigdoDesc& x) const { const MatchedFile* m = dynamic_cast(&x); if (m == 0) return false; else return offset() == m->offset() && size() == m->size() && md5() == m->md5(); } bool JigdoDesc::WrittenFile::operator==(const JigdoDesc& x) const { // NB MatchedFile and WrittenFile considered equal! const MatchedFile* m = dynamic_cast(&x); if (m == 0) return false; else return offset() == m->offset() && size() == m->size() && md5() == m->md5(); } //________________________________________ inline bistream& operator>>(bistream& s, JigdoDescVec& v) throw(JigdoDescError, bad_alloc) { return v.get(s); } inline bostream& operator<<(bostream& s, JigdoDescVec& v) { return v.put(s); } JigdoDescVec::~JigdoDescVec() { for (iterator i = begin(), e = end(); i != e; ++i) delete *i; } //________________________________________ template Iterator JigdoDesc::ImageInfo::serialize(Iterator i) const { i = serialize1(IMAGE_INFO, i); i = serialize6(size(), i); i = ::serialize(md5(), i); i = serialize4(blockLength(), i); return i; } size_t JigdoDesc::ImageInfo::serialSizeOf() const { return 1 + 6 + 16 + 4; } template Iterator JigdoDesc::UnmatchedData::serialize(Iterator i) const { i = serialize1(UNMATCHED_DATA, i); i = serialize6(size(), i); return i; } size_t JigdoDesc::UnmatchedData::serialSizeOf() const { return 1 + 6; } template Iterator JigdoDesc::MatchedFile::serialize(Iterator i) const { i = serialize1(MATCHED_FILE, i); i = serialize6(size(), i); i = ::serialize(rsync(), i); i = ::serialize(md5(), i); return i; } size_t JigdoDesc::MatchedFile::serialSizeOf() const { return 1 + 6 + 8 + 16;} template Iterator JigdoDesc::WrittenFile::serialize(Iterator i) const { i = serialize1(WRITTEN_FILE, i); i = serialize6(size(), i); i = ::serialize(rsync(), i); i = ::serialize(md5(), i); return i; } size_t JigdoDesc::WrittenFile::serialSizeOf() const { return 1 + 6 + 8 + 16;} #endif jigdo-0.7.3/src/mkjigdo.cc0000644000175000017500000002213210120166075015147 0ustar richardrichard/* $Id: mkjigdo.cc,v 1.2 2004/09/09 23:50:21 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Create location list (.jigdo) and image template (.template) This is the part of the mktemplate code concerned with creating the .jigdo file. See mktemplate.cc for the .template code and the main run() method. */ #include #include #include #include #include #include #include #include #include //______________________________________________________________________ namespace { /* On entry, i points to a line. Move it upwards >= 0 lines, skipping any comment lines immediately before the line, but not any empty lines. */ void skipPrevComments(ConfigFile::iterator& i) { string::const_iterator x; do { --i; x = i->begin(); ConfigFile::advanceWhitespace(x, i->end()); } while (x != i->end() && *x == '#'); ++i; } } /** Index over JigdoParts by md5sum string */ struct MkTemplate::PartIndex { bool operator()(const PartLine* a, const PartLine* b) const { return compat_compare(a->text, a->split, string::npos, b->text, b->split, string::npos) < 0; } }; //______________________________ /* Set up some sections/entries in jigdo file. jigdo->configFile() is either empty or contains a jigdo file passed to the program with --merge. Is called before the MkTemplate operation does its main work. */ void MkTemplate::prepareJigdo() { ConfigFile& j = jigdo->configFile(); typedef ConfigFile::iterator iterator; /* Remove existing [Parts] section(s) from file and store the entries in jigdoParts. During template generation, new entries are added to the set. At the end, the parts are output again. The reason why we don't simply append to an existing [Parts] section is that in the final jigdo file, the parts should always come after all other sections, so that if the download tool features progressive display, the list of images can be displayed soon. */ matchedParts.clear(); jigdoParts.clear(); string sect = "Parts"; iterator curr = j.firstSection(sect); while (curr != j.end()) { iterator prev = curr; ++curr; j.erase(prev); // Remove [Parts] line // Walk through lines of this [Parts] section while (curr != j.end() && !curr.isSection()) { string::const_iterator s = curr->begin(); ConfigFile::advanceWhitespace(s, curr->end()); prev = curr; ++curr; if (s == prev->end()) { j.erase(prev); // Remove empty lines } else if (*s != '#') { // Leave alone comment lines // Remove entry lines, enter them into jigdoParts size_t begin, end, value; // value is offset of first char after '=' PartLine x; if (prev.setLabelOffsets(begin, end, value)) { x.text.assign(*prev, value, string::npos); x.split = x.text.length(); x.text.append(*prev, begin, end - begin); } else { x.text.swap(*prev); x.split = x.text.length(); } jigdoParts.insert(x); j.erase(prev); } } // Advance to next [Parts] section, if any if (curr == j.end()) break; if (curr.isSection(sect)) continue; curr.nextSection(sect); } j.rescan(); //____________________ // Create [Jigdo] section, unless already present if (j.firstSection("Jigdo") == j.end()) { // If jigdo file empty, insert small header comment if (j.empty()) { j.push_back("# JigsawDownload"); j.push_back("# See <"); j.back() += URL; j.back() += "> for details about jigdo"; j.push_back(); } iterator i = j.firstSection(); skipPrevComments(i); j.insert(i, "[Jigdo]"); j.insert(i, "Version="); iterator x = i; --x; append(*x, FILEFORMAT_MAJOR); *x += '.'; append(*x, FILEFORMAT_MINOR); j.insert(i, "Generator=jigdo-file/" JIGDO_VERSION); j.insert(i); } j.rescan(); } //______________________________________________________________________ /* After MkTemplate has read the image, add [Parts] and [Image] sections to the jigdo. */ void MkTemplate::finalizeJigdo(const string& imageLeafName, const string& templLeafName, const MD5Sum& templMd5Sum) { ConfigFile& j = jigdo->configFile(); typedef ConfigFile::iterator iterator; // Add new [Image] section or add Template-MD5Sum value to existing section if (addImageSection) { Base64String md5Sum; md5Sum.write(templMd5Sum.digest(), 16).flush(); // Search for first empty "Template-MD5Sum=" line in file size_t off = 0; string sect = "Image"; string label = "Template-MD5Sum"; for (ConfigFile::Find f(&j, sect, label, &off); !f.finished(); off = f.next()) { // Is the value empty, i.e. end of line after the '='? string::iterator x = f.label()->begin() + off; if (ConfigFile::advanceWhitespace(x, f.label()->end())) { /* Append md5sum to existing Template-MD5Sum line - more accurately, insert md5sum before any # comment. */ f.label()->insert(off, md5Sum.result()); break; } } if (off == 0) { // Append a new [Image] section if (!j.back().empty()) j.push_back(); j.push_back("[Image]"); //j.rescan(); j.push_back("Filename="); j.back() += imageLeafName; j.push_back("Template="); j.back() += templLeafName; j.push_back("Template-MD5Sum="); j.back() += md5Sum.result(); j.rescan(); } } //____________________ /* Prepare to add server lines to last [Servers] section in file. If no such section present yet, create one. */ ConfigFile::iterator servers = j.end(); if (addServersSection) { string sect = "Servers"; iterator serverSection = j.firstSection(sect); while (serverSection != j.end()) { servers = serverSection; serverSection.nextSection(sect); } if (servers != j.end()) { servers.nextSection(); servers.prevLabel(); ++servers; } else { Paranoid(!j.empty()); if (!j.back().empty()) j.push_back(); j.push_back("[Servers]"); j.rescan(); } } //____________________ /* Add new lines to [Parts] section, but only if the part's md5sum isn't listed in the section yet. */ { // Build index over JigdoParts by md5sum string in partIndex set partIndex; for (set::iterator i = jigdoParts.begin(), e = jigdoParts.end(); i != e; ++i) partIndex.insert(&*i); // For quick check: Has LocationPath already been added to [Servers]? set locPaths; // Auto-generated label name string locName = "A"; string sect = "Servers"; // Now add FileParts from matchedParts to jigdoParts as PartLines Base64String m; for (vector::iterator i = matchedParts.begin(), e = matchedParts.end(); i != e; ++i) { m.write((*i)->getMD5Sum(cache)->digest(), 16).flush(); PartLine x; x.text.swap(m.result()); x.split = 0; Paranoid(m.result().empty()); // Skip if md5sum already in partIndex if (partIndex.find(&x) != partIndex.end()) continue; // Otherwise, add a new entry LocationPathSet::iterator location = (*i)->getLocation(); string s = location->getLabel(); if (s.empty()) { // Need to create new label name while (true) { ConfigFile::Find f(&j, sect, locName); if (f.finished()) break; size_t n = 0; while (++locName[n] == 'Z' + 1) { // "Increment locName" locName[n] = 'A'; if (++n == locName.size()) { locName.append(1, 'A'); break; } } } const_cast(*location).setLabel(locName); s = locName; } s += ':'; s += (*i)->leafName(); s = ConfigFile::quote(s); x.split = s.length(); s += x.text; s.swap(x.text); pair::iterator,bool> partIns = jigdoParts.insert(x); partIndex.insert(&*partIns.first); // Maybe also add a line to the [Servers] section if (!addServersSection) continue; pair::iterator,bool> locIns = locPaths.insert(&*location); if (!locIns.second) continue; // Already been seen - ignore s = location->getLabel(); s += '='; s += location->getUri(); if (j.find(sect, s) == j.end()) j.insert(servers, s); } } matchedParts.clear(); // Add [Parts] section if (!j.back().empty()) j.push_back(); j.push_back("[Parts]"); j.rescan(); for (set::iterator i = jigdoParts.begin(), e = jigdoParts.end(); i != e; ++i) { j.push_back(); j.back().append(i->text, i->split, string::npos); j.back() += '='; j.back().append(i->text, 0, i->split); } jigdoParts.clear(); } jigdo-0.7.3/src/mktemplate-funcs.sh0000644000175000017500000000153010262247425017034 0ustar richardrichard# Functions for mktemplate-test*.sh set -e rm -rf mktemplate-testdir mkdir mktemplate-testdir cd mktemplate-testdir # Write $1 bytes of random data to stdout random() { ../util/random "$@" # dd if=/dev/urandom bs="$bs" count=1 2>/dev/null } if test "$1" = "all"; then shift 1 mtargs="--report=noprogress --debug=make-template" else mtargs="--report=quiet --debug=~general" fi mt() { #echo ../jigdo-file make-template -0 $mtargs --image=image "$@" if ../jigdo-file make-template -0 $mtargs --image=image "$@"; then ../jigdo-file list-template --debug=~general \ --template=image.template >image.tlist return 0 else return 1 fi } # Provide on stdin the expected image.tlist tlist() { if diff -u image.tlist -; then true; else echo "FAILED: image.tlist does not have expected contents!" return 1 fi } jigdo-0.7.3/src/mktemplate-test1.sh0000644000175000017500000000064110262453075016760 0ustar richardrichard. $srcdir/mktemplate-funcs.sh # Fixed sometime after 0.7.0 release random 256k >in1 cp in1 in2 random 1k >>in1 random 1k >>in2 random 1k >image cat in1 >>image random 1k >>image mt in* tlist <in1 random 924 >image cat image >>in1 random 1k >>image mt in* tlist <in1 random 100 >in2 cat in1 >>in2 random 100 >>in2 random 1k >image cat in2 >>image random 1k >>image mt --greedy-matching in* tlist < #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ void MkTemplate::ProgressReporter::error(const string& message) { cerr << message << endl; } void MkTemplate::ProgressReporter::scanningImage(uint64) { } void MkTemplate::ProgressReporter::matchFound(const FilePart*, uint64) { } void MkTemplate::ProgressReporter::finished(uint64) { } MkTemplate::ProgressReporter MkTemplate::noReport; //______________________________________________________________________ MkTemplate::MkTemplate(JigdoCache* jcache, bistream* imageStream, JigdoConfig* jigdoInfo, bostream* templateStream, ProgressReporter& pr, int zipQuality, size_t readAmnt, bool addImage, bool addServers, bool useBzip2) : fileSizeTotal(0U), fileCount(0U), block(), readAmount(readAmnt), off(), unmatchedStart(), greedyMatching(true), cache(jcache), image(imageStream), templ(templateStream), zip(0), zipQual(zipQuality), reporter(pr), matches(new PartialMatchQueue()), sectorLength(), jigdo(jigdoInfo), addImageSection(addImage), addServersSection(addServers), useBzLib(useBzip2), matchExec() { } //______________________________________________________________________ /* Because make-template should be debuggable even in non-debug builds, always compile in debug messages. */ #undef debug Logger MkTemplate::debug("make-template"); //______________________________________________________________________ namespace { // Find the position of the highest set bit (e.g. for 0x20, result is 5) inline int bitWidth(uint32 x) { int r = 0; Assert(x <= 0xffffffff); // Can't cope with >32 bits if (x & 0xffff0000) { r += 16; x >>= 16; } if (x & 0xff00) { r += 8; x >>= 8; } if (x & 0xf0) { r += 4; x >>= 4; } if (x & 0xc) { r += 2; x >>= 2; } if (x & 2) { r += 1; x >>=1; } if (x & 1) r += 1; return r; } //________________________________________ /* Avoid integer divisions: Modulo addition/subtraction, with certain assertions */ // Returns (a + b) % m, if (a < m && b <= m) inline size_t modAdd(size_t a, size_t b, size_t m) { size_t r = a + b; if (r >= m) r -= m; Paranoid(r == (a + b) % m); return r; } // Returns (a - b) mod m, if (a < m && b <= m) inline size_t modSub(size_t a, size_t b, size_t m) { size_t r = a + m - b; if (r >= m) r -= m; Paranoid(r == (m + a - b) % m); return r; } // Returns (a + b) % m, if (a < m && -m <= b && b <= m) inline size_t modAdd(size_t a, int b, size_t m) { size_t r = a; if (b < 0) r += m; r += b; if (r >= m) r -= m; Paranoid(r == (a + static_cast(b + m)) % m); return r; } //____________________ /* Write part of a circular buffer to a Zobstream object, starting with offset "start" (incl) and ending with offset "end" (excl). Offsets can be equal to bufferLength. If both offsets are equal, the whole buffer content is written. */ inline void writeBuf(const byte* const buf, size_t begin, size_t end, const size_t bufferLength, Zobstream* zip) { Paranoid(begin <= bufferLength && end <= bufferLength); if (begin < end) { zip->write(buf + begin, end - begin); } else { zip->write(buf + begin, bufferLength - begin); zip->write(buf, end); } } //____________________ // Write lower 48 bits of x to s in little-endian order void write48(bostream& s, uint64 x) { # if 0 s << static_cast(x & 0xff) << static_cast((x >> 8) & 0xff) << static_cast((x >> 16) & 0xff) << static_cast((x >> 24) & 0xff) << static_cast((x >> 32) & 0xff) << static_cast((x >> 40) & 0xff); # else s.put(static_cast( x & 0xff)); s.put(static_cast((x >> 8) & 0xff)); s.put(static_cast((x >> 16) & 0xff)); s.put(static_cast((x >> 24) & 0xff)); s.put(static_cast((x >> 32) & 0xff)); s.put(static_cast((x >> 40) & 0xff)); # endif } } // namespace //______________________________________________________________________ /** Build up a template DESC section by appending items to a JigdoDescVec. Calls to descUnmatchedData() are allowed to accumulate, so that >1 consecutive unmatched data areas are merged into one in the DESC section. */ class MkTemplate::Desc { public: Desc() : files(), offset(0) { } // Insert in DESC section: information about whole image inline void imageInfo(uint64 len, const MD5Sum& md5, size_t blockLength) { files.reserve((files.size() + 16) % 16); files.push_back(new JigdoDesc::ImageInfo(len, md5, blockLength)); } // Insert in DESC section: info about some data that was not matched inline void unmatchedData(uint64 len) { JigdoDesc::UnmatchedData* u; if (files.size() > 0 && (u = dynamic_cast(files.back())) !=0) { // Add more unmatched data to already existing UnmatchedData object u->resize(u->size() + len); } else { // Create new UnmatchedData object. files.reserve((files.size() + 16) % 16); files.push_back(new JigdoDesc::UnmatchedData(offset, len)); } offset += len; } // Insert in DESC section: information about a file that matched inline void matchedFile(uint64 len, const RsyncSum64& r, const MD5Sum& md5) { files.reserve((files.size() + 16) % 16); files.push_back(new JigdoDesc::MatchedFile(offset, len, r, md5)); offset += len; } inline bostream& put(bostream& s, MD5Sum* md) { files.put(s, md); return s; } private: JigdoDescVec files; uint64 offset; }; //______________________________________________________________________ /* The following are helpers used by run(). It is usually not adequate to make such large functions inline, but there is only one call to them, anyway. */ inline bool MkTemplate::scanFiles(size_t blockLength, uint32 blockMask, size_t md5BlockLength) { bool result = SUCCESS; cache->setParams(blockLength, md5BlockLength); FileVec::iterator hashPos; for (JigdoCache::iterator file = cache->begin(); file != cache->end(); ++file) { const RsyncSum64* sum = file->getRsyncSum(cache); if (sum == 0) continue; // Error - skip // Add file to hash list hashPos = block.begin() + (sum->getHi() & blockMask); hashPos->push_back(&*file); } return result; } //________________________________________ void MkTemplate::checkRsyncSumMatch2(const size_t blockLen, const size_t back, const size_t md5BlockLength, uint64& nextEvent, FilePart* file) { /* Don't schedule match if its startOff (== off - blockLen) is impossible. This is e.g. the case when file A is 1024 bytes long (i.e. is immediately matched once seen), and the first 1024 bytes of file B are equal to file A, and file B is in the image. [I think A gets matched, then the following line prevents that a partial match for B is also recorded.] Also prevents matches whose startOffset is <0, which could otherwise happen because rsum covers a blockLen-sized area of 0x7f bytes at the beginning. */ if (off < unmatchedStart + blockLen) return; PartialMatch* x; // Ptr to new entry in "matches" if (matches->full()) { x = matches->findDropCandidate(§orLength, off - blockLen); if (x == 0 || x->file() == file) { /* If no more space left in queue and none of the entries in it is appropriate for discarding, just discard this possible file match! It's the only option if there are many, many overlapping matches, otherwise the program would get extremely slow. */ debug(" %1: DROPPED possible %2 match at offset %3 (queue full)", off, file->leafName(), off - blockLen); return; } // Overwrite existing entry in the queue debug(" %1: DROPPED possible %2 match at offset %3 (queue full, match " "below replaces it)", off, x->file()->leafName(), x->startOffset()); } else { // !matches->full() // Add new entry to the queue x = matches->addFront(); } /* Rolling rsum matched - schedule an MD5Sum match. NB: In extreme cases, nextEvent may be equal to off */ x->setStartOffset(off - blockLen); size_t eventLen = (file->size() < md5BlockLength ? file->size() : md5BlockLength); x->setNextEvent(matches, x->startOffset() + eventLen); debug(" %1: Head of %2 match at offset %3, my next event %4", off, file->leafName(), x->startOffset(), x->nextEvent()); if (x->nextEvent() < nextEvent) nextEvent = x->nextEvent(); x->setBlockOffset(back); x->setBlockNumber(0); x->setFile(file); } /* Look for matches of sum (i.e. scanImage()'s rsum). If found, insert appropriate entry in "matches". */ void MkTemplate::checkRsyncSumMatch(const RsyncSum64& sum, const uint32& bitMask, const size_t blockLen, const size_t back, const size_t md5BlockLength, uint64& nextEvent) { typedef const vector FVec; FVec& hashEntry = block[sum.getHi() & bitMask]; if (hashEntry.empty()) return; FVec::const_iterator i = hashEntry.begin(), e = hashEntry.end(); do { FilePart* file = *i; const RsyncSum64* fileSum = file->getRsyncSum(cache); if (fileSum != 0 && *fileSum == sum) // Insert new partial file match in "matches" queue checkRsyncSumMatch2(blockLen, back, md5BlockLength, nextEvent, file); ++i; } while (i != e); return; } //________________________________________ // Read the 'count' first bytes from file x and write them to zip bool MkTemplate::rereadUnmatched(FilePart* file, uint64 count) { // Lower peak memory usage: Deallocate cache's buffer cache->deallocBuffer(); ArrayAutoPtr tmpBuf(new byte[readAmount]); string inputName = file->getPath(); inputName += file->leafName(); auto_ptr inputFile(new bifstream(inputName.c_str(),ios::binary)); while (inputFile->good() && count > 0) { // read data readBytes(*inputFile, tmpBuf.get(), (readAmount < count ? readAmount : count)); size_t n = inputFile->gcount(); zip->write(tmpBuf.get(), n); // will catch Zerror "upstream" Paranoid(n <= count); count -= n; } if (count == 0) return SUCCESS; // error string err = subst(_("Error reading from `%1' (%2)"), inputName, strerror(errno)); reporter.error(err); return FAILURE; } //________________________________________ // Print info about a part of the input image void MkTemplate::printRangeInfo(uint64 start, uint64 end, const char* msg, const PartialMatch* x) { static const string empty; debug("[%1,%2) %3 %4", start, end, msg, (x != 0 ? x->file()->leafName() : empty) ); } // oldAreaEnd != start, something's seriously wrong void MkTemplate::debugRangeFailed() { static bool printed = false; cerr << "Assertion failed: oldAreaEnd == start" << endl; if (!printed) { cerr << "You have found a bug in jigdo-file. The generated .template file is\n" "very likely broken! To help me find the bug, please rerun the\n" "command as follows:\n" " [previous-command] --report=noprogress --debug=make-template >log 2>&1\n" "and send the _compressed_ `log' file to ." << endl; printed = true; } } //________________________________________ /* The block didn't match, so the whole file x doesn't match - re-read from file any data that is no longer buffered (and not covered by another match), and write it to the Zobstream. */ bool MkTemplate::checkMD5Match_mismatch(const size_t stillBuffered, PartialMatch* x, Desc& desc) { const PartialMatch* oldestMatch = matches->findLowestStartOffset(); uint64 rereadEnd = off - stillBuffered; uint64 xStartOffset = x->startOffset(); if (x != oldestMatch || xStartOffset >= rereadEnd) { // Everything still buffered, or there is another pending match matches->eraseFront(); // return x to free pool return SUCCESS; } /* Reread the right amount of data from the file for x, covering the image area from x->startOffset() to the first byte which is "claimed" by something else - either another match or the start of the still buffered data. */ FilePart* xfile = x->file(); Assert(matches->front() == x); matches->eraseFront(); // return x to free pool if (!matches->empty()) { // set rereadEnd to new lowest startOffset in matches const PartialMatch* newOldestMatch = matches->findLowestStartOffset(); if (rereadEnd > newOldestMatch->startOffset()) rereadEnd = newOldestMatch->startOffset(); } debugRangeInfo(xStartOffset, rereadEnd, "UNMATCHED after some blocks, re-reading from", x); desc.unmatchedData(rereadEnd - xStartOffset); unmatchedStart = rereadEnd; uint64 bytesToWrite = rereadEnd - xStartOffset; return rereadUnmatched(xfile, bytesToWrite); } //________________________________________ /* The file x was found in the image, and --match-exec is set. Set up env vars, run command. Why does this use system() instead of the "more secure" exec()? Because then things are actually easier to get right IMHO! My scenario is that someone has set up an env var with the destination path for the fallback, say $DEST, which might contain spaces. In that case, using exec() with some kind of "%label" substitution is awkward and error-prone - let's just have one single substitution scheme, that used by the shell! */ bool MkTemplate::matchExecCommands(PartialMatch* x) { Paranoid(!matchExec.empty()); string matchPath, leaf; const string& leafName = x->file()->leafName(); string::size_type lastSlash = leafName.rfind(DIRSEP); if (lastSlash == string::npos) { leaf = leafName; } else { matchPath.assign(leafName, 0, lastSlash + 1); leaf.assign(leafName, lastSlash + 1, string::npos); } Base64String md5Sum; md5Sum.write(x->file()->getMD5Sum(cache)->digest(), 16).flush(); string file = x->file()->getLocation()->getPath(); file += leafName; // Set environment vars if (compat_setenv("LABEL", x->file()->getLocation()->getLabel().c_str()) || compat_setenv("LABELPATH", x->file()->getLocation()->getPath() .c_str()) || compat_setenv("MATCHPATH", matchPath.c_str()) || compat_setenv("LEAF", leaf.c_str()) || compat_setenv("MD5SUM", md5Sum.result().c_str()) || compat_setenv("FILE", file.c_str())) { reporter.error(_("Could not set up environment for --match-exec " "command")); return FAILURE; } // Execute command int status = system(matchExec.c_str()); if (status == 0) return SUCCESS; reporter.error(_("Command supplied with --match-exec failed")); return FAILURE; } //________________________________________ /* Calculate MD5 for the previous md5ChunkLength (or less if at end of match) bytes. If the calculated checksum matches and it is the last MD5 block in the file, record a file match. If the i-th MD5Sum does not match, write the i*md5ChunkLength bytes directly to templ. @param stillBuffered bytes of image data "before current position" that are still in the buffer; they are at buf[data] to buf[(data+stillBuffered-1)%bufferLength] */ bool MkTemplate::checkMD5Match(byte* const buf, const size_t bufferLength, const size_t data, const size_t md5BlockLength, uint64& nextEvent, const size_t stillBuffered, Desc& desc) { PartialMatch* x = matches->front(); Paranoid(x != 0 && matches->nextEvent() == off); /* Calculate MD5Sum from buf[x->blockOff] to buf[data-1], deal with wraparound. NB 0 <= x->blockOff < bufferLength, but 1 <= data < bufferLength+1 */ static MD5Sum md; md.reset(); if (x->blockOffset() < data) { md.update(buf + x->blockOffset(), data - x->blockOffset()); } else { md.update(buf + x->blockOffset(), bufferLength - x->blockOffset()); md.update(buf, data); } md.finishForReuse(); //____________________ const MD5* xfileSum = x->file()->getSums(cache, x->blockNumber()); if (debug) debug("checkMD5Match?: image %1, file %2 block #%3 %4", md.toString(), x->file()->leafName(), x->blockNumber(), (xfileSum ? xfileSum->toString() : "[error]") ); if (xfileSum == 0 || md != *xfileSum) { /* The block didn't match, so the whole file doesn't match - re-read from file any data that is no longer buffered (and not covered by another match), and write it to the Zobstream. */ return checkMD5Match_mismatch(stillBuffered, x, desc); } //____________________ // Another block of file matched - was it the last one? if (off < x->startOffset() + x->file()->size()) { // Still some more to go - update x and its position in queue x->setBlockOffset(data); x->setBlockNumber(x->blockNumber() + 1); x->setNextEvent(matches, min(x->nextEvent() + md5BlockLength, x->startOffset() + x->file()->size())); nextEvent = min(nextEvent, x->nextEvent()); debug("checkMD5Match: match and more to go, next at off %1", x->nextEvent()); return SUCCESS; } //____________________ Assert(off == x->startOffset() + x->file()->size()); // Heureka! *MATCH* // x = address of PartialMatch obj of file that matched const PartialMatch* oldestMatch = matches->findLowestStartOffset(); if (!greedyMatching && x != oldestMatch) { // A larger match is possible, so skip this match debug("IGNORING match due to --no-greedy-matching: [%1,%2) %3", x->startOffset(), off, x->file()->leafName()); matches->eraseFront(); // return x to free pool return SUCCESS; } reporter.matchFound(x->file(), x->startOffset()); matchedParts.push_back(x->file()); /* Re-read and write out data before the start of the match, i.e. of any half-finished bigger match (which we abandon now that we've found a smaller match inside it). */ if (x != oldestMatch && oldestMatch->startOffset() < off - stillBuffered) { unmatchedStart = min(off - stillBuffered, x->startOffset()); debugRangeInfo(oldestMatch->startOffset(), unmatchedStart, "UNMATCHED, re-reading partial match from", oldestMatch); size_t toReread = unmatchedStart - oldestMatch->startOffset(); desc.unmatchedData(toReread); if (rereadUnmatched(oldestMatch->file(), toReread)) return FAILURE; } /* Write out data that is still buffered, and is before the start of the match. */ if (unmatchedStart < x->startOffset()) { debugRangeInfo(unmatchedStart, x->startOffset(), "UNMATCHED, buffer flush before match"); size_t toWrite = x->startOffset() - unmatchedStart; Paranoid(off - unmatchedStart <= bufferLength); size_t writeStart = modSub(data, off - unmatchedStart, bufferLength); writeBuf(buf, writeStart, modAdd(writeStart, toWrite, bufferLength), bufferLength, zip); desc.unmatchedData(toWrite); } // Assert(x->file->mdValid); desc.matchedFile(x->file()->size(), *(x->file()->getRsyncSum(cache)), *(x->file()->getMD5Sum(cache))); unmatchedStart = off; debugRangeInfo(x->startOffset(), off, "MATCH:", x); // With --match-exec, execute user-supplied command(s) if (!matchExec.empty() && matchExecCommands(x) == FAILURE) return FAILURE; /* Remove all matches with startOff < off (this includes x). This is the greedy approach and is not ideal: If a small file A happens to match one small fraction of a large file B and B is contained in the image, then only a match of A will be found. */ Paranoid(x->startOffset() < off); matches->eraseStartOffsetLess(off); return SUCCESS; } //________________________________________ /* This is called in case the end of the image has been reached, but unmatchedStart < off, i.e. there was a partial match at the end of the image. Just discards that match and writes the data to the template, either by re-reading from the partially matched file, or from the buffer. Compare to similar code in checkMD5Match. Since we are at the end of the image, the full last bufferLength bytes of the image are in the buffer. */ bool MkTemplate::unmatchedAtEnd(byte* const buf, const size_t bufferLength, const size_t data, Desc& desc) { Paranoid(unmatchedStart < off); // cf. where this is called // Re-read and write out data that is no longer buffered. const PartialMatch* y = matches->findStartOffset(unmatchedStart); if (y != 0 && y->startOffset() < off - bufferLength) { unmatchedStart = off - bufferLength; debugRangeInfo(y->startOffset(), unmatchedStart, "UNMATCHED at end, re-reading partial match from", y); size_t toReread = unmatchedStart - y->startOffset(); desc.unmatchedData(toReread); if (rereadUnmatched(y->file(), toReread)) return FAILURE; } // Write out data that is still buffered if (unmatchedStart < off) { debugRangeInfo(unmatchedStart, off, "UNMATCHED at end"); size_t toWrite = off - unmatchedStart; Assert(toWrite <= bufferLength); size_t writeStart = modSub(data, toWrite, bufferLength); writeBuf(buf, writeStart, data, bufferLength, zip); desc.unmatchedData(toWrite); unmatchedStart = off; } return SUCCESS; } //________________________________________ /* The "matches" queue is full. Typically, when this happens there is a big zero-filled area in the image and one or more input files start with zeroes. At this point, we must start dropping some prospective file matches. This is done based on the heuristics that actual file matches occur at a "sector boundary" in the image. See INITIAL_SECTOR_LENGTH in mktemplate.hh for more. This method is a variant of the main loop which is used when matches.full(), and which advances the rsum window by one sector at a time. */ void MkTemplate::scanImage_mainLoop_fastForward(uint64 nextEvent, RsyncSum64* rsum, byte* buf, size_t* data, size_t* n, size_t* rsumBack, size_t bufferLength, size_t blockLength, uint32 blockMask, size_t md5BlockLength) { # if 0 // Simple version debug("DROPPING, fast forward (queue full)"); Assert(off >= blockLength); unsigned sectorMask = sectorLength - 1; while (off < nextEvent) { rsum->removeFront(buf[*rsumBack], blockLength); rsum->addBack(buf[*data]); ++*data; ++off; --*n; *rsumBack = modAdd(*rsumBack, 1, bufferLength); if (((off - blockLength) & sectorMask) == 0) { checkRsyncSumMatch(*rsum, blockMask, blockLength, *rsumBack, md5BlockLength, nextEvent); sectorMask = sectorLength - 1; Paranoid(matches->empty() || matches->front()->startOffset() >= unmatchedStart); } } # else debug("DROPPING, fast forward (queue full)"); Assert(off >= blockLength); unsigned sectorMask = sectorLength - 1; uint64 notSectorMask = ~implicit_cast(sectorMask); while (off < nextEvent) { /* Calculate next value of off where a match would end up having an even (i.e. sectorSize-aligned) start offset. |----sectorLength----|----sectorLength----| |========+========+===========+===+===========+====+=====>EOF File offset: 0 | off | |--blockLength--| | |--blockLength--| | nextAlignedOff */ uint64 nextAlignedOff = off - blockLength; nextAlignedOff = (nextAlignedOff + sectorLength) & notSectorMask; nextAlignedOff += blockLength; Assert(nextAlignedOff > off); unsigned len = nextAlignedOff - off; if (len > nextEvent - off) len = nextEvent - off; // Advance rsum by len bytes in one go # if DEBUG RsyncSum64 rsum2 = *rsum; size_t rsumBack2 = *rsumBack; uint64 off2 = off; size_t data2 = *data; # endif if (*rsumBack + len <= bufferLength) { rsum->removeFront(buf + *rsumBack, len, blockLength); } else { rsum->removeFront(buf + *rsumBack, bufferLength - *rsumBack, blockLength); rsum->removeFront(buf, len + *rsumBack - bufferLength, blockLength + *rsumBack - bufferLength); } Paranoid(*data + len <= bufferLength); rsum->addBack(buf + *data, len); *data += len; off += len; *n -= len; *rsumBack = modAdd(*rsumBack, implicit_cast(len), bufferLength); Paranoid(off == nextEvent || off == nextAlignedOff); # if DEBUG for (unsigned i = 0; i < len; ++i) { rsum2.removeFront(buf[rsumBack2], blockLength); rsum2.addBack(buf[data2]); ++data2; ++off2; rsumBack2 = modAdd(rsumBack2, 1, bufferLength); } Assert(rsumBack2 == *rsumBack); Assert(off2 == off); Assert(data2 == *data); Assert(rsum2 == *rsum); # endif //debug("DROPPING, fast forward (queue full) to %1", off); if (off == nextAlignedOff) { Paranoid(((off - blockLength) & sectorMask) == 0); checkRsyncSumMatch(*rsum, blockMask, blockLength, *rsumBack, md5BlockLength, nextEvent); Paranoid(matches->empty() || matches->front()->startOffset() >= unmatchedStart); sectorMask = sectorLength - 1; notSectorMask = ~implicit_cast(sectorMask); } } // endwhile (off < nextEvent) # endif } //________________________________________ /* Scan image. Central function for template generation. Treat buf as a circular buffer. Read new data into at most half the buffer. Calculate a rolling checksum covering blockLength bytes. When it matches an entry in block, start calculating MD5Sums of blocks of length md5BlockLength. Since both image and templ can be non-seekable, we run into a problem in the following case: After the initial RsyncSum match, a few of the md5BlockLength-sized chunks of one input file were matched, but not all, so in the end, there is no match. Consequently, we would now need to re-read that part of the image and pump it through zlib to templ - but we can't if the image is stdin! Solution: Since we know that the MD5Sum of a block matched part of an input file, we can re-read from there. */ inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength, size_t blockLength, uint32 blockMask, size_t md5BlockLength, MD5Sum& templMd5Sum) { bool result = SUCCESS; /* Cause input files to be analysed */ if (scanFiles(blockLength, blockMask, md5BlockLength)) result = FAILURE; /* Initialise rolling sums with blockSize bytes 0x7f, and do the same with part of buffer, to avoid special-case code in main loop. (Any value would do - except that 0x00 or 0xff might lead to a larger number of false positives.) */ RsyncSum64 rsum; byte* bufEnd = buf + bufferLength; //for (byte* z = bufEnd - blockLength; z < bufEnd; ++z) *z = 0x7f; // Init entire buf, keep valgrind happy for (byte* z = buf; z < bufEnd; ++z) *z = 0x7f; rsum.addBackNtimes(0x7f, blockLength); // Compression pipe for templ data auto_ptr zipDel; if (useBzLib) zipDel.reset(implicit_cast( new ZobstreamBz(*templ, zipQual, 256U, &templMd5Sum) )); else zipDel.reset(implicit_cast( new ZobstreamGz(*templ, ZIPCHUNK_SIZE, zipQual, 15, 8, 256U, &templMd5Sum) )); zip = zipDel.get(); Desc desc; // Buffer for DESC data, will be appended to templ at end size_t data = 0; // Offset into buf of byte currently being processed off = 0; // Current absolute offset in image, corresponds to "data" uint64 nextReport = 0; // call reporter once off reaches this value /* The area delimited by unmatchedStart (incl) and off (excl) has "not been dealt with", either by writing it to zip, or by a match with the first MD5 block of an input file. Once a partial match of a file has been detected, unmatchedStart "gets stuck" at the start offset of this file within the image. */ unmatchedStart = 0; MD5Sum imageMd5Sum; // MD5 of whole image MD5Sum md; // Re-used for each 2nd-level check of any rsum match matches->erase(); sectorLength = INITIAL_SECTOR_LENGTH; // Read image size_t rsumBack = bufferLength - blockLength; try { /* Catch Zerrors, which can occur in zip->write(), writeBuf(), checkMD5Match(), zip->close() */ while (image->good()) { debug("---------- main loop. off=%1 data=%2 unmatchedStart=%3", off, data, unmatchedStart); if (off >= nextReport) { // Keep user entertained reporter.scanningImage(off); nextReport += REPORT_INTERVAL; } // zip->write() out any old data that we'll destroy with read() size_t thisReadAmount = (readAmount < bufferLength - data ? readAmount : bufferLength - data); uint64 newUnmatchedStart = 0; if (off > blockLength) newUnmatchedStart = off - blockLength; if (!matches->empty()) { newUnmatchedStart = min(newUnmatchedStart, matches->lowestStartOffset()->startOffset()); } if (unmatchedStart < newUnmatchedStart) { size_t toWrite = newUnmatchedStart - unmatchedStart; Paranoid(off - unmatchedStart <= bufferLength); //debug("off=%1 unmatchedStart=%2 buflen=%3", // off, unmatchedStart, bufferLength); size_t writeStart = modSub(data, off - unmatchedStart, bufferLength); debugRangeInfo(unmatchedStart, unmatchedStart + toWrite, "UNMATCHED"); writeBuf(buf, writeStart, modAdd(writeStart, toWrite, bufferLength), bufferLength, zip); unmatchedStart = newUnmatchedStart; desc.unmatchedData(toWrite); } // Read new data from image # if DEBUG // just for testing, make it sometimes read less static size_t chaosOff, acc; if (unmatchedStart == 0) { chaosOff = 0; acc = 0; } acc += buf[chaosOff] ^ buf[off % bufferLength] + ~off; if (chaosOff == 0) chaosOff = bufferLength - 1; else --chaosOff; if ((acc & 0x7) == 0) { thisReadAmount = acc % thisReadAmount + 1; debug("thisReadAmount=%1", thisReadAmount); } # endif readBytes(*image, buf + data, thisReadAmount); size_t n = image->gcount(); imageMd5Sum.update(buf + data, n); while (n > 0) { // Still unprocessed bytes left uint64 nextEvent = off + n; // Special event: end of buffer if (!matches->empty()) nextEvent = min(nextEvent, matches->front()->nextEvent()); if (!matches->full()) { sectorLength = INITIAL_SECTOR_LENGTH; /* Unrolled innermost loop - see below for single-iteration version. Also see checkRsyncSumMatch above. */ while (off + 32 < nextEvent && rsumBack < bufferLength - 32) { size_t dataOld = data; do { const vector* hashEntry; # define x ; \ rsum.removeFront(buf[rsumBack], blockLength); \ rsum.addBack(buf[data]); \ ++data; ++rsumBack; \ hashEntry = &block[rsum.getHi() & blockMask]; \ if (hashEntry->size() > 1) break; \ if (hashEntry->size() == 1) { \ FilePart* file = (*hashEntry)[0]; \ const RsyncSum64* fileSum = file->getRsyncSum(cache); \ if (fileSum != 0 && *fileSum == rsum) break; \ } x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x # undef x dataOld = data; // Special case: "Did not break out of loop" } while (false); if (dataOld == data) { off += 32; n -= 32; } else { dataOld = data - dataOld; off += dataOld; n -= dataOld; checkRsyncSumMatch(rsum, blockMask, blockLength, rsumBack, md5BlockLength, nextEvent); } if (matches->full()) break; } } // endif (!matches->full()) if (!matches->full()) { // Innermost loop - single-byte version, matches not full while (off < nextEvent) { // Roll checksum by one byte rsum.removeFront(buf[rsumBack], blockLength); rsum.addBack(buf[data]); ++data; ++off; --n; rsumBack = modAdd(rsumBack, 1, bufferLength); /* Look for matches of rsum. If found, insert appropriate entry in matches list and maybe modify nextEvent. */ checkRsyncSumMatch(rsum, blockMask, blockLength, rsumBack, md5BlockLength, nextEvent); /* We mustn't by accident schedule an event for a part of the image that has already been flushed out of the buffer/matched */ Paranoid(matches->empty() || matches->front()->startOffset() >= unmatchedStart); } } else { // Innermost loop - MATCHES IS FULL scanImage_mainLoop_fastForward(nextEvent, &rsum, buf, &data, &n, &rsumBack, bufferLength, blockLength, blockMask, md5BlockLength); } // endif (matches->full()) if (matches->empty()) debug(" %1: Event, matches empty", off); else debug(" %1: Event, matchesOff=%2", off, matches->nextEvent()); /* Calculate MD5 for the previous md5ChunkLength (or less if at end of match) bytes, if necessary. If the calculated checksum matches and it is the last MD5 block in the file, record a file match. If the i-th MD5Sum does not match, write the i*md5ChunkLength bytes directly to templ. */ while (!matches->empty() && matches->nextEvent() == off) { size_t stillBuffered = bufferLength - n; if (stillBuffered > off) stillBuffered = off; if (checkMD5Match(buf, bufferLength, data, md5BlockLength, nextEvent, stillBuffered, desc)) return FAILURE; // no recovery possible, exit immediately } Assert(matches->empty() || matches->nextEvent() > off); } // endwhile (n > 0), i.e. more unprocessed bytes left in buffer if (data == bufferLength) data = 0; Assert(data < bufferLength); } // endwhile (image->good()), i.e. more data left in input image // End of image data - any remaining partial match is UNMATCHED if (unmatchedStart < off && unmatchedAtEnd(buf, bufferLength, data, desc)) { return FAILURE; } Assert(unmatchedStart == off); zip->close(); } catch (Zerror ze) { string err = subst(_("Error during compression: %1"), ze.message); reporter.error(err); try { zip->close(); } catch (Zerror zze) { } return FAILURE; } imageMd5Sum.finish(); desc.imageInfo(off, imageMd5Sum, cache->getBlockLen()); desc.put(*templ, &templMd5Sum); if (!*templ) { string err = _("Could not write template data"); reporter.error(err); return FAILURE; } reporter.finished(off); return result; } //______________________________________________________________________ // Central function which processes the data and finds matches bool MkTemplate::run(const string& imageLeafName, const string& templLeafName) { bool result = SUCCESS; oldAreaEnd = 0; // Kick out files that are too small for (JigdoCache::iterator f = cache->begin(), e = cache->end(); f != e; ++f) { if (f->size() < cache->getBlockLen()) { f->markAsDeleted(cache); continue; } fileSizeTotal += f->size(); ++fileCount; } // Hash table performance drops when linked lists become long => "+1" int blockBits = bitWidth(fileCount) + 1; uint32 blockMask = (1 << blockBits) - 1; block.resize(blockMask + 1); size_t max_MD5Len_blockLen = cache->getBlockLen() + 64; // +64 for Assert below if (max_MD5Len_blockLen < cache->getMD5BlockLen()) max_MD5Len_blockLen = cache->getMD5BlockLen(); /* Pass 1 imposes no minimum buffer length, only pass 2: Must always be able to read readAmount bytes into one buffer half in one go; must be able to start calculating an MD5Sum at a position that is blockLength bytes back in input; must be able to write out at least previous md5BlockLength bytes in case there is no match. */ size_t bufferLength = 2 * (max_MD5Len_blockLen > readAmount ? max_MD5Len_blockLen : readAmount); // Avoid reading less bytes than readAmount at any time bufferLength = (bufferLength + readAmount - 1) / readAmount * readAmount; Paranoid(bufferLength % readAmount == 0); // for efficiency only // Asserting this makes things easier in pass 2. Yes it is ">" not ">=" Assert(cache->getMD5BlockLen() > cache->getBlockLen()); if (debug) { debug("Nr of files: %1 (%2 bits)", fileCount, blockBits); debug("Total bytes: %1", fileSizeTotal); debug("blockLength: %1", cache->getBlockLen()); debug("md5BlockLen: %1", cache->getMD5BlockLen()); debug("bufLen (kB): %1", bufferLength/1024); debug("zipQual: %1", zipQual); } MD5Sum templMd5Sum; ArrayAutoPtr bufDel(new byte[bufferLength]); byte* buf = bufDel.get(); prepareJigdo(); // Add [Jigdo] { // Write header to template file string s = TEMPLATE_HDR; append(s, FILEFORMAT_MAJOR); s += '.'; append(s, FILEFORMAT_MINOR); s += " jigdo-file/" JIGDO_VERSION; s += "\r\nSee "; s += URL; s += " for details about jigdo.\r\n\r\n"; const byte* t = reinterpret_cast(s.data()); writeBytes(*templ, t, s.size()); templMd5Sum.update(t, s.size()); if (!*templ) result = FAILURE; } // Read input image and output parts that do not match if (scanImage(buf, bufferLength, cache->getBlockLen(), blockMask, cache->getMD5BlockLen(), templMd5Sum)) { result = FAILURE; } cache->deallocBuffer(); templMd5Sum.finish(); // Add [Image], (re-)add [Parts] finalizeJigdo(imageLeafName, templLeafName, templMd5Sum); debug("MkTemplate::run() finished"); return result; } jigdo-0.7.3/src/mktemplate.hh0000644000175000017500000002472610431672643015723 0ustar richardrichard/* $Id: mktemplate.hh,v 1.10 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Create location list (.jigdo) and image template (.template) */ #ifndef MKTEMPLATE_HH #define MKTEMPLATE_HH #ifndef INLINE # ifdef NOINLINE # define INLINE # else # define INLINE inline # endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ /** Create location list (jigdo) and image template (template) from one big file and a list of files. The template file contains a compressed version of the big file, excluding the data of any of the other files that are contained somewhere in the big file. Instead of their data, the image template file just lists their checksums. */ class MkTemplate { public: class ProgressReporter; /** A create operation with no files known to it yet. @param jcache Cache for the files (will not be deleted in dtor) @param imageStream The large image file @param jigdoInfo Where to output .jigdo data to @param templateStream Stream for outputting binary image template @param pr Function object which is called at regular intervals during run() to inform about files scanned, nr of bytes scanned, matches found etc. @param zipQuality 0 (fast) to 9 (smallest output) @param readAmnt Number of bytes that are read at a time with one read() call by the operation before the data is processed. Should not be too large because the OS copes best when small bits of I/O are interleaved with small bits of CPU work. In practice, the default seems to work well. @param addImage Add a [Image] section to the output .jigdo. @param addServers Add a [Servers] section to the output .jigdo. @param useBzip2 false=>gzip, true=>bzip2 */ MkTemplate(JigdoCache* jcache, bistream* imageStream, JigdoConfig* jigdoInfo, bostream* templateStream, ProgressReporter& pr = noReport, int zipQuality = 9, size_t readAmnt = 128U*1024, bool addImage = true, bool addServers = true, bool useBzip2 = false); inline ~MkTemplate(); /** Set command(s) to be executed when a file matches. */ inline void setMatchExec(const string& me); /** Set and get whether to skip smaller matches if a larger match could be possible (with the risk to skip both). True <=> prefer small matches; false <=> skip small, prefer large */ inline void setGreedyMatching(bool x) { greedyMatching = x; } inline bool getGreedyMatching() const { return greedyMatching; } /** First scan through all the individual files, creating checksums, then read image file and find matches. Write .template and .jigdo files. @param imageLeafName Name for the image, which should be a relative path name. This does not need to be similar to the filename that the image has now - it is *only* used when creating the .jigdo file, nowhere else. It is the name that will be used when someone reassembles the image. @param templLeafName Name to write to jigdo file as template URL. Can be a full http/ftp URL, or a relative URL. @return returns FAILURE if any open/read/write/close failed, unless it was that of a file with reportErrors==false. */ bool run(const string& imageLeafName = "image", const string& templLeafName = "template"); /** Default reporter: Only prints error messages to stderr */ static ProgressReporter noReport; private: // Values for codes in the template data's DESC section static const byte IMAGE_INFO = 1, UNMATCHED_DATA = 2, MATCHED_FILE = 3; static const size_t REPORT_INTERVAL = 256U*1024; /* If the queue of matches runs full, we enter a special mode where new matches are only added to the queue if their start offset is a multiple of this assumed sector size. This is a heuristics - matches are more likely to start at "even" offsets. Because the actual sector size of the input image is not known, we'll prefer SECTOR_LENGTH-aligned matches to matches at odd offsets, 2*SECTOR_LENGTH-aligned ones to SECTOR_LENGTH-aligned ones, 4*SECTOR_LENGTH-aligned ones to 2*SECTOR_LENGTH-aligned ones etc. Initial value for sectorLength. */ static const unsigned INITIAL_SECTOR_LENGTH = 512; static const unsigned MAX_SECTOR_LENGTH = 65536; /* debug(...) may be defined as a CPP macro. Luckily, that won't affect this occurance of the word. */ static Logger debug; // Disallow assignment; op is never defined inline MkTemplate& operator=(const MkTemplate&); // Various helper classes and functions for run() class Desc; class PartialMatch; class PartialMatchQueue; friend class PartialMatchQueue; void prepareJigdo(); void finalizeJigdo(const string& imageLeafName, const string& templLeafName, const MD5Sum& templMd5Sum); INLINE bool scanFiles(size_t blockLength, uint32 blockMask, size_t md5BlockLength); INLINE bool scanImage(byte* buf, size_t bufferLength, size_t blockLength, uint32 blockMask, size_t md5BlockLength, MD5Sum&); static INLINE void insertInTodo(PartialMatchQueue& matches, PartialMatch* x); void checkRsyncSumMatch2(const size_t blockLen, const size_t back, const size_t md5BlockLength, uint64& nextEvent, FilePart* file); INLINE void checkRsyncSumMatch(const RsyncSum64& sum, const uint32& bitMask, const size_t blockLen, const size_t back, const size_t md5BlockLength, uint64& nextEvent); INLINE bool checkMD5Match(byte* const buf, const size_t bufferLength, const size_t data, const size_t md5BlockLength, uint64& nextEvent, const size_t stillBuffered, Desc& desc); INLINE bool checkMD5Match_mismatch(const size_t stillBuffered, PartialMatch* x, Desc& desc); INLINE bool unmatchedAtEnd(byte* const buf, const size_t bufferLength, const size_t data, Desc& desc); bool rereadUnmatched(FilePart* file, uint64 count); INLINE void scanImage_mainLoop_fastForward(uint64 nextEvent, RsyncSum64* rsum, byte* buf, size_t* data, size_t* n, size_t* rsumBack, size_t bufferLength, size_t blockLength, uint32 blockMask, size_t md5BlockLength); INLINE bool matchExecCommands(PartialMatch* x); inline void debugRangeInfo(uint64 start, uint64 end, const char* msg, const PartialMatch* x = 0); void printRangeInfo(uint64 start, uint64 end, const char* msg, const PartialMatch* x = 0); void debugRangeFailed(); uint64 fileSizeTotal; // Accumulated lengths of all the files size_t fileCount; // Total number of files added // Look up list of FileParts by hash of RsyncSum typedef vector > FileVec; FileVec block; // Nr of bytes to read in one go, as specified by caller of MkTemplate() size_t readAmount; /* The area delimited by unmatchedStart (incl) and off (excl) has "not been dealt with", either by writing it to zip, or by a match with the first MD5 block of an input file. Once a partial match of a file has been detected, unmatchedStart "gets stuck" at the start offset of this file within the image. */ uint64 off; // Current absolute offset in image uint64 unmatchedStart; bool greedyMatching; JigdoCache* cache; bistream* image; bostream* templ; Zobstream* zip; // Compressing stream for template data output int zipQual; // 0..9, passed to zlib ProgressReporter& reporter; PartialMatchQueue* matches; // queue of partially matched files unsigned sectorLength; //____________________ JigdoConfig* jigdo; struct PartLine; struct PartIndex; set jigdoParts; // lines of [Parts] section vector matchedParts; // New parts to add to [Parts] at end // true => add a [Image/Servers] section to the output .jigdo file bool addImageSection; bool addServersSection; bool useBzLib; string matchExec; //____________________ // For debugging of the template creation uint64 oldAreaEnd; }; #include //______________________________________________________________________ /** Class allowing MkTemplate to convey information back to the creator of a MkTemplate object. The default versions of the methods do nothing at all (except for error(), which prints the error to cerr) - you need to supply an object of a derived class to MkTemplate() to get called back. */ class MkTemplate::ProgressReporter { public: virtual ~ProgressReporter() { } /** General-purpose error reporting */ virtual void error(const string& message); /** Called during second pass, when the image file is scanned. @param offset Offset in image file */ virtual void scanningImage(uint64 offset); // Error while reading the image file - aborting immediately // virtual void abortingScan(); /** Called during second pass, when MkTemplate has found an area in the image whose MD5Sum matches that of a file @param file The file that matches @param offInImage Offset of the match from start of image */ virtual void matchFound(const FilePart* file, uint64 offInImage); /** The MkTemplate operation has finished. @param imageSize Length of the image file */ virtual void finished(uint64 imageSize); }; //______________________________________________________________________ /** Line content with whitespace and '=' removed and left/right side swapped, i.e. " xyz= foo" becomes "fooxyz". */ struct MkTemplate::PartLine { string text; size_t split; // Offset of first char after "foo" bool operator<(const PartLine& x) const { int cmp = text.compare(x.text); return cmp < 0 || (cmp == 0 && split < x.split); } }; MkTemplate::~MkTemplate() { } void MkTemplate::setMatchExec(const string& me) { matchExec = me; } void MkTemplate::debugRangeInfo(uint64 start, uint64 end, const char* msg, const PartialMatch* x) { printRangeInfo(start, end, msg, x); if (oldAreaEnd != start) debugRangeFailed(); oldAreaEnd = end; } #endif jigdo-0.7.3/src/net/download-test.cc0000644000175000017500000000000010105633471017065 0ustar richardrichardjigdo-0.7.3/src/net/download.cc0000644000175000017500000003107010154632020016114 0ustar richardrichard/* $Id: download.cc,v 1.22 2004/12/05 16:15:12 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Download data from URL, write to output function, report on progress This is the one and only file which accesses libcurl directly. */ #include #include #include #include #include #include #if HAVE_UNAME # include #endif #include #include #include #include #include #include #include //______________________________________________________________________ string Download::userAgent; struct curl_slist* Download::extraHeaders = 0; DEBUG_UNIT("download") namespace { Logger curlDebug("curl"); } // CURLSH* Download::shHandle = 0; // Initialize (g)libcurl void Download::init() { glibcurl_init(); // shHandle = curl_share_init(); // curl_share_setopt(shHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); glibcurl_set_callback(glibcurlCallback, 0); if (extraHeaders == 0) { // Do not send "Pragma: no-cache" header extraHeaders = curl_slist_append(extraHeaders, "Pragma:"); } if (userAgent.empty()) { userAgent = "jigdo/" JIGDO_VERSION; # if WINDOWS userAgent += " (Windows"; OSVERSIONINFO info; memset(&info, 0, sizeof(OSVERSIONINFO)); info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&info) != 0) { const char* s = ""; if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { // 95/98/Me if (info.dwMinorVersion < 10) s = " 95"; else if (info.dwMinorVersion < 90) s = " 98"; else s = " Me"; } else if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) { // NT/00/XP/03 if (info.dwMajorVersion < 5) s = " NT"; else if (info.dwMinorVersion == 0) s = " 2000"; else if (info.dwMinorVersion == 1) s = " XP"; else if (info.dwMinorVersion == 2) s = " 2003"; else s = " >2003"; } userAgent += s; } userAgent += ')'; # elif HAVE_UNAME struct utsname ubuf; if (uname(&ubuf) == 0) { userAgent += " ("; userAgent += ubuf.sysname; userAgent += ' '; userAgent += ubuf.release; userAgent += ')'; } # endif userAgent += " (+http://atterer.net/jigdo/) "; //userAgent += curl_version(); const char* p = curl_version(); while (*p != ' ' && *p != '\0') userAgent += *p++; debug("User-Agent: %1", userAgent); userAgent += '\0'; } } void Download::cleanup() { debug("cleanup"); // Dumps core when called - wish I knew why: //curl_share_cleanup(shHandle); glibcurl_cleanup(); if (extraHeaders != 0) curl_slist_free_all(extraHeaders); userAgent.erase(); } //______________________________________________________________________ Download::Download(const string& uri, Output* o) : handle(0), uriVal(uri), uriValWithoutNull(uri), resumeOffsetVal(0), currentSize(0), outputVal(o), state(CREATED), stopLaterId(0), insideNewData(false) { /* string::data() just points at the "raw" memory that contains the string data. In contrast, string::c_str() may create a temporary buffer, add the null byte, and destroy that buffer during the next method invocation on that string. Since libcurl needs access to the URL all the time, using c_str() seems unwise/unportable. Add terminator to data() instead. */ uriVal += '\0'; curlError = new char[CURL_ERROR_SIZE]; } //________________________________________ Download::~Download() { debug("~Download %1", uriVal.data()); Assert(!insideNewData); delete[] curlError; // stop(); if (handle != 0) { glibcurl_remove(handle); debug("~Download: curl_easy_cleanup(%1)", (void*)handle); curl_easy_cleanup(handle); } if (stopLaterId != 0) g_source_remove(stopLaterId); } //______________________________________________________________________ // void Download::setPragmaNoCache(bool pragmaNoCache) { // Paranoid(state == CREATED || failed() || succeeded() || interrupted()); // // Force reload from originating server, bypassing proxies? // if (pragmaNoCache) // HTRequest_addGnHd(request, HT_G_PRAGMA_NO_CACHE); // else // HTRequest_setGnHd(request, static_cast(HTRequest_gnHd(request) // & ~HT_G_PRAGMA_NO_CACHE)); // } /* Important: The handle can be used several times - we must ensure that any non-default settings (e.g. "Range" header) are reset before reusing it. */ void Download::run() { debug("run resumeOffset=%1", resumeOffset()); Assert(outputVal != 0); // Must have set up output if (handle == 0) { handle = curl_easy_init(); Assert(handle != 0); } if (curlDebug) { // Enable debug output curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); } // Supply error string buffer curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curlError); curlError[0] = '\0'; curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1); // Follow redirects curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); // Enable referer for HTTP redirs (CURLOPT_REFERER to be set by creator) curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1); // Set user agent curl_easy_setopt(handle, CURLOPT_USERAGENT, userAgent.data()); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curlWriter); curl_easy_setopt(handle, CURLOPT_WRITEDATA, this); curl_easy_setopt(handle, CURLOPT_PRIVATE, this); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, extraHeaders); //curl_easy_setopt(handle, CURLOPT_SHARE, shHandle); glibcurl_add(handle); // Set URL curl_easy_setopt(handle, CURLOPT_URL, uriVal.data()); //Paranoid(handle != 0); // Don't call this after stop() state = RUNNING; // Shall we resume the download from a certain offset? currentSize = resumeOffset(); curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, resumeOffset()); // TODO: CURLOPT_PROXY* return; } //______________________________________________________________________ size_t Download::curlWriter(void* data, size_t size, size_t nmemb, void* selfPtr) { Download* self = static_cast(selfPtr); unsigned len = size * nmemb; if (self->stopLaterId != 0) return len; self->insideNewData = true; double contentLen; if (curl_easy_getinfo(self->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLen) == CURLE_OK && contentLen > 0.5) { self->outputVal->download_dataSize( static_cast(contentLen + self->resumeOffset())); } //if (self->state == PAUSE_SCHEDULED) self->pauseNow(); self->currentSize += len; self->outputVal->download_data(reinterpret_cast(data), len, self->currentSize); self->insideNewData = false; return len; } //______________________________________________________________________ void Download::glibcurlCallback(void*) { int inQueue; while (true) { CURLMsg* msg = curl_multi_info_read(glibcurl_handle(), &inQueue); if (msg == 0) break; char* privatePtr; curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &privatePtr); Download* download = reinterpret_cast(privatePtr); if (msg->msg == CURLMSG_DONE) { debug("glibcurlCallback: Download %1 done, code %2 (%3)", download, msg->data.result, download->curlError); if (msg->data.result == CURLE_OK) { download->state = SUCCEEDED; download->outputVal->download_succeeded(); } else { State newState = (msg->data.result == CURLE_PARTIAL_FILE ? INTERRUPTED : ERROR); download->generateError(newState, msg->data.result); } //glibcurl_remove(download->handle); } } } //______________________________________________________________________ /* Pause download by removing the request's socket from the list of sockets to call select() with. */ // void Download::pauseNow() { // Paranoid(state == PAUSE_SCHEDULED); // state = PAUSED; // if (handle == 0) return; // Assert(glibcurl_remove(handle) == CURLM_OK); // debug("Download::pauseNow"); // } void Download::pause() { Assert(!insideNewData); state = PAUSED; if (handle == 0) return; Assert(glibcurl_remove(handle) == CURLM_OK); debug("Download::pause"); } // Analogous to pauseNow() above void Download::cont() { /* Broken ATM: libcurl will go * Connection 0 seems to be dead! * Closing connection #0 * About to connect() to 127.0.0.1 port 8000 * Connected to localhost (127.0.0.1) port 8000 ... and then restart the transfer, but without the right range header */ Assert(false); //if (state == PAUSE_SCHEDULED) state = RUNNING; if (state == RUNNING) return; Assert(paused()); state = RUNNING; if (handle == 0) return; glibcurl_add(handle); debug("Download::cont"); } //______________________________________________________________________ void Download::stop() { if (handle == 0) return; if (state == ERROR || state == INTERRUPTED || state == SUCCEEDED) return; state = INTERRUPTED;//ERROR;//SUCCEEDED; if (insideNewData) { debug("stop later"); // Cannot call curl_easy_cleanup() (segfaults), so do it later if (stopLaterId != 0) return; stopLaterId = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, &stopLater_callback, (gpointer)this, NULL); Assert(stopLaterId != 0); // because we use 0 as a special value } else { debug("stop now: curl_easy_cleanup(%1)", (void*)handle); glibcurl_remove(handle); curl_easy_cleanup(handle); handle = 0; } // None of this is really the right thing. Believe me, I tried both. ;-/ //string err = _("Download stopped"); //outputVal->download_failed(&err); //outputVal->download_succeeded(); } gboolean Download::stopLater_callback(gpointer data) { Download* self = static_cast(data); Assert(!self->insideNewData); debug("stopLater_callback: curl_easy_cleanup(%1)", (void*)self->handle); if (self->handle != 0) { glibcurl_remove(self->handle); curl_easy_cleanup(self->handle); self->handle = 0; } self->stopLaterId = 0; return FALSE; // "Don't call me again" } //______________________________________________________________________ // Call output->error() with appropriate string taken from request object /* If this is called, the Download is assumed to have failed in a non-recoverable way. cc is a CURLcode. Note that outputVal may decide to delete us when called. */ void Download::generateError(State newState, int cc) { debug("generateError: state=%1 newState=%2 curlError=%3", state, newState, cc); if (state == ERROR || state == INTERRUPTED || state == SUCCEEDED) return; string s; state = newState; /* libcurl is not internationalized, so the string always ought to be UTF-8. Oh well, check just to be sure. */ bool validUtf8 = g_utf8_validate(curlError, -1, NULL); Assert(validUtf8); if (!validUtf8) { s = _("Error"); outputVal->download_failed(&s); return; } switch (cc) { case CURLE_PARTIAL_FILE: s = _("Transfer interrupted"); break; case CURLE_HTTP_RETURNED_ERROR: { int httpCode = 0; for (const char* p = curlError; *p != 0; ++p) { if (*p >= '0' && *p <= '9') httpCode = 10 * httpCode + *p - '0'; else httpCode = 0; } debug("genErr: `%1' => %2", curlError, httpCode); if (httpCode == 0) break; switch (httpCode) { case 305: s = _("305 Use Proxy"); break; case 400: s = _("400 Bad Request"); break; case 401: s = _("401 Unauthorized"); break; case 403: s = _("403 Forbidden"); break; case 404: s = _("404 Not Found"); break; case 407: s = _("407 Proxy Authentication Required"); break; case 408: s = _("408 Request Timeout"); break; case 500: s = _("500 Internal Server Error"); break; case 501: s = _("501 Not Implemented"); break; case 503: s = _("503 Service Unavailable"); break; } break; } default: # if ENABLE_NLS // See lib/strerror.c in curl's source for possible error strings s = gettext(curl_easy_strerror(static_cast(cc))); # else s = curl_easy_strerror(static_cast(cc)); # endif s[0] = toupper(s[0]); } if (s.empty() && curlError[0] != '\0') { s = toupper(curlError[0]); s += (curlError + 1); } curlError[0] = '\0'; outputVal->download_failed(&s); } jigdo-0.7.3/src/net/download.hh0000644000175000017500000002124210431672643016143 0ustar richardrichard/* $Id: download.hh,v 1.17 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Download data from URL, write to output function, report on progress This is an abstraction of the HTTP/FTP download facility. The rest of the program only calls libwww indirectly through this code. */ #ifndef DOWNLOAD_HH #define DOWNLOAD_HH #include #include #include // #include typedef void CURL; typedef void CURLSH; struct curl_slist; #ifdef ERROR # undef ERROR /* Windows... */ #endif //______________________________________________________________________ /** Class containing the state of a download. libwww must be properly initialised before usage (this happens in glibwww-init.cc). The purpose of this class is a minimal API for downloading HTTP and FTP URLs. A few useful extra features are added by Job::SingleURL. The Output object is e.g. a GtkSingleUrl for single-file downloads */ class Download { public: /// Class for outputting progress reports and data class Output; /** Initialize libwww - call this before starting any downloads */ static void init(); /** Clean up - call this after all requests are finished */ static void cleanup(); Download(const string& uri, Output* o /*= 0*/); ~Download(); /** Set offset to resume from - download must not yet have been started, call before run(). Caution: The offset is not reset after the download has finished/failed and will be reused if you re-run(), so better *always* call this immediately before run(). */ inline void setResumeOffset(uint64 offset); /** Value passed to setResumeOffset() */ inline uint64 resumeOffset() const; /** Whether to send a "Pragma: no-cache" header. The header is sent iff pragmaNoCache==true. Caution: The setting is not reset after the download has finished/failed and will be reused if you re-run(), so better *always* call this immediately before run(). */ // void setPragmaNoCache(bool pragmaNoCache); /** Start downloading. Returns (almost) immediately and runs the download in the background via the glib/libwww event loop. If resumeOffset > 0, continue downloading the data from a given byte position. If a resume is not possible (e.g. server does not support HTTP ranges), there's an error. */ void run(); inline const string& uri() const; /** Pause the request. Actually, this only sets a flag, which causes the request to be paused at the next convenient opportunity. The socket of the Download is no longer passed to select(). */ /*inline*/ void pause(); /** Continue with the download */ void cont(); /** Is the download paused? (i.e. really paused now, not just pause() called.) */ inline bool paused() const; /** Is the download paused, or is it not yet paused, but will be soon? */ //inline bool pausedSoon() const; /** Did the download fail with an error? */ inline bool failed() const; /** Is download finished? (Also returns true if FTP/HTTP1.0 connection dropped) */ inline bool succeeded() const; /** The connection was interrupted during the download, or it timed out. */ inline bool interrupted() const; /** Forcibly stop this download. Careful, in case we are pipelining it is unavoidable that other (pending and not pending) downloads in the same pipeline also fail; they'll have to be resumed. [outofdate:stop() must not be called when libwww is delivering data, i.e. you must not call it from your download_data() method.] stop() is special in that it neither calls job_failed() nor job_succeeded(). Usually, you will have to call one or the other (depending on context) to notify everyone that the request was stopped. */ void stop(); /** Return the Output object */ inline Output* output() const; /** Set the Output object */ inline void setOutput(Output* o); private: enum State { CREATED, // but not run() yet RUNNING, // downloading //PAUSE_SCHEDULED, // will switch to pause next time data arrives PAUSED, // socket no longer being polled, we'll get no more data INTERRUPTED, // like ERROR, but will try resuming the download ERROR, SUCCEEDED }; // Called by libcurl when data is received from the net static size_t curlWriter(void* data, size_t size, size_t nmemb, void* selfPtr); // Called by glibcurl after curl_multi_perform() static void glibcurlCallback(void*); // Unregister request from glibwww event loop // void pauseNow(); // Call output->error() with appropriate string taken from request object void generateError(State newState = ERROR, int cc = -1); /* A callback function which is registered if the download needs to be stopped. It'll get executed the next time the main glib loop is executed. This delayed execution is necessary because libwww doesn't like Download::stop() being called from download_newData(). */ static gboolean stopLater_callback(gpointer data); // static CURLSH* shHandle; // Handle of curl_shared object CURL* handle; // Handle of curl_easy object char* curlError; // Curl error string buffer string uriVal; // Careful: Includes a trailing null byte! string uriValWithoutNull; uint64 resumeOffsetVal; uint64 currentSize; Output* outputVal; // Usually points to a Job::SingleUrl State state; unsigned stopLaterId; // glib idle function id, or 0 if none static string userAgent; static struct curl_slist* extraHeaders; bool insideNewData; }; //______________________________________________________________________ /** A derived class of Output must be supplied by the user of Download, to take care of the downloaded data and to output progress reports. */ class Download::Output { public: /** The Download object does *not* delete its Output. */ virtual ~Output() { } /* Called by the Download when it is deleted. If the Output object considers itself owned by its Download, it can delete itself. */ //virtual void download_deleted() = 0; /** Called as soon as the size of the downloaded data is known. May not be called at all if the size is unknown. May be called before the first download_data() or sometime afterwards. Problem with libwww: Returns size as long int - 2 GB size limit! */ virtual void download_dataSize(uint64 n) = 0; /** Called during download whenever data arrives, with the data that just arrived. You can write the data to a file, copy it away etc. currentSize is the offset into the downloaded data (including the "size" new bytes) - useful for "x% done" messages. */ virtual void download_data(const byte* data, unsigned size, uint64 currentSize) = 0; /** Called when download is complete. If this is called, you may want to check whether dataSize() was ever called, and if it was, check whether currentSize == dataSize. If this is not the case (i.e. less data was received than the amount promised by the server), consider resume()ing the download. */ virtual void download_succeeded() = 0; /** Short progress message ("Timeout", "404 not found") which means that the download has failed. From now on, Download::failed() will be true. Message is always valid UTF-8 */ virtual void download_failed(string* message) = 0; /** Short progress message ("Looking up host", "Connecting" etc). As a special case, an empty string indicates that the download proper has begun. Message is always valid UTF-8 */ virtual void download_message(string* message) = 0; }; //______________________________________________________________________ const string& Download::uri() const { return uriValWithoutNull; } Download::Output* Download::output() const { return outputVal; } void Download::setOutput(Download::Output* o) { outputVal = o; } // void Download::pause() { // if (state == RUNNING) state = PAUSE_SCHEDULED; // } bool Download::paused() const { return state == PAUSED; } // bool Download::pausedSoon() const { // return state == PAUSED || state == PAUSE_SCHEDULED; } bool Download::failed() const { return state == ERROR; } bool Download::succeeded() const { return state == SUCCEEDED; } bool Download::interrupted() const { return state == INTERRUPTED; } uint64 Download::resumeOffset() const { return resumeOffsetVal; } void Download::setResumeOffset(uint64 resumeOffset) { resumeOffsetVal = resumeOffset; } #endif jigdo-0.7.3/src/net/glibwww-callbacks.cc0000644000175000017500000002035707701377757017745 0ustar richardrichard/* $Id: glibwww-callbacks.cc,v 1.1.1.1 2003/07/04 22:30:07 atterer Exp $ -*- C++ -*- This code was taken from glibwww2 , main author: James Henstdridge , distributable under GPL, v2 or later. Added support for compilation under Windows (mingw32). */ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ #include #include #undef PACKAGE #undef _ extern "C" { #include #include } #define WWW_HIGH_PRIORITY (G_PRIORITY_HIGH_IDLE + 50) #define WWW_LOW_PRIORITY G_PRIORITY_LOW #define WWW_SCALE_PRIORITY(p) ((WWW_HIGH_PRIORITY - WWW_LOW_PRIORITY) * p \ / HT_PRIORITY_MAX + WWW_LOW_PRIORITY) #define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR) #define WRITE_CONDITION (G_IO_OUT | G_IO_ERR) #define EXCEPTION_CONDITION (G_IO_PRI) /* Windows: Need G_IO_IN for WRITE_CONDITION, otherwise connect() hangs */ #ifdef G_OS_WIN32 # undef WRITE_CONDITION # define WRITE_CONDITION (G_IO_IN | G_IO_OUT | G_IO_ERR) #endif #if HTEVENT_TYPES != 3 # warning "HTEVENT_TYPES != 3 not supported" # warning "Compile libwww without WWW_WIN_ASYNC for Windows!" #endif typedef struct _SockEventInfo SockEventInfo; struct _SockEventInfo { SOCKET s; HTEventType type; HTEvent *event; guint io_tag; guint timer_tag; }; typedef struct _SockInfo SockInfo; struct _SockInfo { SOCKET s; GIOChannel *io; SockEventInfo ev[HTEvent_TYPES]; }; static GHashTable *sockhash = NULL; static SockInfo * get_sock_info(SOCKET s, gboolean create) { SockInfo *info; if (!sockhash) sockhash = g_hash_table_new(g_direct_hash, g_direct_equal); info = static_cast( g_hash_table_lookup(sockhash, GINT_TO_POINTER(s))); if (!info && create) { info = g_new0(SockInfo, 1); info->s = s; # ifdef G_OS_WIN32 info->io = g_io_channel_win32_new_socket(s); # else info->io = g_io_channel_unix_new(s); # endif info->ev[0].s = info->ev[1].s = info->ev[2].s = s; info->ev[0].type = HTEvent_READ; info->ev[1].type = HTEvent_WRITE; info->ev[2].type = HTEvent_OOB; info->ev[0].io_tag = info->ev[1].io_tag = info->ev[2].io_tag = 0; info->ev[0].timer_tag = info->ev[1].timer_tag = info->ev[2].timer_tag = 0; g_hash_table_insert(sockhash, GINT_TO_POINTER(s), info); } return info; } static gboolean glibwww_timeout_func (gpointer data); static gboolean glibwww_io_func(GIOChannel *source, GIOCondition condition, gpointer data); static int glibwww_event_register (SOCKET s, HTEventType type, HTEvent *event) { //fprintf(stderr, "glibwww_event_register socket=%d type=%d event=%p\n", s, int(type), event); SockInfo *info; gint priority = G_PRIORITY_DEFAULT; GIOCondition condition; if (s == INVSOC || HTEvent_INDEX(type) >= HTEvent_TYPES) return 0; info = get_sock_info(s, TRUE); info->ev[HTEvent_INDEX(type)].event = event; switch (HTEvent_INDEX(type)) { case HTEvent_INDEX(HTEvent_READ): condition = static_cast(READ_CONDITION); break; case HTEvent_INDEX(HTEvent_WRITE): condition = static_cast(WRITE_CONDITION); break; case HTEvent_INDEX(HTEvent_OOB): condition = static_cast(EXCEPTION_CONDITION); break; default: g_assert_not_reached (); condition = static_cast(0); /* this should never occur */ } if (event->priority != HT_PRIORITY_OFF) priority = WWW_SCALE_PRIORITY(event->priority); if (!info->ev[HTEvent_INDEX(type)].io_tag) { info->ev[HTEvent_INDEX(type)].io_tag = g_io_add_watch_full(info->io, priority, condition, glibwww_io_func, &info->ev[HTEvent_INDEX(type)], NULL); } if (event->millis >= 0 && !info->ev[HTEvent_INDEX(type)].timer_tag) { info->ev[HTEvent_INDEX(type)].timer_tag = g_timeout_add_full(priority, event->millis, glibwww_timeout_func, &info->ev[HTEvent_INDEX(type)], NULL); } return HT_OK; } static int glibwww_event_unregister (SOCKET s, HTEventType type) { SockInfo *info = get_sock_info(s, FALSE); //fprintf(stderr, "glibwww_event_unregister socket=%d type=%d info=%p\n", s, int(type), info); if (info) { if (info->ev[HTEvent_INDEX(type)].io_tag) g_source_remove(info->ev[HTEvent_INDEX(type)].io_tag); if (info->ev[HTEvent_INDEX(type)].timer_tag) g_source_remove(info->ev[HTEvent_INDEX(type)].timer_tag); info->ev[HTEvent_INDEX(type)].event = NULL; info->ev[HTEvent_INDEX(type)].io_tag = 0; info->ev[HTEvent_INDEX(type)].timer_tag = 0; # ifdef G_OS_WIN32 /* clean up sock hash if needed */ if (info->ev[0].io_tag == 0 && info->ev[1].io_tag == 0 && info->ev[2].io_tag == 0) { /*g_message("Freeing sock:%d", s);*/ g_hash_table_remove(sockhash, GINT_TO_POINTER(s)); g_io_channel_unref(info->io); g_free(info); } # endif return HT_OK; } return HT_ERROR; } static gboolean glibwww_timeout_func (gpointer data) { SockEventInfo *info = (SockEventInfo *)data; HTEvent *event = info->event; if (event) (* event->cbf) (info->s, event->param, HTEvent_TIMEOUT); return info->timer_tag != 0; /* XXXX a hack */ } static gboolean glibwww_io_func(GIOChannel */*source*/, GIOCondition /*condition*/, gpointer data) { SockEventInfo *info = (SockEventInfo *)data; HTEvent *event = info->event; //fprintf(stderr, "glibwww_io_func: event %p, event->cbf %p\n", event, event->cbf); if (info->timer_tag) { g_source_remove(info->timer_tag); info->timer_tag = 0; } if (event && event->millis >= 0) { gint priority = G_PRIORITY_DEFAULT; if (event->priority != HT_PRIORITY_OFF) priority = WWW_SCALE_PRIORITY(event->priority); info->timer_tag = g_timeout_add_full(priority, info->event->millis, glibwww_timeout_func, info, NULL); } if (event) (* event->cbf) (info->s, event->param, info->type); return info->io_tag != 0; /* XXXX a hack */ } static GHashTable *timers = NULL; static gboolean glibwww_dispatch_timer(gpointer data) { HTTimer *timer = (HTTimer *)data; HTTimer_dispatch(timer); return FALSE; } static BOOL glibwww_timer_register(HTTimer *timer) { guint tag; if (!timers) timers = g_hash_table_new(g_direct_hash, g_direct_equal); tag = g_timeout_add(HTTimer_expiresRelative(timer), glibwww_dispatch_timer, timer); g_hash_table_insert(timers, timer, GUINT_TO_POINTER(tag)); return YES; } static BOOL glibwww_timer_unregister(HTTimer *timer) { guint tag; if (!timers) return NO; tag = GPOINTER_TO_UINT(g_hash_table_lookup(timers, timer)); if (tag) { g_source_remove(tag); g_hash_table_remove(timers, timer); return YES; } return NO; } void glibwww_register_callbacks(void) { HTEvent_setRegisterCallback(glibwww_event_register); HTEvent_setUnregisterCallback(glibwww_event_unregister); HTTimer_registerSetTimerCallback(glibwww_timer_register); HTTimer_registerDeleteTimerCallback(glibwww_timer_unregister); } jigdo-0.7.3/src/net/glibwww-init.cc0000644000175000017500000003336207701377756016770 0ustar richardrichard/* $Id: glibwww-init.cc,v 1.1.1.1 2003/07/04 22:30:06 atterer Exp $ -*- C++ -*- This code was taken from glibwww2 , main author: James Henstdridge , distributable under GPL, v2 or later. */ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ #undef PACKAGE #undef _ extern "C" { #include #include #include #include #include #include #include #include #include } /* clean up the cpp namespace -- libwww is particularly dirty */ #undef PACKAGE #undef VERSION #undef _ #include #include #ifndef FTP_PORT #define FTP_PORT 21 #endif #ifndef HTTP_PORT #define HTTP_PORT 80 #endif /*#include "WWWMIME.h"*/ static void HTMIMEInit (void); /*static*/ void glibwww_parse_proxy_env (void); static int HTProxyFilter (HTRequest *request, void *param, int status); static int HTRedirectFilter (HTRequest *request, HTResponse *response, void *param, int status); static int HTCredentialsFilter (HTRequest *request, void *param, int status); static int HTAuthFilter (HTRequest *request, HTResponse *response, void *param, int status); static int HTAuthInfoFilter (HTRequest *request, HTResponse *response, void *param, int status); static gboolean exitfunc = FALSE; void glibwww_init(const gchar *appName, const gchar *appVersion) { if (!HTLib_isInitialized()) HTLibInit(appName, appVersion); /*HTAlertInit(); HTAlert_setInteractive(NO);*/ HTTransport_add("tcp", HT_TP_SINGLE,HTReader_new,HTWriter_new); HTTransport_add("buffered_tcp",HT_TP_SINGLE,HTReader_new,HTBufferWriter_new); HTTransport_add("local", HT_TP_SINGLE,HTReader_new,HTWriter_new); HTProtocol_add("ftp", "tcp", FTP_PORT, NO, HTLoadFTP, NULL); HTProtocol_add("http", "buffered_tcp", HTTP_PORT, NO, HTLoadHTTP, NULL); HTProtocol_add("file", "local", 0, NO, HTLoadFile, NULL); HTNet_setMaxSocket(6); HTNet_addBefore(HTCredentialsFilter, "http://*", NULL, HT_FILTER_LATE); HTNet_addBefore(HTProxyFilter, NULL, NULL, HT_FILTER_LATE); HTNet_addAfter(HTAuthFilter, "http://*", NULL, HT_NO_ACCESS, HT_FILTER_MIDDLE); HTNet_addAfter(HTAuthFilter, "http://*", NULL, HT_REAUTH, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "http://*", NULL, HT_PERM_REDIRECT, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "http://*", NULL, HT_FOUND, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "http://*", NULL, HT_SEE_OTHER, HT_FILTER_MIDDLE); HTNet_addAfter(HTRedirectFilter, "http://*", NULL, HT_TEMP_REDIRECT, HT_FILTER_MIDDLE); HTNet_addAfter(HTAuthInfoFilter, "http://*", NULL, HT_ALL, HT_FILTER_MIDDLE); HTAA_newModule ("basic", HTBasic_generate, HTBasic_parse, NULL, HTBasic_delete); //[RA]glibwww_parse_proxy_env(); /* set proxy from our config files ... */ /* glibwww_add_proxy("http", ...); */ /* glibwww_add_proxy("ftp", ...); */ HTMIME_setSaveStream (HTSaveLocally); HTFormat_addConversion("message/rfc822", "*/*", HTMIMEConvert, 1.0, 0.0, 0.0); HTFormat_addConversion("message/x-rfc822-foot", "*/*", HTMIMEFooter, 1.0, 0.0, 0.0); HTFormat_addConversion("message/x-rfc822-head", "*/*", HTMIMEHeader, 1.0, 0.0, 0.0); HTFormat_addConversion("message/x-rfc822-cont", "*/*", HTMIMEContinue, 1.0, 0.0, 0.0); HTFormat_addConversion("message/x-rfc822-upgrade", "*/*", HTMIMEUpgrade, 1.0, 0.0, 0.0); HTFormat_addConversion("message/x-rfc822-partial", "*/*", HTMIMEPartial, 1.0, 0.0, 0.0); HTFormat_addConversion("text/x-http", "*/*", HTTPStatus_new, 1.0, 0.0, 0.0); HTFormat_addCoding("*", HTIdentityCoding, HTIdentityCoding, 0.3); HTFormat_addTransferCoding("deflate", NULL, HTZLib_inflate, 1.0); HTFormat_addTransferCoding("chunked", HTChunkedEncoder,HTChunkedDecoder,1.0); HTMIMEInit(); HTFileInit(); HTHost_setEventTimeout(30000); HTFTP_setTransferMode(FTP_BINARY_TRANSFER_MODE); glibwww_register_callbacks(); if (!exitfunc) g_atexit(glibwww_cleanup); exitfunc = TRUE; /*WWWTRACE = SHOW_MEM_TRACE;*/ } void glibwww_cleanup(void) { if (HTLib_isInitialized()) { HTFormat_deleteAll(); //HTLibTerminate(); Causes segfaults -- RA } } struct ProxyEntry { gchar *protocol; gchar *proxy; }; static GList *proxies = NULL; static GList *noproxy = NULL; void glibwww_add_proxy(const gchar *protocol, const gchar *proxy) { GList *tmp; struct ProxyEntry *ent; for (tmp = proxies; tmp; tmp = tmp->next) { ent = static_cast(tmp->data); if (!g_ascii_strcasecmp(protocol, ent->protocol)) { // g_free(ent->proxy); // ent->proxy = g_strdup(proxy); // RA: allow proxy==null to delete existing entry g_free(ent->proxy); if (proxy) { ent->proxy = g_strdup(proxy); } else { g_free(ent->protocol); proxies = g_list_remove(proxies, ent); g_free(ent); } return; } } // RA: allow proxy==null to delete existing entry if (proxy == 0) return; ent = g_new(struct ProxyEntry, 1); ent->protocol = g_strdup(protocol); ent->proxy = g_strdup(proxy); proxies = g_list_prepend(proxies, ent); } void glibwww_add_noproxy(const gchar *host) { noproxy = g_list_prepend(noproxy, g_strdup(host)); } static const gchar * glibwww_get_proxy(const gchar *url) { gchar *protocol; GList *tmp; if (!url || !proxies) return NULL; if (noproxy) { char *host = HTParse(url, "", PARSE_HOST); char *ptr = strchr(host, ':'); if (ptr != NULL) *ptr = ':'; for (tmp = noproxy; tmp; tmp = tmp->next) { char *nophost = static_cast(tmp->data); char *np = nophost + strlen(nophost); char *hp = host + strlen(host); while (np>=nophost && hp>=host && (*np--==*hp--)) ; if (np==nophost-1 && (hp==host-1 || *hp=='.')) return NULL; } free(host); } protocol = HTParse(url, "", PARSE_ACCESS); for (tmp = proxies; tmp; tmp = tmp->next) { struct ProxyEntry *ent = static_cast(tmp->data); if (!g_ascii_strcasecmp(ent->protocol, protocol)) { HT_FREE(protocol); return ent->proxy; } } HT_FREE(protocol); return NULL; } /*[RA]static*/ void glibwww_parse_proxy_env(void) { static const char *protocollist[] = { "http", "ftp", "news", "wais", "gopher", NULL }; const char **prot = protocollist; char *nop; for (prot = protocollist; *prot != NULL; prot++) { gchar *var = g_strconcat(*prot, "_proxy", NULL); gchar *proxy = getenv(var); if (proxy && proxy[0]) glibwww_add_proxy(*prot, proxy); else { gchar *up = var; while ((*up = TOUPPER(*up))) up++; if ((proxy = getenv(var)) != NULL && proxy[0]) glibwww_add_proxy(*prot, proxy); } g_free(var); } nop = getenv("no_proxy"); if (nop && nop[0]) { char *str = g_strdup(nop); char *ptr = str; char *name; while ((name = HTNextField(&ptr)) != NULL) glibwww_add_noproxy(name); g_free(str); } } /* mime initialisation */ static void HTMIMEInit(void) { struct { char *string; HTParserCallback *pHandler; } fixedHandlers[] = { {"accept", &HTMIME_accept}, {"accept-charset", &HTMIME_acceptCharset}, {"accept-encoding", &HTMIME_acceptEncoding}, {"accept-language", &HTMIME_acceptLanguage}, {"accept-ranges", &HTMIME_acceptRanges}, {"authorization", NULL}, {"cache-control", &HTMIME_cacheControl}, {"connection", &HTMIME_connection}, {"content-encoding", &HTMIME_contentEncoding}, {"content-length", &HTMIME_contentLength}, {"content-range", &HTMIME_contentRange}, {"content-transfer-encoding", &HTMIME_contentTransferEncoding}, {"content-type", &HTMIME_contentType}, {"digest-MessageDigest", &HTMIME_messageDigest}, {"keep-alive", &HTMIME_keepAlive}, {"link", &HTMIME_link}, {"location", &HTMIME_location}, {"max-forwards", &HTMIME_maxForwards}, {"mime-version", NULL}, {"pragma", &HTMIME_pragma}, {"protocol", &HTMIME_protocol}, {"protocol-info", &HTMIME_protocolInfo}, {"protocol-request", &HTMIME_protocolRequest}, {"proxy-authenticate", &HTMIME_authenticate}, {"proxy-authorization", &HTMIME_proxyAuthorization}, {"public", &HTMIME_public}, {"range", &HTMIME_range}, {"referer", &HTMIME_referer}, {"retry-after", &HTMIME_retryAfter}, {"server", &HTMIME_server}, {"trailer", &HTMIME_trailer}, {"transfer-encoding", &HTMIME_transferEncoding}, {"upgrade", &HTMIME_upgrade}, {"user-agent", &HTMIME_userAgent}, {"vary", &HTMIME_vary}, {"via", &HTMIME_via}, {"warning", &HTMIME_warning}, {"www-authenticate", &HTMIME_authenticate}, {"authentication-info", &HTMIME_authenticationInfo}, {"proxy-authentication-info", &HTMIME_proxyAuthenticationInfo} }; size_t i; for (i = 0; i < sizeof(fixedHandlers)/sizeof(fixedHandlers[0]); i++) HTHeader_addParser(fixedHandlers[i].string, NO, fixedHandlers[i].pHandler); } /* the following filters are from HTFilter.c: */ static int HTProxyFilter(HTRequest *request, void */*param*/, int /*status*/) { HTParentAnchor *anchor = HTRequest_anchor(request); char *addr = HTAnchor_physical(anchor); const char *physical = NULL; if ((physical = glibwww_get_proxy(addr))) { HTRequest_setFullURI(request, YES); HTRequest_setProxy(request, physical); } else { HTRequest_setFullURI(request, NO); HTRequest_deleteProxy(request); } return HT_OK; } static int HTRedirectFilter(HTRequest *request, HTResponse *response, void */*param*/, int /*status*/) { HTMethod method = HTRequest_method(request); HTAnchor *new_anchor = HTResponse_redirection(response); if (!new_anchor) return HT_OK; if (!HTMethod_isSafe(method)) return HT_OK; HTRequest_deleteCredentialsAll(request); if (HTRequest_doRetry(request)) { HTRequest_setAnchor(request, new_anchor); HTLoad(request, NO); } else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT, NULL, 0, "HTRedirectFilter"); return HT_OK; } return HT_ERROR; } static int HTCredentialsFilter(HTRequest *request, void *param, int status) { if (HTAA_beforeFilter(request, param, status) == HT_OK) return HT_OK; else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_UNAUTHORIZED, NULL, 0, "HTCredentialsFilter"); return HT_ERROR; } } static int HTAuthFilter(HTRequest *request, HTResponse *response, void *param, int status) { if (HTAA_afterFilter(request, response, param, status) == HT_OK) { HTLoad(request, NO); return HT_ERROR; } return HT_OK; } static int HTAuthInfoFilter(HTRequest *request, HTResponse *response, void *param, int status) { if (!HTResponse_challenge(response)) return HT_OK; else if (HTAA_updateFilter(request, response, param, status) == HT_OK) return HT_OK; else return HT_ERROR; } jigdo-0.7.3/src/net/glibwww-trans.cc0000644000175000017500000001352607701377760017147 0ustar richardrichard/* $Id: glibwww-trans.cc,v 1.1.1.1 2003/07/04 22:30:08 atterer Exp $ -*- C++ -*- This code was taken from glibwww2 , main author: James Henstdridge , distributable under GPL, v2 or later. */ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ extern "C" { #include #include #include } #undef PACKAGE #undef VERSION #undef _ #include #include #include static gboolean delete_request(HTRequest */*request*/) { printf ("delete_request\n"); /*HTRequest_delete(request);*/ return FALSE; } struct LoadToFileData { gchar *url; gchar *file; GWWWLoadToFileFunc callback; gpointer user_data; }; static int after_load_to_file(HTRequest *request, HTResponse */*response*/, void *param, int status) { struct LoadToFileData *data = (struct LoadToFileData *)param; printf ("after_load_to_file\n"); /* Ignore these after events. We will get another call to the * after filter when the data actually loads. */ switch (status) { case HT_NO_ACCESS: case HT_REAUTH: case HT_PERM_REDIRECT: case HT_FOUND: case HT_SEE_OTHER: case HT_TEMP_REDIRECT: return HT_OK; default: break; } if (data) { if (data->callback) (* data->callback)(data->url, data->file, status, data->user_data); g_free(data->url); g_free(data->file); g_free(data); } /* schedule for the request to be deleted */ g_idle_add((GSourceFunc)delete_request, request); return HT_OK; } GWWWRequest * glibwww_load_to_file(const gchar *url, const gchar *file, GWWWLoadToFileFunc callback, gpointer user_data) { FILE *fp; HTRequest *request; HTStream *writer; struct LoadToFileData *data; g_return_val_if_fail(url != NULL, NULL); g_return_val_if_fail(file != NULL, NULL); if ((fp = fopen(file, "wb")) == NULL) return NULL; request = HTRequest_new(); writer = HTFWriter_new(request, fp, NO); HTRequest_setOutputFormat(request, WWW_SOURCE); HTRequest_setOutputStream(request, writer); HTRequest_setDebugStream(request, writer); HTRequest_setAnchor(request, HTAnchor_findAddress(url)); data = g_new(struct LoadToFileData, 1); data->url = g_strdup(url); data->file = g_strdup(file); data->callback = callback; data->user_data = user_data; HTRequest_addAfter(request, after_load_to_file, NULL, data, HT_ALL, HT_FILTER_LAST, FALSE); if (HTLoad(request, NO) == NO) { fclose(fp); HTRequest_delete(request); return NULL; } return request; } struct LoadToMemData { gchar *url; HTChunk *chunk; GWWWLoadToMemFunc callback; gpointer user_data; }; static int after_load_to_mem(HTRequest *request, HTResponse */*response*/, void *param, int status) { struct LoadToMemData *data = (struct LoadToMemData *)param; printf ("after_load_to_mem\n"); /* Ignore these after events. We will get another call to the * after filter when the data actually loads. */ switch (status) { case HT_NO_ACCESS: case HT_REAUTH: case HT_PERM_REDIRECT: case HT_FOUND: case HT_SEE_OTHER: case HT_TEMP_REDIRECT: return HT_OK; default: break; } if (data->callback) (* data->callback)(data->url, HTChunk_data(data->chunk), HTChunk_size(data->chunk), status, data->user_data); g_free(data->url); HTChunk_delete(data->chunk); g_free(data); /* schedule for the request to be deleted */ g_idle_add((GSourceFunc)delete_request, request); return HT_OK; } GWWWRequest * glibwww_load_to_mem(const gchar *url, GWWWLoadToMemFunc callback, gpointer user_data) { HTRequest *request; HTStream *writer; HTChunk *chunk = NULL; struct LoadToMemData *data; g_return_val_if_fail(url != NULL, NULL); request = HTRequest_new(); writer = HTStreamToChunk(request, &chunk, 0); HTRequest_setOutputFormat(request, WWW_SOURCE); HTRequest_setOutputStream(request, writer); HTRequest_setDebugStream(request, writer); HTRequest_setAnchor(request, HTAnchor_findAddress(url)); data = g_new(struct LoadToMemData, 1); data->url = g_strdup(url); data->chunk = chunk; data->callback = callback; data->user_data = user_data; HTRequest_addAfter(request, after_load_to_mem, NULL, data, HT_ALL, HT_FILTER_LAST, FALSE); if (HTLoad(request, NO) == NO) { HTChunk_delete(chunk); HTRequest_delete(request); return NULL; } return request; } gboolean glibwww_abort_request(GWWWRequest *request) { g_return_val_if_fail(request != NULL, FALSE); return HTRequest_kill(request) == YES; } void glibwww_request_progress(GWWWRequest *request, glong *nread, glong *total) { glong tot = HTAnchor_length(HTRequest_anchor(request)); glong nr = -1; if (tot > 0) nr = HTRequest_bodyRead(request); else nr = HTRequest_bytesRead(request); if (nread) *nread = nr; if (total) *total = tot; } jigdo-0.7.3/src/net/glibwww.hh0000644000175000017500000000501607701377755016033 0ustar richardrichard/* $Id: glibwww.hh,v 1.1.1.1 2003/07/04 22:30:05 atterer Exp $ -*- C++ -*- This code was taken from glibwww2 , main author: James Henstdridge , distributable under GPL, v2 or later. */ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ #ifndef _GLIBWWW_H_ #define _GLIBWWW_H_ #include #include /* [RA] public: */ void glibwww_parse_proxy_env(); /* Defined here so we don't have to include any libwww headers */ typedef struct _HTRequest GWWWRequest; /* If status < 0, an error occured */ typedef void (*GWWWLoadToFileFunc) (const gchar *url, const gchar *file, int status, gpointer user_data); typedef void (*GWWWLoadToMemFunc) (const gchar *url, const gchar *buffer, int size, int status, gpointer user_data); /* Initialise enough of libwww for doing http/ftp downloads with * authentication, redirection and proxy support. */ void glibwww_init (const gchar *appName, const gchar *appVersion); void glibwww_cleanup (void); /* not necessary -- registered with g_atexit() */ /* register the GUI dialogs for glibwww. This will take care of all the * authentication and progress bar stuff for the application. */ void glibwww_register_gnome_dialogs (void); /* Setup proxies as needed -- use the http://proxyhost:port/ notation */ void glibwww_add_proxy (const gchar *protocol, const gchar *proxy); void glibwww_add_noproxy (const gchar *host); /* Load a url to a file or to memory. The callback will be invoked * exactly once. */ GWWWRequest *glibwww_load_to_file (const gchar *url, const gchar *file, GWWWLoadToFileFunc callback, gpointer user_data); GWWWRequest *glibwww_load_to_mem (const gchar *url, GWWWLoadToMemFunc callback, gpointer user_data); /* Abort a currently running download */ gboolean glibwww_abort_request(GWWWRequest *request); /* Get the progress of the currently running request. nread or total may * return a negative result if it can't determine how far along things are. */ void glibwww_request_progress (GWWWRequest *request, glong *nread, glong *total); /* This is called by glibwww_init, but may be useful if you only want to * use the callbacks provided by glibwww for embedding libwww into the * glib main loop */ void glibwww_register_callbacks (void); #endif jigdo-0.7.3/src/net/libwww-HTFTP.c0000755000175000017500000014576207717143066016403 0ustar richardrichard/* HTFTP.c ** FILE TRANSFER PROTOCOL (FTP) CLIENT ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: libwww-HTFTP.c,v 1.4 2003/08/15 11:38:30 atterer Exp $ ** ** A cache of control connections is kept. ** ** Note: Port allocation (not used anymore) ** ** It is essential that the port is allocated by the system, rather ** than chosen in rotation by us (FTP_POLL_PORTS), or the following ** problem occurs. ** ** It seems that an attempt by the server to connect to a port which has ** been used recently by a listen on the same socket, or by another ** socket this or another process causes a hangup of (almost exactly) ** one minute. Therefore, we have to use a rotating port number. ** The problem remains that if the application is run twice in quick ** succession, it will hang for what remains of a minute. ** ** Authors ** TBL Tim Berners-lee ** DD Denis DeLaRoca 310 825-4580 ** LM Lou Montulli ** FM Foteos Macrides ** HF Henrik Frystyk ** AL Ari Luotonen ** ** History: ** 2 May 91 Written TBL, as a part of the WorldWideWeb project. ** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc ** 10 Feb 92 Retry if cached connection times out or breaks ** 8 Dec 92 Bug fix 921208 TBL after DD ** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD ** fails on princeton.edu! ** 27 Dec 93 (FM) Fixed up so FTP now works with VMS hosts. Path ** must be Unix-style and cannot include the device ** or top directory. ** ?? ??? ?? (LM) Added code to prompt and send passwords for non ** anonymous FTP ** 25 Mar 94 (LM) Added code to recognize different ftp server types ** and code to parse dates and sizes on most hosts. ** 27 Mar 93 (FM) Added code for getting dates and sizes on VMS hosts. ** 27 Apr 94 (HF) The module is basically rewritten to conform with ** rfc 959, 1123 and 1579 and turned into a state ** machine. New semantics of ftp URLs are supported. ** 2 May 94 (AL) Fixed possible security hole when the URL contains ** a newline, that could cause multiple commands to be ** sent to an FTP server. ** Sep 95 HFN Rewritten to support streams and persistent conenctions ** and multiplexed IO ** Mar 1998 NG Neil Griffin - Bug fixes and additions for FTP support on NT. ** Jan 2000 JB Joe Bester - Fixed the protocol bug that appeared after ** after the CERT advisory warning on wu. Added code ** to do an incremental streaming of FTP data. ** Feb 2003 Richard Atterer - added REST support ** ** Notes: ** Portions Copyright 1994 Trustees of Dartmouth College ** Code for recognizing different FTP servers and ** parsing "ls -l" output taken from Macintosh Fetch ** program with permission from Jim Matthews, ** Dartmouth Software Development Team. */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "WWWCore.h" #include "WWWStream.h" #include "WWWTrans.h" #include "WWWFile.h" #include "HTReqMan.h" #include "HTNetMan.h" #include "HTFTPDir.h" #include "HTFTP.h" /* Implemented here */ #ifndef FTP_PORT #define FTP_PORT 21 #define FTP_DATA 20 #endif #define WWW_FTP_CLIENT "libwww@" /* If can't get user-info, use this */ #define FTP_DIR(me) ((me)->type=='L' || (me)->type=='N') /* ** Local context structure used in the HTNet object. */ typedef enum _HTFTPState { FTP_SUCCESS = -2, FTP_ERROR = -1, FTP_BEGIN = 0, FTP_NEED_CCON, /* Control connection */ FTP_NEED_LOGIN, FTP_NEED_DCON, /* Data connection */ FTP_NEED_DATA, FTP_NEED_SERVER /* For directory listings */ } HTFTPState; typedef struct _ftp_ctrl { HTChunk * cmd; int repcode; char * reply; char * uid; char * passwd; char * account; HTFTPState state; /* State of the connection */ int substate; /* For hierarchical states */ BOOL sent; /* Read or write command */ BOOL cwd; /* Done cwd */ BOOL reset; /* Expect greeting */ FTPServerType server; /* Type of server */ HTNet * cnet; /* Control connection */ HTNet * dnet; /* Data connection */ BOOL alreadyLoggedIn; } ftp_ctrl; typedef struct _ftp_data { char host[30]; /* Host to contact for data */ char * file; /* File or dir name */ char * offset; /* offset into file */ BOOL pasv; /* Active or passive */ char type; /* 'A', 'I', 'L'(IST), 'N'(LST) */ int complete; /* Check if both ctrl and data is loaded */ BOOL stream_error; } ftp_data; struct _HTStream { const HTStreamClass * isa; HTStream * target; HTRequest * request; ftp_ctrl * ctrl; HTEOLState state; HTChunk * welcome; BOOL junk; /* For too long lines */ BOOL first_line; char buffer[MAX_FTP_LINE+1]; int buflen; HTHost * host; }; struct _HTInputStream { const HTInputStreamClass * isa; }; /* Determine whether we should use passive or active data TCP connections */ typedef enum _FTPDataCon { FTP_DATA_PASV = 0x1, FTP_DATA_PORT = 0x2 } FTPDataCon; PRIVATE FTPDataCon FTPMode = FTP_DATA_PORT; /* Added by Neil Griffin */ PRIVATE FTPTransferMode g_FTPTransferMode = FTP_DEFAULT_TRANSFER_MODE; PRIVATE FTPControlMode g_FTPControlMode = FTP_DEFAULT_CONTROL_MODE; /* ------------------------------------------------------------------------- */ /* FTP Status Line Stream */ /* ------------------------------------------------------------------------- */ /* FTPCleanup ** ---------- ** This function closes the connection and frees memory. ** Returns YES on OK, else NO */ PRIVATE int FTPCleanup (HTRequest * request, int status) { if (request) { HTNet * cnet = HTRequest_net(request); ftp_ctrl * ctrl = (ftp_ctrl *) HTNet_context(cnet); HTStream * input = HTRequest_inputStream(request); if (status == HT_INTERRUPTED) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_INTERRUPT); if (cbf) (*cbf)(request, HT_PROG_INTERRUPT, HT_MSG_NULL, NULL, NULL, NULL); } else if (status == HT_TIMEOUT) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_TIMEOUT); if (cbf) (*cbf)(request, HT_PROG_TIMEOUT, HT_MSG_NULL, NULL, NULL, NULL); } else if (status == HT_LOADED) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); } /* Free control stream with data TO network */ if (!HTRequest_isDestination(request) && input) { if (status == HT_INTERRUPTED) (*input->isa->abort)(input, NULL); else (*input->isa->_free)(input); } /* Remove the request object and our own context structure for ftp */ if (cnet && ctrl) { HTNet * dnet = ctrl->dnet; ftp_data * data = (ftp_data *) HTNet_context(dnet); HTChunk_delete(ctrl->cmd); HT_FREE(ctrl->reply); HT_FREE(ctrl->uid); HT_FREE(ctrl->passwd); HT_FREE(ctrl->account); HT_FREE(ctrl); if (dnet && data) { HT_FREE(data->file); HT_FREE(data); } /* See if we got a content length */ if (status == HT_LOADED) HTAnchor_setLength(HTRequest_anchor(request), HTNet_bytesRead(dnet)); /* Delete the data net object */ HTChannel_deleteInput(HTNet_channel(dnet), status); HTChannel_deleteOutput(HTNet_channel(dnet), status); HTNet_delete(dnet, HT_IGNORE); } HTNet_delete(cnet, status); return YES; } return NO; } /* ScanResponse ** ------------ ** Analyzes the response from the FTP server. ** Returns HT_LOADED if OK, HT_OK if more, HT_ERROR if error ** the control connection. */ PRIVATE int ScanResponse (HTStream * me) { int reply = 0; char cont = '\0'; char *ptr = me->buffer+4; *(me->buffer+me->buflen) = '\0'; /* begin _GM_ */ /* Note: libwww bug ID: GM3 */ me->ctrl->alreadyLoggedIn = NO; /* end _GM_ */ if (isdigit((int) *(me->buffer))) sscanf(me->buffer, "%d%c", &reply, &cont); if (me->first_line) { HTTRACE(PROT_TRACE, "FTP Rx...... `%s\'\n" _ me->buffer); if (!reply) return HT_ERROR; me->first_line = NO; me->ctrl->repcode = reply; StrAllocCopy(me->ctrl->reply, ptr); /* begin _GM_ */ /* Note: libwww bug ID: GM3 */ if ( (reply == 530) && (HTStrCaseStr(me->buffer, "already") != NULL) ) { me->ctrl->alreadyLoggedIn = YES; } else { me->ctrl->alreadyLoggedIn = NO; } /* end _GM_ */ } else { HTChunk_puts(me->welcome, ptr); HTChunk_putc(me->welcome, '\n'); } me->buflen = 0; me->state = EOL_BEGIN; if (cont != '-') { me->first_line = YES; return HT_LOADED; } return HT_OK; } /* ** Searches for FTP header line until buffer fills up or a CRLF or LF ** is found */ PRIVATE int FTPStatus_put_block (HTStream * me, const char * b, int l) { int status; HTHost_setConsumed(me->host, l); while (l-- > 0) { if (me->state == EOL_FCR) { if (*b == LF) { if (!me->junk) { if ((status = ScanResponse(me)) != HT_OK) return status; } else { me->buflen = 0; me->junk = NO; } } } else if (*b == CR) { me->state = EOL_FCR; } else if (*b == LF) { if (!me->junk) { if ((status = ScanResponse(me)) != HT_OK) return status; } else { me->buflen = 0; me->junk = NO; } } else { *(me->buffer+me->buflen++) = *b; if (me->buflen >= MAX_FTP_LINE) { HTTRACE(PROT_TRACE, "FTP Status.. Line too long - chopped\n"); me->junk = YES; if ((status = ScanResponse(me)) != HT_OK) { me->junk = NO; return status; } } } b++; } return HT_OK; } PRIVATE int FTPStatus_put_string (HTStream * me, const char * s) { return FTPStatus_put_block(me, s, (int) strlen(s)); } PRIVATE int FTPStatus_put_character (HTStream * me, char c) { return FTPStatus_put_block(me, &c, 1); } PRIVATE int FTPStatus_flush (HTStream * me) { return (*me->target->isa->flush)(me->target); } PRIVATE int FTPStatus_free (HTStream * me) { int status = HT_OK; if (me->target) { if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; } HTChunk_delete(me->welcome); HT_FREE(me); return HT_OK; } PRIVATE int FTPStatus_abort (HTStream * me, HTList * e) { if (me->target) (*me->target->isa->abort)(me->target, e); HTChunk_delete(me->welcome); HT_FREE(me); HTTRACE(PROT_TRACE, "FTPStatus... ABORTING...\n"); return HT_ERROR; } /* FTPStatus Stream ** ----------------- */ PRIVATE const HTStreamClass FTPStatusClass = { "FTPStatus", FTPStatus_flush, FTPStatus_free, FTPStatus_abort, FTPStatus_put_character, FTPStatus_put_string, FTPStatus_put_block }; PRIVATE HTStream * FTPStatus_new (HTRequest * request, ftp_ctrl * ctrl, HTHost * host) { HTStream * me; if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("FTPStatus_new"); me->isa = &FTPStatusClass; me->request = request; me->first_line = YES; me->welcome = HTChunk_new(256); me->ctrl = ctrl; me->state = EOL_BEGIN; me->host = host; return me; } /* ------------------------------------------------------------------------- */ /* FTP Client Functions for managing control and data connections */ /* ------------------------------------------------------------------------- */ PRIVATE int SendCommand (HTRequest *request, ftp_ctrl *ctrl, char *token, char *pars) { int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2; HTStream * input = HTRequest_inputStream(request); HTChunk_setSize(ctrl->cmd, len); if (pars && *pars) sprintf(HTChunk_data(ctrl->cmd), "%s %s%c%c", token, pars, CR, LF); else sprintf(HTChunk_data(ctrl->cmd), "%s%c%c", token, CR, LF); HTTRACE(PROT_TRACE, "FTP Tx...... %s" _ HTChunk_data(ctrl->cmd)); return (*input->isa->put_block)(input, HTChunk_data(ctrl->cmd), len); } /* HTFTPParseURL ** ------------- ** Scan URL for uid and passwd, and any data type indication. The ** expected format is [user[:password]@]host[:port]. ** If no values are found then use defaults. ** Returns YES if OK, else NO */ PRIVATE BOOL HTFTPParseURL (HTRequest * request, char *url, ftp_ctrl *ctrl, ftp_data *data) { char *login = HTParse(url, "", PARSE_HOST); char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION); char *ptr = strchr(login, '@'); if (ptr) { /* Uid and/or passwd specified */ char *passwd; *ptr = '\0'; if ((passwd = strchr(login, ':'))) { /* Passwd specified */ *passwd++ = '\0'; HTUnEscape(passwd); StrAllocCopy(ctrl->passwd, passwd); } HTUnEscape(login); StrAllocCopy(ctrl->uid, login); } else if (g_FTPControlMode & FTP_ALWAYS_ASK_UID_PW) { ctrl->uid=NULL; ctrl->passwd=NULL; } else { /* Use anonymous */ HTUserProfile * up = HTRequest_userProfile(request); const char * mailaddress = HTUserProfile_email(up); StrAllocCopy(ctrl->uid, "anonymous"); if (mailaddress) StrAllocCopy(ctrl->passwd, mailaddress); else StrAllocCopy(ctrl->passwd, WWW_FTP_CLIENT); } HTTRACE(PROT_TRACE, "FTPParse.... uid `%s\' pw `%s\'\n" _ ctrl->uid ? ctrl->uid : "" _ ctrl->passwd ? ctrl->passwd : ""); /* ** Look for any type in the URI. If not 'type' parameter then look for ** trailing slash. */ if ((ptr = strchr(path, ';')) != NULL) { *ptr = '\0'; if (strncasecomp(ptr, ";type=", 6)) /* Look for type */ data->type = TOUPPER(*(ptr+6)); else if (*(ptr-1) == '/') data->type = 'D'; } else if (*(path+strlen(path)-1) == '/') { *(path+strlen(path)-1) = '\0'; data->type = 'D'; } HTTRACE(PROT_TRACE, "FTPParse.... Datatype %c\n" _ data->type ? data->type : '?'); StrAllocCopy(data->file, path); data->offset = data->file; HT_FREE(login); HT_FREE(path); return YES; } /* Use LIST or NLST ** ---------------- ** This function sets the type field for what type of list we can use ** Returns YES if OK, else NO */ PRIVATE BOOL FTPListType (ftp_data * data, FTPServerType type) { if (!data) return NO; switch (type) { case FTP_GENERIC: data->type='N'; break; case FTP_MACHTEN: data->type='L'; break; case FTP_UNIX: data->type='L'; break; case FTP_VMS: data->type='L'; break; case FTP_CMS: data->type='N'; break; case FTP_DCTS: data->type='N'; break; case FTP_TCPC: data->type='N'; break; case FTP_PETER_LEWIS: data->type='L'; break; case FTP_NCSA: data->type='N'; break; case FTP_WINNT: data->type='L'; break; default: data->type='N'; break; } return YES; } /* Open a Data socket for listening on ** ----------------------------------- ** Set up a port to listen for data ** Returns YES if OK, else NO */ PRIVATE BOOL AcceptDataSocket (HTNet *cnet, HTNet *dnet, ftp_data *data) { if (HTHost_listen(NULL, dnet, "ftp://localhost:0") == HT_ERROR) return NO; /* ** Now we must find out who we are to tell the other guy ** We have to get the local IP interface from the control connection as ** this is not yet set in the unaccepted data socket */ { SockA local_port, local_host; int addr_size = sizeof(local_port); memset((void *) &local_host, '\0', addr_size); memset((void *) &local_port, '\0', addr_size); if (getsockname(HTNet_socket(cnet), (struct sockaddr *) &local_host, &addr_size) < 0 || getsockname(HTNet_socket(dnet), (struct sockaddr *) &local_port, &addr_size) < 0) { HTRequest_addSystemError(HTNet_request(dnet), ERR_FATAL, socerrno, NO, "getsockname"); return NO; } HTTRACE(PROT_TRACE, "FTP......... This host is `%s\'\n" _ HTInetString(&local_host)); { u_long addr = local_host.sin_addr.s_addr; u_short port = local_port.sin_port; sprintf(data->host, "%d,%d,%d,%d,%d,%d", (int)*((unsigned char *)(&addr)+0), (int)*((unsigned char *)(&addr)+1), (int)*((unsigned char *)(&addr)+2), (int)*((unsigned char *)(&addr)+3), (int)*((unsigned char *)(&port)+0), (int)*((unsigned char *)(&port)+1)); } } return YES; } /* HTFTPLogin ** ----------- ** This function makes a login to a ftp-server. It takes the user name ** and passwd specified in ctrl->user and if that fails or an additional ** account is needed, the user is prompted. ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK */ PRIVATE int HTFTPLogin (HTRequest *request, HTNet *cnet, ftp_ctrl *ctrl) { int status; typedef enum _state { SUB_ERROR = -2, SUB_SUCCESS = -1, NEED_SELECT = 0, NEED_GREETING, NEED_REIN, NEED_UID, NEED_UID_NO_REIN, NEED_PASSWD, NEED_ACCOUNT, PROMPT_USER } state; /* Jump into a second level state machine */ while (1) { switch ((state) ctrl->substate) { case NEED_SELECT: { HTAlertCallback * cbf = HTAlert_find(HT_PROG_LOGIN); if (cbf) (*cbf)(request, HT_PROG_LOGIN, HT_MSG_NULL, NULL, NULL, NULL); HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_SELECT\n"); ctrl->substate = ctrl->reset ? NEED_REIN : NEED_GREETING; } break; case NEED_GREETING: HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_GREETING\n"); status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { if (ctrl->repcode/100 == 2) { ctrl->substate = (ctrl->uid && *ctrl->uid) ? NEED_UID : PROMPT_USER; } else { ctrl->substate = SUB_ERROR; } } else { ctrl->substate = SUB_ERROR; } break; case NEED_REIN: HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_REIN\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "REIN", NULL); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { /* ** If the FTP server doesn't support the REIN command, then ** the return code will be 502 */ if ((ctrl->repcode/100 == 2) || (ctrl->repcode == 502)) { ctrl->substate = (ctrl->uid && *ctrl->uid) ? NEED_UID_NO_REIN : PROMPT_USER;/*_NO_REIN?*/ } else { ctrl->substate = SUB_SUCCESS; /* hope the best */ } } else { ctrl->substate = SUB_ERROR; } ctrl->sent = NO; } break; case NEED_UID: HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_UID\n"); case NEED_UID_NO_REIN: if (ctrl->substate == NEED_UID_NO_REIN) { HTTRACE(PROT_TRACE, "FTP Login... now in state " "NEED_UID_NO_REIN\n"); } if (!ctrl->sent) { status = SendCommand(request, ctrl, "USER", ctrl->uid); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { int code = ctrl->repcode/100; if (code == 2) /* Logged in w/o passwd! */ ctrl->substate = SUB_SUCCESS; else if (code == 3) { /* Password demanded */ ctrl->substate = (ctrl->passwd && *ctrl->passwd) ? NEED_PASSWD : PROMPT_USER; /* begin _GM_ */ /* Note: libwww bug ID: GM3 */ /* } else if (ctrl->repcode == 530) */ /* ctrl->substate = PROMPT_USER;*/ /* User unknown */ } else if (ctrl->repcode == 530) { /* If REIN failed and a subsequent USER also failed, just assume that we're still logged in. */ if (ctrl->substate == NEED_UID_NO_REIN) ctrl->alreadyLoggedIn = YES; if (ctrl->alreadyLoggedIn == YES) { ctrl->substate = SUB_SUCCESS; HTTRACE(PROT_TRACE, "FTP Login... Already logged in\n"); } else { ctrl->substate = PROMPT_USER; HTTRACE(PROT_TRACE, "FTP Login... User Unknown\n"); } } /* end _GM_ */ else ctrl->substate = SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_PASSWD: HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_PASSWD\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "PASS", ctrl->passwd); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { int code = ctrl->repcode/100; if (code == 2) /* Logged in with passwd */ ctrl->substate = SUB_SUCCESS; else if (code == 3) { /* Account required */ HTAlertCallback *cbf = HTAlert_find(HT_A_PROMPT); HTAlertPar * reply = HTAlert_newReply(); if (cbf && (*cbf)(request, HT_A_PROMPT, HT_MSG_ACCOUNT, NULL, NULL, reply)) { ctrl->account = HTAlert_replyMessage(reply); ctrl->substate = NEED_ACCOUNT; } else ctrl->substate = SUB_ERROR; HTAlert_deleteReply(reply); } else if (ctrl->repcode == 530) ctrl->substate = PROMPT_USER; else ctrl->substate = SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_ACCOUNT: HTTRACE(PROT_TRACE, "FTP Login... now in state NEED_ACCOUNT\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "ACCT", ctrl->account); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { int code = ctrl->repcode/100; if (code == 2) /* Logged in with account */ ctrl->substate = SUB_SUCCESS; else ctrl->substate = SUB_ERROR; /* hopeless */ } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case PROMPT_USER: HTTRACE(PROT_TRACE, "FTP Login... now in state PROMPT_USER\n"); { HTAlertCallback *cbf = HTAlert_find(HT_A_USER_PW); HTAlertPar * reply = HTAlert_newReply(); HT_FREE(ctrl->uid); HT_FREE(ctrl->passwd); if (cbf && (*cbf)(request, HT_A_USER_PW, HT_MSG_FTP_UID, NULL, NULL, reply)){ ctrl->uid = HTAlert_replyMessage(reply); ctrl->passwd = HTAlert_replySecret(reply); } else { ctrl->uid = NULL; ctrl->passwd = NULL; } HTAlert_deleteReply(reply); if (ctrl->uid && *ctrl->uid && ctrl->passwd && *ctrl->passwd) ctrl->substate = NEED_UID; else ctrl->substate = SUB_ERROR; } break; case SUB_ERROR: HTTRACE(PROT_TRACE, "FTP Login... now in state SUB_ERROR\n"); HTRequest_addError(request, ERR_FATAL, NO, HTERR_FTP_LOGIN_FAILURE, NULL, 0, "HTFTPLogin"); HTTRACE(PROT_TRACE, "FTP......... Login failed\n"); ctrl->substate = 0; return HT_ERROR; break; case SUB_SUCCESS: HTTRACE(PROT_TRACE, "FTP Login... now in state SUB_SUCCESS\n"); HTTRACE(PROT_TRACE, "FTP......... Logged in as `%s\'\n" _ ctrl->uid); ctrl->substate = 0; return HT_OK; break; } } } /* HTFTPDataConnection ** ------------------- ** Prepares a data connection to the server and initializes the ** transfer mode. ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK */ PRIVATE int HTFTPDataConnection (HTRequest * request, HTNet *cnet, ftp_ctrl *ctrl, ftp_data *data) { int status; HTNet *dnet = ctrl->dnet; typedef enum _state { SUB_ERROR = -2, SUB_SUCCESS = -1, NEED_TYPE = 0, NEED_SELECT, NEED_PASV, NEED_PORT } state; /* Jump into a second level state machine */ while (1) { switch ((state) ctrl->substate) { case NEED_TYPE: HTTRACE(PROT_TRACE, "FTP Data.... now in state NEED_TYPE\n"); if(!data->type|| data->pasv || data->type=='N' || data->type=='L'){ ctrl->substate = NEED_SELECT; break; } if (!ctrl->sent) { char type[2]; *type = data->type; *(type+1) = '\0'; status = SendCommand(request, ctrl, "TYPE", type); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { if (ctrl->repcode/100 == 2) ctrl->substate = NEED_SELECT; else ctrl->substate = SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_SELECT: HTTRACE(PROT_TRACE, "FTP Data.... now in state NEED_SELECT\n"); if (FTPMode & FTP_DATA_PASV && !data->pasv) ctrl->substate = NEED_PASV; else if (AcceptDataSocket(cnet, dnet, data)) ctrl->substate = NEED_PORT; else ctrl->substate = SUB_ERROR; break; case NEED_PASV: HTTRACE(PROT_TRACE, "FTP Data.... now in state NEED_PASV\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "PASV", NULL); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { if (ctrl->repcode == 227) { /* ** If succes, we have to scan for the returned number. ** As the format for the response isn't standard, ** the best thing to do is to scan for the first digit ** after the status code, see RFC1123 */ char *host = ctrl->reply; int h0, h1, h2, h3, p0=0, p1=0; while (*host && !isdigit((int) *host++)); if (!*host || sscanf(--host, "%d,%d,%d,%d,%d,%d", &h0,&h1,&h2,&h3,&p0,&p1) < 6) { HTTRACE(PROT_TRACE, "FTP Data.... PASV No addr\n"); ctrl->substate = SUB_ERROR; break; } else { int port = (p0<<8)+p1; sprintf(data->host, "ftp://%d.%d.%d.%d:%d/", h0, h1, h2, h3, port); data->pasv = YES; ctrl->substate = SUB_SUCCESS; } } else { ctrl->substate = AcceptDataSocket(cnet, dnet, data) ? NEED_PORT : SUB_ERROR; } } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_PORT: HTTRACE(PROT_TRACE, "FTP Data.... now in state NEED_PORT\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "PORT", data->host); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { data->pasv = NO; ctrl->substate = (ctrl->repcode/100 == 2) ? SUB_SUCCESS : SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case SUB_ERROR: HTTRACE(PROT_TRACE, "FTP Data.... now in state SUB_ERROR\n"); HTTRACE(PROT_TRACE, "FTP Data.... Can't setup data connection\n"); ctrl->substate = 0; return HT_ERROR; break; case SUB_SUCCESS: HTTRACE(PROT_TRACE, "FTP Data.... now in state SUB_SUCCESS\n"); HTTRACE(PROT_TRACE, "FTP Data.... Data connection negotiated\n"); ctrl->substate = 0; return HT_OK; break; } } } /* HTFTPServerInfo ** --------------- ** This function finds out what server we are talking to. ** Maybe we can upgrade from NLST to LIST. ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK ** Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for making ** his code available. */ PRIVATE int HTFTPServerInfo (HTRequest *request, HTNet *cnet, ftp_ctrl *ctrl, ftp_data *data) { int status; typedef enum _state { SUB_ERROR = -2, SUB_SUCCESS = -1, NEED_SYST = 0, CHECK_SYST, NEED_PWD, CHECK_PWD } state; /* Jump into a second level state machine */ while (1) { switch ((state) ctrl->substate) { case NEED_SYST: HTTRACE(PROT_TRACE, "FTP Server.. now in state NEED_SYST\n"); if (!ctrl->sent) { if (ctrl->server != FTP_UNSURE) { FTPListType(data, ctrl->server); return HT_OK; } status = SendCommand(request, ctrl, "SYST", NULL); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { ctrl->substate=ctrl->repcode==215 ? CHECK_SYST : NEED_PWD; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case CHECK_SYST: HTTRACE(PROT_TRACE, "FTP Server.. now in state CHECK_SYST\n"); { char *reply = ctrl->reply; if (!*reply) { HTTRACE(PROT_TRACE, "FTP Server.. No server info?\n"); ctrl->substate = NEED_PWD; break; } if (strncmp(reply, "UNIX Type: L8MAC-OSMachTen", 28) == 0) { ctrl->server = FTP_MACHTEN; } else if (strstr(reply, "UNIX") != NULL) { ctrl->server = FTP_UNIX; } else if (strncmp(reply, "VMS", 3) == 0) { ctrl->server = FTP_VMS; } else if ((strncmp(reply, "VM/CMS", 6) == 0) || (strncmp(reply, "VM", 2) == 0)) { ctrl->server = FTP_CMS; } else if (strncmp(reply, "DCTS", 4) == 0) { ctrl->server = FTP_DCTS; } else if (strstr(reply, "MAC-OS TCP/ConnectII") != NULL) { /* Check old versions of TCP/C using / in pathnames */ ctrl->server = FTP_TCPC + FTP_UNSURE; } else if (strncmp(reply, "MACOS Peter's Server", 20) == 0) { ctrl->server = FTP_PETER_LEWIS; } else if (strncmp(reply, "Windows_NT", 10) == 0) { ctrl->server = FTP_WINNT; } /* If we are unsure, try PWD to get more information */ if (ctrl->server & FTP_UNSURE) ctrl->substate = NEED_PWD; else ctrl->substate = SUB_SUCCESS; } break; case NEED_PWD: HTTRACE(PROT_TRACE, "FTP Server.. now in state NEED_PWD\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "PWD", NULL); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { ctrl->substate = (ctrl->repcode/100 == 2) ? CHECK_PWD : SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case CHECK_PWD: HTTRACE(PROT_TRACE, "FTP Server.. now in state CHECK_PWD\n"); { char *start = strchr(ctrl->reply, '"'); char *end; if (!start || (end = strchr(++start, '"')) == NULL) { HTTRACE(PROT_TRACE, "FTP Server.. No current directory?\n"); ctrl->server = FTP_GENERIC; } else { *end = '\0'; if (ctrl->server & FTP_TCPC) { ctrl->server = *start == '/' ? FTP_NCSA : FTP_TCPC; } else if (*start == '/') { /* path names starting with / imply Unix, right? */ ctrl->server = FTP_UNIX; } else if (*(end-1) == ']') { /* path names ending with ] imply VMS, right? */ ctrl->server = FTP_VMS; } else ctrl->server = FTP_GENERIC; } ctrl->substate = SUB_SUCCESS; } break; case SUB_ERROR: HTTRACE(PROT_TRACE, "FTP Server.. now in state SUB_ERROR\n"); HTTRACE(PROT_TRACE, "FTP Server.. Can't get server information\n"); ctrl->substate = 0; ctrl->server = FTP_GENERIC; return HT_ERROR; break; case SUB_SUCCESS: HTTRACE(PROT_TRACE, "FTP Server.. now in state SUB_SUCCESS\n"); { HTHost * host = HTNet_host(cnet); HTTRACE(PROT_TRACE, "FTP Server.. Guessed type %d\n" _ ctrl->server); HTHost_setVersion(host, ctrl->server); FTPListType(data, ctrl->server); ctrl->substate = 0; return HT_OK; break; } } } } /* HTFTPGetData ** ------------ ** This function asks for the file or a directory. First we try in one go, ** but if that doesn't work, then we use CWD for each segment and then ** try to retrieve it. If that also fails, then we try if it is a ** directory. ** Returns HT_OK, HT_LOADED, HT_ERROR, or HT_WOULD_BLOCK */ PRIVATE int HTFTPGetData (HTRequest *request, HTNet *cnet, SOCKET sockfd, ftp_ctrl *ctrl, ftp_data *data) { int status; char *segment = NULL; HTNet *dnet = ctrl->dnet; BOOL data_is_active = (sockfd == HTNet_socket(dnet)); HTPostCallback *pcbf; typedef enum _state { SUB_ERROR = -2, SUB_SUCCESS = -1, NEED_SELECT = 0, NEED_CONNECT, NEED_ACCEPT, NEED_REST, NEED_ACTION, NEED_CWD, NEED_SEGMENT, NEED_STREAM, NEED_BODY } state; /* Jump into a second level state machine */ while (1) { switch ((state) ctrl->substate) { case NEED_SELECT: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_SELECT\n"); ctrl->substate = data->pasv ? NEED_CONNECT : NEED_REST; break; case NEED_CONNECT: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_CONNECT\n"); status = HTHost_connect(HTNet_host(dnet), dnet, data->host); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_OK) { HTTRACE(PROT_TRACE, "FTP Get Data Active data socket %d\n" _ HTNet_socket(dnet)); ctrl->substate = NEED_REST; } else { /* Swap to PORT on the fly */ NETCLOSE(HTNet_socket(dnet)); HTNet_setSocket(dnet, INVSOC); HTTRACE(PROT_TRACE, "FTP Get Data Swap to PORT on the fly\n"); ctrl->substate = NEED_SELECT; HT_FREE(segment); return HT_OK; } break; case NEED_ACCEPT: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_ACCEPT\n"); status = HTHost_accept(HTNet_host(dnet), dnet, NULL); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_OK) { HTTRACE(PROT_TRACE, "FTP Get Data Passive data socket %d\n" _ HTNet_socket(dnet)); ctrl->substate = NEED_STREAM; } else ctrl->substate = SUB_ERROR; break; case NEED_REST: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_REST\n"); if (!ctrl->sent) { /* If the user added a Range header to the request and that header is a single "from offset x to the end", transform that into a REST command. */ HTAssocList* ranges = HTRequest_range(request); HTAssoc* r1, * r2; const char* rangeVal, * s; int len = 0; char* chunkData; HTStream* input; ctrl->substate = NEED_ACTION; /* Do nothing unless exactly one range item present. */ if (ranges == 0) break; r1 = HTAssocList_nextObject(ranges); if (r1 == 0) break; r2 = HTAssocList_nextObject(ranges); if (r2 != 0) break; /* Do nothing unless assoc reads: "bytes" => "12345-" */ if (strcmp(HTAssoc_name(r1), "bytes") != 0) break; rangeVal = HTAssoc_value(r1); s = rangeVal; if (*s < '0' || *s > '9') break; while (*s >= '0' && *s <= '9') len += *s++ - '0'; if (*s != '-' || s[1] != '\0' || len == 0) break; /* send: "REST 12345" */ len = 4 + 1 + (s - rangeVal) + 2; HTChunk_setSize(ctrl->cmd, len + 1); chunkData = HTChunk_data(ctrl->cmd); sprintf(chunkData, "REST %s%c", rangeVal, LF); chunkData[len - 2] = CR; /* Overwrite '-' */ input = HTRequest_inputStream(request); HTTRACE(PROT_TRACE, "FTP Get Data Tx...... %s" _ chunkData); status = (*input->isa->put_block)(input, chunkData, len); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; ctrl->substate = NEED_REST; /* Wait for reply */ } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) { return HT_WOULD_BLOCK; } else if (status == HT_LOADED && ctrl->repcode == 350) { /* Add appropriate range header to response object */ HTResponse* resp = HTRequest_response(request); HTAssocList* ranges = HTRequest_range(request); HTAssoc* r1 = HTAssocList_nextObject(ranges); HTResponse_addRange(resp, "bytes", HTAssoc_value(r1)); ctrl->substate = NEED_ACTION; } else { ctrl->substate = SUB_ERROR; } ctrl->sent = NO; } break; case NEED_ACTION: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_ACTION\n"); if (!ctrl->sent) { char *cmd = (data->type=='L') ? "LIST" : (data->type=='N') ? "NLST" : "RETR"; if (HTRequest_method(request) == METHOD_PUT) cmd = "STOR"; StrAllocCopy(segment, data->offset); HTUnEscape(segment); HTCleanTelnetString(segment); status = SendCommand(request, ctrl, cmd, segment); HT_FREE(segment); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { int code = ctrl->repcode; if (code==125 || code==150 || code==225) ctrl->substate = data->pasv ? NEED_STREAM : NEED_ACCEPT; else if (code/100==5 && !ctrl->cwd) ctrl->substate = NEED_SEGMENT; else { if (ctrl->repcode == 550) { HTTRACE(PROT_TRACE, "FTP Get Data no such file or directory\n"); data->stream_error = YES; } ctrl->substate = SUB_ERROR; } } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_SEGMENT: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_SEGMENT\n"); { char *ptr; if (data->offset == data->file) { if (ctrl->server == FTP_VMS) { /* Change to root */ if ((segment = (char *) HT_MALLOC(strlen(ctrl->uid)+3)) == NULL) HT_OUTOFMEM("segment "); sprintf(segment, "[%s]", ctrl->uid); } else StrAllocCopy(segment, "/"); data->offset++; ctrl->substate = NEED_CWD; } else { if ((ptr = strchr(data->offset, '/'))) { *ptr='\0'; StrAllocCopy(segment, data->offset); *ptr='/'; data->offset = ++ptr; HTUnEscape(segment); HTCleanTelnetString(segment); ctrl->substate = NEED_CWD; } else ctrl->substate = NEED_REST; } } break; case NEED_CWD: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_CWD\n"); if (!ctrl->sent) { status = SendCommand(request, ctrl, "CWD", segment); HT_FREE(segment); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_ERROR) ctrl->substate = SUB_ERROR; ctrl->cwd = YES; ctrl->sent = YES; } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED) { if (ctrl->repcode/100 == 2) ctrl->substate = NEED_SEGMENT; else ctrl->substate = SUB_ERROR; } else ctrl->substate = SUB_ERROR; ctrl->sent = NO; } break; case NEED_STREAM: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_STREAM\n"); /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * target = FTP_DIR(data) ? HTFTPDir_new(request, ctrl->server, data->type) : HTStreamStack(HTAnchor_format(HTRequest_anchor(request)), HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); HTNet_setReadStream(dnet, target); HTRequest_setOutputConnected(request, YES); } data_is_active = YES; ctrl->substate = NEED_BODY; break; case NEED_BODY: HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_BODY\n"); if (data_is_active) { if (HTRequest_method(request) == METHOD_PUT) { HTParentAnchor * entity = HTRequest_entityAnchor(request); const char * document = (const char *) HTAnchor_document(entity); int length = (int)HTAnchor_length(entity); HTStream * output = (HTStream *)HTChannel_output(HTNet_host(dnet)->channel); pcbf = HTRequest_postCallback(request); if (pcbf) { status = (*pcbf)(request, output); } else { status = (*output->isa->put_block)(output, document, length); if (status == HT_OK) { status = HT_LOADED; } } if (status == HT_WOULD_BLOCK) { return HT_WOULD_BLOCK; } else if ( status == HT_LOADED ) { ctrl->substate = SUB_SUCCESS; data->complete |= 3; } else if ( status == HT_OK ) { return HT_WOULD_BLOCK; } else { ctrl->substate = SUB_ERROR; data->stream_error = YES; } continue; } else { status = HTHost_read(HTNet_host(dnet), dnet); } if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED || status == HT_CLOSED || status == HT_OK) { HTDoClose(dnet); data->complete |= 1; if (data->complete >= 3) ctrl->substate = SUB_SUCCESS; else data_is_active = NO; } else { ctrl->substate = SUB_ERROR; data->stream_error = YES; } } else { status = HTHost_read(HTNet_host(cnet), cnet); if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; else if (status == HT_LOADED || status == HT_CLOSED) { if (ctrl->repcode/100 == 2) { data->complete |= 2; if (data->complete >= 3) ctrl->substate = SUB_SUCCESS; else data_is_active = YES; } else ctrl->substate = SUB_ERROR; } else ctrl->substate = SUB_ERROR; } break; case SUB_ERROR: HTTRACE(PROT_TRACE, "FTP Get Data now in state SUB_ERROR\n"); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTFTPGetData"); ctrl->substate = 0; HT_FREE(segment); return HT_ERROR; break; case SUB_SUCCESS: HTTRACE(PROT_TRACE, "FTP Get Data now in state SUB_SUCCESS\n"); ctrl->substate = 0; HT_FREE(segment); return HT_LOADED; break; } } } /* ------------------------------------------------------------------------- */ /* Retrieve File from Server as an atomic action. ** ----------------------------------------------- ** Given a hypertext address, this routine loads a document. ** ** On entry, ** request This is the request structure ** returns HT_ERROR Error has occured in call back ** HT_OK Call back was OK */ PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type); PUBLIC int HTLoadFTP (SOCKET soc, HTRequest * request) { HTNet * cnet = HTRequest_net(request); ftp_ctrl * ctrl = NULL; ftp_data * data = NULL; HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); /* ** Initiate a new FTP ctrl and data structure and bind to request structure ** This is actually state FTP_BEGIN, but it can't be in the state ** machine as we need the structure first. */ HTTRACE(PROT_TRACE, "FTP......... Looking for `%s\'\n" _ url); if ((ctrl = (ftp_ctrl *) HT_CALLOC(1, sizeof(ftp_ctrl))) == NULL || (data = (ftp_data *) HT_CALLOC(1, sizeof(ftp_data))) == NULL) HT_OUTOFMEM("HTLoadFTP"); ctrl->cmd = HTChunk_new(128); ctrl->state = FTP_BEGIN; ctrl->server = FTP_UNSURE; ctrl->dnet = HTNet_dup(cnet); ctrl->cnet = cnet; HTNet_setContext(cnet, ctrl); HTNet_setEventCallback(cnet, FTPEvent); HTNet_setEventParam(cnet, ctrl); HTNet_setRawBytesCount(ctrl->dnet, YES); /* for now, the dnet comes back to the same place ** - vestigial from when the callback was from the request object */ HTNet_setContext(ctrl->dnet, data); HTNet_setEventCallback(ctrl->dnet, FTPEvent); HTNet_setEventParam(ctrl->dnet, ctrl); return FTPEvent(soc, ctrl, HTEvent_BEGIN); } PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type) { ftp_ctrl * ctrl = (ftp_ctrl *) pVoid; ftp_data * data = (ftp_data *) HTNet_context(ctrl->dnet); int status = HT_ERROR; HTNet * cnet = ctrl->cnet; HTRequest * request = HTNet_request(cnet); HTParentAnchor * anchor = HTRequest_anchor(request); char * url = HTAnchor_physical(anchor); HTHost *host = HTNet_host(cnet); if (type == HTEvent_CLOSE) { /* Interrupted */ if (soc == HTNet_socket(cnet) && data->complete<1) FTPCleanup(request, HT_INTERRUPTED); else FTPCleanup(request, HT_LOADED); return HT_OK; } else if (type == HTEvent_TIMEOUT) { /* ** Don't time out the control connection if we are actually recieving data ** on the data connection */ if (!(soc == HTNet_socket(cnet) && !(data->complete & 1) && HTNet_bytesRead(ctrl->dnet)>0)) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_TIME_OUT, NULL, 0, "HTLoadHTTP"); FTPCleanup(request, HT_TIMEOUT); } return HT_OK; } else { ctrl = (ftp_ctrl *) HTNet_context(cnet); /* Get existing copy */ data = (ftp_data *) HTNet_context(ctrl->dnet); } /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (ctrl->state) { case FTP_BEGIN: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_BEGIN\n"); /* Only handle GET requests for now */ if (HTRequest_method(request) != METHOD_GET && HTRequest_method(request) != METHOD_PUT ) { HTTRACE(PROT_TRACE, "FTP Event... This module only supports the GET or PUT methods\n"); ctrl->state = FTP_ERROR; break; } HTFTPParseURL(request, url, ctrl, data); /* The following is added by Neil Griffin, GAIN Software */ /* ** If the user hasn't specified a permanent transfer type, then ** use the transfer type specified at the end of the URL. */ if (g_FTPTransferMode == FTP_DEFAULT_TRANSFER_MODE) { switch (data->type) { case 'a' : data->type = 'A'; break; case 'A' : data->type = 'A'; break; case 'i' : data->type = 'I'; break; case 'I' : data->type = 'I'; break; case 'd' : FTPListType(data, ctrl->server); break; case 'D' : FTPListType(data, ctrl->server); break; default : data->type = 'I'; break; } /* Otherwise, use the permanent transfer type specified by the user. */ } else { switch (g_FTPTransferMode) { case FTP_ASCII_TRANSFER_MODE : data->type = 'A'; break; case FTP_BINARY_TRANSFER_MODE : data->type = 'I'; break; case FTP_DIR_TRANSFER_MODE : FTPListType(data, ctrl->server); break; default : data->type = 'I'; break; } } HTTRACE(PROT_TRACE, "FTP Event... Transfer mode set to '%c'\n" _ data->type); /* ** See if we can get any hints to what we might expect content wise. */ if (!FTP_DIR(data)) HTBind_getAnchorBindings(anchor); /* Ready for next state */ ctrl->state = FTP_NEED_CCON; break; case FTP_NEED_CCON: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_CONN\n"); status = HTHost_connect(host, cnet, url); host = HTNet_host(cnet); if (status == HT_OK) { /* ** Check the protocol class to see if we have connected to a ** the right class of server, in this case HTTP. */ { char * s_class = HTHost_class(host); if (s_class && strcasecomp(s_class, "ftp")) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0, "HTLoadNews"); ctrl->state = FTP_ERROR; break; } HTHost_setClass(host, "ftp"); } /* Check persistent connection */ if (HTNet_persistent(cnet)) { ctrl->server = HTHost_version(host); HTTRACE(PROT_TRACE, "FTP Server.. Cache says type %d server\n" _ ctrl->server); ctrl->reset = 1; } else HTNet_setPersistent(cnet, YES, HT_TP_SINGLE); /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * readstream = FTPStatus_new(request, ctrl, host); HTNet_setReadStream(cnet, readstream); } /* ** Create the stream pipe TO the channel from the application ** and hook it up to the request object */ { HTOutputStream * output = HTNet_getOutput(cnet, NULL, 0); HTRequest_setInputStream(request, (HTStream *) output); } /* ** Set up concurrent read/write if this request isn't the ** source for a PUT or POST. As source we don't start reading ** before all destinations are ready. If destination then ** register the input stream and get ready for read */ if (HTRequest_isPostWeb(request)) { HTEvent * event = HTNet_event(cnet); HTEvent_register(HTNet_socket(cnet), HTEvent_READ, event); HTRequest_linkDestination(request); } ctrl->state = FTP_NEED_LOGIN; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else ctrl->state = FTP_ERROR; /* Error or interrupt */ break; case FTP_NEED_LOGIN: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_LOGIN\n"); status = HTFTPLogin(request, cnet, ctrl); if (status == HT_WOULD_BLOCK) return HT_OK; ctrl->state = (status == HT_OK) ? FTP_NEED_DCON : FTP_ERROR; break; case FTP_NEED_DCON: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_DCON\n"); status = HTFTPDataConnection(request, cnet, ctrl, data); if (status == HT_WOULD_BLOCK) return HT_OK; if (status == HT_OK) ctrl->state = (data->type=='N') ? FTP_NEED_SERVER : FTP_NEED_DATA; else ctrl->state = FTP_ERROR; break; case FTP_NEED_DATA: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_DATA\n"); status = HTFTPGetData(request, cnet, soc, ctrl, data); if (status == HT_WOULD_BLOCK) return HT_OK; if (status == HT_LOADED) ctrl->state = FTP_SUCCESS; else if (status == HT_OK) ctrl->state = FTP_NEED_DCON; else ctrl->state = FTP_ERROR; break; case FTP_NEED_SERVER: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_SERVER\n"); status = HTFTPServerInfo(request, cnet, ctrl, data); if (status == HT_WOULD_BLOCK) return HT_OK; ctrl->state = FTP_NEED_DATA; break; case FTP_SUCCESS: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_SUCCESS\n"); FTPCleanup(request, HT_LOADED); return HT_OK; break; case FTP_ERROR: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_ERROR\n"); FTPCleanup(request, HT_ERROR); return HT_OK; break; } } /* End of while(1) */ } PUBLIC void HTFTP_setTransferMode(FTPTransferMode mode) { g_FTPTransferMode = mode; } PUBLIC FTPTransferMode HTFTP_transferMode (void) { return g_FTPTransferMode; } PUBLIC void HTFTP_setControlMode (FTPControlMode mode) { g_FTPControlMode = mode; } PUBLIC FTPControlMode HTFTP_controlMode (void) { return g_FTPControlMode; } jigdo-0.7.3/src/net/libwww-HTHost.c0000644000175000017500000014473207701377755016667 0ustar richardrichard/* HTHost.c ** REMOTE HOST INFORMATION ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: libwww-HTHost.c,v 1.1.1.1 2003/07/04 22:30:05 atterer Exp $ ** ** This object manages the information that we know about a remote host. ** This can for example be what type of host it is, and what version ** it is using. We also keep track of persistent connections ** ** April 96 HFN Written */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "HTParse.h" #include "HTAlert.h" #include "HTError.h" #include "HTNetMan.h" #include "HTTrans.h" #include "HTTPUtil.h" #include "HTTCP.h" #include "HTHost.h" /* Implemented here */ #include "HTHstMan.h" #define HOST_OBJECT_TTL 43200L /* Default host timeout is 12 h */ #define TCP_IDLE_PASSIVE 120L /* Passive TTL in s on an idle connection */ #define TCP_IDLE_ACTIVE 60000L /* Active TTL in ms on an idle connection */ #define MAX_PIPES 50 /* maximum number of pipelined requests */ #define MAX_HOST_RECOVER 0 /* Max number of auto recovery */ #define DEFAULT_DELAY 30 /* Default write flush delay in ms */ struct _HTInputStream { const HTInputStreamClass * isa; }; PRIVATE int HostEvent(SOCKET soc, void * pVoid, HTEventType type); /* Type definitions and global variables etc. local to this module */ PRIVATE time_t HostTimeout = HOST_OBJECT_TTL; /* Timeout for host objects */ PRIVATE time_t HTPassiveTimeout = TCP_IDLE_PASSIVE; /* Passive timeout in s */ PRIVATE ms_t HTActiveTimeout = TCP_IDLE_ACTIVE; /* Active timeout in ms */ PRIVATE HTList ** HostTable = NULL; PRIVATE HTList * PendHost = NULL; /* List of pending host elements */ /* JK: New functions for interruption the automatic pending request activation */ PRIVATE HTHost_ActivateRequestCallback * ActivateReqCBF = NULL; PRIVATE int HTHost_ActivateRequest (HTNet *net); PRIVATE BOOL DoPendingReqLaunch = YES; /* controls automatic activation of pending requests */ PRIVATE int EventTimeout = -1; /* Global Host event timeout */ PRIVATE ms_t WriteDelay = DEFAULT_DELAY; /* Delay in ms */ PRIVATE int MaxPipelinedRequests = MAX_PIPES; /* ------------------------------------------------------------------------- */ PRIVATE void free_object (HTHost * me) { if (me) { int i; HT_FREE(me->hostname); HT_FREE(me->type); HT_FREE(me->server); HT_FREE(me->user_agent); HT_FREE(me->range_units); /* Delete the channel (if any) */ if (me->channel) { HTChannel_delete(me->channel, HT_OK); me->channel = NULL; } /* Unregister the events */ for (i = 0; i < HTEvent_TYPES; i++) HTEvent_delete(me->events[i]); /* Delete the timer (if any) */ if (me->timer) HTTimer_delete(me->timer); /* Delete the queues */ HTList_delete(me->pipeline); HTList_delete(me->pending); HT_FREE(me); } } PRIVATE BOOL delete_object (HTList * list, HTHost * me) { HTTRACE(CORE_TRACE, "Host info... object %p from list %p\n" _ me _ list); HTList_removeObject(list, (void *) me); free_object(me); return YES; } PRIVATE BOOL isLastInPipe (HTHost * host, HTNet * net) { return HTList_lastObject(host->pipeline) == net; } PRIVATE BOOL killPipeline (HTHost * host, HTEventType type) { if (host) { int piped = HTList_count(host->pipeline); int pending = HTList_count(host->pending); int cnt; HTTRACE(CORE_TRACE, "Host kill... Pipeline due to %s event\n" _ HTEvent_type2str(type)); /* Terminate all net objects in pending queue */ for (cnt=0; cntpending); if (net) { HTTRACE(CORE_TRACE, "Host kill... Terminating net object %p from pending queue\n" _ net); net->registeredFor = 0; (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, type); if (host->lock == net) host->lock = NULL; } } /* Terminate all net objects in pipeline */ if (piped >= 1) { /* ** Terminte all net objects in the pipeline */ for (cnt=0; cntpipeline); if (net) { HTTRACE(CORE_TRACE, "Host kill... Terminating net object %p from pipe line\n" _ net); net->registeredFor = 0; (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, type); } } /* Close the connection, to prevent the server from sending us the remainder of the data for the currently active request. */ HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, HT_INTERRUPTED); } return YES; } return NO; } /* ** Silently close an idle persistent connection after ** HTActiveTimeout secs */ PRIVATE int IdleTimeoutEvent (HTTimer * timer, void * param, HTEventType type) { HTHost * host = (HTHost *) param; SOCKET sockfd = HTChannel_socket(host->channel); HTTimer_delete(timer); host->timer = NULL; return HostEvent (sockfd, host, HTEvent_CLOSE); } /* ** HostEvent - host event manager - recieves events from the event ** manager and dispatches them to the client net objects by calling the ** net object's cbf. ** */ PRIVATE int HostEvent (SOCKET soc, void * pVoid, HTEventType type) { HTHost * host = (HTHost *)pVoid; if (type == HTEvent_READ || type == HTEvent_CLOSE || type == HTEvent_ACCEPT) { HTNet * targetNet; /* call the first net object */ do { int ret; /* netscape and apache servers can do a lazy close well after usage * of previous socket has been dispensed by the library, * the section below makes sure the event does not get miss attributed */ if (HTChannel_socket(host->channel) != soc && type != HTEvent_ACCEPT && !host->listening) { HTTRACE(CORE_TRACE, "Host Event.. wild socket %d type = %s real socket is %d\n" _ soc _ type == HTEvent_CLOSE ? "Event_Close" : "Event_Read" _ HTChannel_socket(host->channel)); return HT_OK; } targetNet = (HTNet *)HTList_firstObject(host->pipeline); if (targetNet) { HTTRACE(CORE_TRACE, "Host Event.. READ passed to `%s\'\n" _ HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet)))); if ((ret = (*targetNet->event.cbf)(HTChannel_socket(host->channel), targetNet->event.param, type)) != HT_OK) return ret; } if (targetNet == NULL && host->remainingRead > 0) { HTTRACE(CORE_TRACE, "HostEvent... Error: %d bytes left to read and nowhere to put them\n" _ host->remainingRead); host->remainingRead = 0; /* ** Fall through to close the channel */ } /* call pipelined net object to eat all the data in the channel */ } while (host->remainingRead > 0); /* last target net should have set remainingRead to 0 */ if (targetNet) return HT_OK; /* If there was notargetNet, it should be a close */ HTTRACE(CORE_TRACE, "Host Event.. host %p `%s\' closed connection.\n" _ host _ host->hostname); /* Is there garbage in the channel? Let's check: */ { char buf[256]; int ret; memset(buf, '\0', sizeof(buf)); if (HTChannel_socket(host->channel) != INVSOC) { while ((ret = NETREAD(HTChannel_socket(host->channel), buf, sizeof(buf)-1)) > 0) { HTTRACE(CORE_TRACE, "Host Event.. Host %p `%s\' had %d extraneous bytes: `%s\'\n" _ host _ host->hostname _ ret _ 0/*buf*/); memset(buf, '\0', sizeof(buf)); } } } HTHost_clearChannel(host, HT_OK); return HT_OK; /* extra garbage does not constitute an application error */ } else if (type == HTEvent_WRITE || type == HTEvent_CONNECT) { HTNet * targetNet = (HTNet *)HTList_lastObject(host->pipeline); if (targetNet) { HTTRACE(CORE_TRACE, "Host Event.. WRITE passed to `%s\'\n" _ HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet)))); return (*targetNet->event.cbf)(HTChannel_socket(host->channel), targetNet->event.param, type); } HTTRACE(CORE_TRACE, "Host Event Host %p (`%s\') dispatched with event %s but doesn't have a target - %d requests made, %d requests in pipe, %d pending\n" _ host _ host ? host->hostname : "" _ HTEvent_type2str(type) _ host ? host->reqsMade : -1 _ HTList_count(host->pipeline) _ HTList_count(host->pending)); #if 0 HTDEBUGBREAK("Host Event.. Host %p (`%s\') dispatched with event %d\n" _ host _ host ? host->hostname : "" _ type); return HT_ERROR; #else return HT_OK; #endif } else if (type == HTEvent_TIMEOUT) { killPipeline(host, HTEvent_TIMEOUT); } else { HTTRACE(CORE_TRACE, "Don't know how to handle OOB data from `%s\'?\n" _ host->hostname); } return HT_OK; } /* ** Search the host info cache for a host object or create a new one ** and add it. Examples of host names are ** ** www.w3.org ** www.foo.com:8000 ** 18.52.0.18 ** ** Returns Host object or NULL if error. You may get back an already ** existing host object - you're not guaranteed a new one each time. */ PUBLIC HTHost * HTHost_new (char * host, u_short u_port) { HTList * list = NULL; /* Current list in cache */ HTHost * pres = NULL; int hash = 0; if (!host) { HTTRACE(CORE_TRACE, "Host info... Bad argument\n"); return NULL; } /* Find a hash for this host */ { char *ptr; for (ptr=host; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HOST_HASH_SIZE); if (!HostTable) { if ((HostTable = (HTList **) HT_CALLOC(HOST_HASH_SIZE, sizeof(HTList *))) == NULL) HT_OUTOFMEM("HTHost_find"); } if (!HostTable[hash]) HostTable[hash] = HTList_new(); list = HostTable[hash]; } /* Search the cache */ { HTList * cur = list; while ((pres = (HTHost *) HTList_nextObject(cur))) { if (!strcmp(pres->hostname, host) && u_port == pres->u_port) { if (HTHost_isIdle(pres) && time(NULL)>pres->ntime+HostTimeout){ HTTRACE(CORE_TRACE, "Host info... Collecting host info %p\n" _ pres); delete_object(list, pres); pres = NULL; } break; } } } /* If not found then create new Host object, else use existing one */ if (pres) { if (pres->channel) { /* ** If we have a TTL for this TCP connection then ** check that we haven't passed it. */ if (pres->expires > 0) { time_t t = time(NULL); if (HTHost_isIdle(pres) && pres->expires < t) { HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _ pres->channel); HTHost_clearChannel(pres, HT_OK); } else { pres->expires = t + HTPassiveTimeout; HTTRACE(CORE_TRACE, "Host info... REUSING CHANNEL %p\n" _ pres->channel); } } } else { HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres); } } else { if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL) HT_OUTOFMEM("HTHost_add"); pres->hash = hash; StrAllocCopy(pres->hostname, host); pres->u_port = u_port; pres->ntime = time(NULL); pres->mode = HT_TP_SINGLE; pres->delay = WriteDelay; pres->inFlush = NO; { int i; for (i = 0; i < HTEvent_TYPES; i++) pres->events[i]= HTEvent_new(HostEvent, pres, HT_PRIORITY_MAX, EventTimeout); } HTTRACE(CORE_TRACE, "Host info... added `%s\' with host %p to list %p\n" _ host _ pres _ list); HTList_addObject(list, (void *) pres); } return pres; } PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port) { char * port; char * fullhost = NULL; char * parsedHost = NULL; SockA * sin; HTHost * me; char * proxy = HTRequest_proxy(request); fullhost = HTParse(proxy ? proxy : url, "", PARSE_HOST); /* If there's an @ then use the stuff after it as a hostname */ if (fullhost) { char * at_sign; if ((at_sign = strchr(fullhost, '@')) != NULL) parsedHost = at_sign+1; else parsedHost = fullhost; } if (!parsedHost || !*parsedHost) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_HOST, NULL, 0, "HTHost_newWParse"); HT_FREE(fullhost); return NULL; } /* See if the default port should be overridden */ if ((port = strchr(parsedHost, ':')) != NULL) { *port++ = '\0'; if (*port && isdigit((int) *port)) u_port = (u_short) atol(port); } HTTRACE(PROT_TRACE, "HTHost parse Looking up `%s\' on port %u\n" _ parsedHost _ u_port); /* Find information about this host */ if ((me = HTHost_new(parsedHost, u_port)) == NULL) { HTTRACE(PROT_TRACE, "HTHost parse Can't get host info\n"); me->tcpstate = TCP_ERROR; return NULL; } sin = &me->sock_addr; memset((void *) sin, '\0', sizeof(SockA)); #ifdef DECNET sin->sdn_family = AF_DECnet; net->sock_addr.sdn_objnum = port ? (unsigned char)(strtol(port, (char **) 0, 10)) : DNP_OBJ; #else /* Internet */ sin->sin_family = AF_INET; sin->sin_port = htons(u_port); #endif HT_FREE(fullhost); /* parsedHost points into fullhost */ return me; } /* ** Search the host info cache for a host object. Examples of host names: ** ** www.w3.org ** www.foo.com:8000 ** 18.52.0.18 ** ** Returns Host object or NULL if not found. */ PUBLIC HTHost * HTHost_find (char * host) { HTList * list = NULL; /* Current list in cache */ HTHost * pres = NULL; HTTRACE(CORE_TRACE, "Host info... Looking for `%s\'\n" _ host ? host : ""); /* Find a hash for this host */ if (host && HostTable) { int hash = 0; char *ptr; for (ptr=host; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HOST_HASH_SIZE); if (!HostTable[hash]) return NULL; list = HostTable[hash]; /* Search the cache */ { HTList * cur = list; while ((pres = (HTHost *) HTList_nextObject(cur))) { if (!strcmp(pres->hostname, host)) { if (time(NULL) > pres->ntime + HostTimeout) { HTTRACE(CORE_TRACE, "Host info... Collecting host %p\n" _ pres); delete_object(list, pres); pres = NULL; } else { HTTRACE(CORE_TRACE, "Host info... Found `%s\'\n" _ host); } return pres; } } } } return NULL; } /* ** Cleanup the host list */ PUBLIC void HTHost_deleteAll (void) { HTList * list; HTHost * host; int i; if (!HostTable) return; for (i=0; i < HOST_HASH_SIZE; i++) { list = HostTable[i]; if (!list) continue; while ((host = (HTHost *) HTList_removeFirstObject(list)) != NULL) free_object(host); HTList_delete(list); } HT_FREE(HostTable); HostTable = NULL; } /* ** Get and set the hostname of the remote host */ PUBLIC char * HTHost_name (HTHost * host) { return host ? host->hostname : NULL; } /* ** Get and set the type class of the remote host */ PUBLIC char * HTHost_class (HTHost * host) { return host ? host->type : NULL; } PUBLIC void HTHost_setClass (HTHost * host, char * s_class) { if (host && s_class) StrAllocCopy(host->type, s_class); } /* ** Get and set the version of the remote host */ PUBLIC int HTHost_version (HTHost *host) { return host ? host->version : 0; } PUBLIC void HTHost_setVersion (HTHost * host, int version) { if (host) host->version = version; } /* ** Get and set the passive timeout for persistent entries. */ PUBLIC BOOL HTHost_setPersistTimeout (time_t timeout) { if (timeout > 0) { HTPassiveTimeout = timeout; return YES; } return NO; } PUBLIC time_t HTHost_persistTimeout (void) { return HTPassiveTimeout; } /* ** Get and set the active timeout for persistent entries. */ PUBLIC BOOL HTHost_setActiveTimeout (ms_t timeout) { if (timeout > 1000) { HTActiveTimeout = timeout; return YES; } return NO; } PUBLIC ms_t HTHost_activeTimeout (void) { return HTActiveTimeout; } /* Persistent Connection Expiration ** -------------------------------- ** Should normally not be used. If, then use calendar time. */ PUBLIC void HTHost_setPersistExpires (HTHost * host, time_t expires) { if (host) host->expires = expires; } PUBLIC time_t HTHost_persistExpires (HTHost * host) { return host ? host->expires : -1; } PUBLIC void HTHost_setReqsPerConnection (HTHost * host, int reqs) { if (host) host->reqsPerConnection = reqs; } PUBLIC int HTHost_reqsPerConnection (HTHost * host) { return host ? host->reqsPerConnection : -1; } PUBLIC void HTHost_setReqsMade (HTHost * host, int reqs) { if (host) host->reqsMade = reqs; } PUBLIC int HTHost_reqsMade (HTHost * host) { return host ? host->reqsMade : -1; } /* ** Public methods for this host */ PUBLIC HTMethod HTHost_publicMethods (HTHost * me) { return me ? me->methods : METHOD_INVALID; } PUBLIC void HTHost_setPublicMethods (HTHost * me, HTMethod methodset) { if (me) me->methods = methodset; } PUBLIC void HTHost_appendPublicMethods (HTHost * me, HTMethod methodset) { if (me) me->methods |= methodset; } /* ** Get and set the server name of the remote host */ PUBLIC char * HTHost_server (HTHost * host) { return host ? host->server : NULL; } PUBLIC BOOL HTHost_setServer (HTHost * host, const char * server) { if (host && server) { StrAllocCopy(host->server, server); return YES; } return NO; } /* ** Get and set the userAgent name of the remote host */ PUBLIC char * HTHost_userAgent (HTHost * host) { return host ? host->user_agent : NULL; } PUBLIC BOOL HTHost_setUserAgent (HTHost * host, const char * userAgent) { if (host && userAgent) { StrAllocCopy(host->user_agent, userAgent); return YES; } return NO; } /* ** Get and set acceptable range units */ PUBLIC char * HTHost_rangeUnits (HTHost * host) { return host ? host->range_units : NULL; } PUBLIC BOOL HTHost_setRangeUnits (HTHost * host, const char * units) { if (host && units) { StrAllocCopy(host->range_units, units); return YES; } return NO; } /* ** Checks whether a specific range unit is OK. We always say ** YES except if we have a specific statement from the server that ** it doesn't understand byte ranges - that is - it has sent "none" ** in a "Accept-Range" response header */ PUBLIC BOOL HTHost_isRangeUnitAcceptable (HTHost * host, const char * unit) { if (host && unit) { #if 0 if (host->range_units) { char * start = HTStrCaseStr(host->range_units, "none"); /* ** Check that "none" is infact a token. It could be part of some ** other valid string, so we'd better check for it. */ if (start) { } return NO; } #endif return strcasecomp(unit, "bytes") ? NO : YES; } return NO; } /* ** As soon as we know that this host accepts persistent connections, ** we associated the channel with the host. ** We don't want more than MaxSockets-2 connections to be persistent in ** order to avoid deadlock. */ PUBLIC BOOL HTHost_setPersistent (HTHost * host, BOOL persistent, HTTransportMode mode) { if (!host) return NO; if (!persistent) { /* ** We use the HT_IGNORE status code as we don't want to free ** the stream at this point in time. The situation we want to ** avoid is that we free the channel from within the stream pipe. ** This will lead to an infinite look having the stream freing ** itself. */ host->persistent = NO; return HTHost_clearChannel(host, HT_IGNORE); } /* ** Set the host persistent if not already. Also update the mode to ** the new one - it may have changed */ HTHost_setMode(host, mode); if (!host->persistent) { SOCKET sockfd = HTChannel_socket(host->channel); if (sockfd != INVSOC && HTNet_availablePersistentSockets() > 0) { host->persistent = YES; host->expires = time(NULL) + HTPassiveTimeout; /* Default timeout */ HTChannel_setHost(host->channel, host); HTNet_increasePersistentSocket(); HTTRACE(CORE_TRACE, "Host info... added host %p as persistent\n" _ host); return YES; } else { HTTRACE(CORE_TRACE, "Host info... no room for persistent socket %d\n" _ sockfd); return NO; } } else { HTTRACE(CORE_TRACE, "Host info... %p already persistent\n" _ host); return YES; } return NO; } /* ** Check whether we have a persistent channel or not */ PUBLIC BOOL HTHost_isPersistent (HTHost * host) { return host && host->persistent; } /* ** Find persistent channel associated with this host. */ PUBLIC HTChannel * HTHost_channel (HTHost * host) { return host ? host->channel : NULL; } /* ** Check whether we have got a "close" notification, for example in the ** connection header */ PUBLIC BOOL HTHost_setCloseNotification (HTHost * host, BOOL mode) { if (host) { host->close_notification = mode; return YES; } return NO; } PUBLIC BOOL HTHost_closeNotification (HTHost * host) { return host && host->close_notification; } /* ** Clear the persistent entry by deleting the channel object. Note that ** the channel object is only deleted if it's not used anymore. */ PUBLIC BOOL HTHost_clearChannel (HTHost * host, int status) { if (host && host->channel) { HTChannel_setHost(host->channel, NULL); HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ); HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE); host->registeredFor = 0; /* ** We don't want to recursively delete ourselves so if we are ** called from within the stream pipe then don't delete the channel ** at this point */ HTChannel_delete(host->channel, status); host->expires = 0; host->channel = NULL; host->tcpstate = TCP_BEGIN; host->reqsMade = 0; if (HTHost_isPersistent(host)) { HTNet_decreasePersistentSocket(); host->persistent = NO; } host->close_notification = NO; host->broken_pipe = NO; host->mode = HT_TP_SINGLE; host->recovered = 0; HTTRACE(CORE_TRACE, "Host info... removed host %p as persistent\n" _ host); if (!HTList_isEmpty(host->pending)) { HTTRACE(CORE_TRACE, "Host has %d object(s) pending - attempting launch\n" _ HTList_count(host->pending)); HTHost_launchPending(host); } return YES; } return NO; } PUBLIC BOOL HTHost_doRecover (HTHost * host) { return host ? host->do_recover : NO; } /* ** Move all entries in the pipeline and move the rest to the pending ** queue. They will get launched at a later point in time. */ PUBLIC BOOL HTHost_recoverPipe (HTHost * host) { if (host) { int piped = HTList_count(host->pipeline); /* ** First check that we haven't already recovered more than we want */ if (host->recovered >= MAX_HOST_RECOVER) { HTTRACE(CORE_TRACE, "Host recover %p already %d times - not doing it anymore\n" _ host _ host->recovered); HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, HT_INTERRUPTED); return NO; } /* ** If we decided to recover and actually have something in the pipe ** then go ahead. */ if (piped > 0) { int cnt; host->recovered++; HTTRACE(CORE_TRACE, "Host recover %p recovered %d times. Moving %d Net objects from pipe line to pending queue\n" _ host _ host->recovered _ piped); /* ** Unregister this host for all events */ HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ); HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE); host->registeredFor = 0; /* ** Set new mode to single until we know what is going on */ host->mode = HT_TP_SINGLE; /* ** Move all net objects from the net object to the pending queue. */ if (!host->pending) host->pending = HTList_new(); for (cnt=0; cntpipeline); HTTRACE(CORE_TRACE, "Host recover Resetting net object %p\n" _ net); net->registeredFor = 0; (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, HTEvent_RESET); HTList_appendObject(host->pending, net); host->lock = net; } HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, HT_INTERRUPTED); host->do_recover = NO; } return YES; } return NO; } /* ** Terminate a pipeline prematurely, for example because of timeout, ** interruption, etc. */ PUBLIC BOOL HTHost_killPipe (HTHost * host) { return killPipeline(host, HTEvent_CLOSE); } /* ** Handle the connection mode. The mode may change mode in the ** middle of a connection. */ PUBLIC HTTransportMode HTHost_mode (HTHost * host, BOOL * active) { return host ? host->mode : HT_TP_SINGLE; } /* ** If the new mode is lower than the old mode then adjust the pipeline ** accordingly. That is, if we are going into single mode then move ** all entries in the pipeline and move the rest to the pending ** queue. They will get launched at a later point in time. */ PUBLIC BOOL HTHost_setMode (HTHost * host, HTTransportMode mode) { if (host) { /* ** Check the new mode and see if we must adjust the queues. */ if (mode == HT_TP_SINGLE && host->mode > mode) { int piped = HTList_count(host->pipeline); if (piped > 0) { int cnt; HTTRACE(CORE_TRACE, "Host info... Moving %d Net objects from pipe line to pending queue\n" _ piped); if (!host->pending) host->pending = HTList_new(); for (cnt=0; cntpipeline); HTTRACE(CORE_TRACE, "Host info... Resetting net object %p\n" _ net); (*net->event.cbf)(HTChannel_socket(host->channel), net->event.param, HTEvent_RESET); HTList_appendObject(host->pending, net); } HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, HT_INTERRUPTED); } } /* ** If we know that this host is bad then we don't allow anything than ** single mode. We can't recover connections for the rest of our life */ if (mode == HT_TP_PIPELINE && host->recovered > MAX_HOST_RECOVER) { HTTRACE(PROT_TRACE, "Host info... %p is bad for pipelining so we won't do it!!!\n" _ host); } else { host->mode = mode; HTTRACE(PROT_TRACE, "Host info... New mode is %d for host %p\n" _ host->mode _ host); } } return NO; } /* ** Check whether a host is idle meaning if it is ready for a new ** request which depends on the mode of the host. If the host is ** idle, i.e. ready for use then return YES else NO. If the host supports ** persistent connections then still only return idle if no requests are ** ongoing. */ PUBLIC BOOL HTHost_isIdle (HTHost * host) { return (host && HTList_isEmpty(host->pipeline)); } PRIVATE BOOL _roomInPipe (HTHost * host) { int count; if (!host || (host->reqsPerConnection && host->reqsMade >= host->reqsPerConnection) || HTHost_closeNotification(host) || host->broken_pipe) return NO; count = HTList_count(host->pipeline); switch (host->mode) { case HT_TP_SINGLE: return count <= 0; case HT_TP_PIPELINE: return (host->recovered < MAX_HOST_RECOVER || MAX_HOST_RECOVER==0) ? (count < MaxPipelinedRequests) : (count <= 0); case HT_TP_INTERLEAVE: return YES; } return NO; } /* ** Add a net object to the host object. If the host ** is idle then add to active list (pipeline) else add ** it to the pending list ** Return HT_PENDING if we must pend, HT_OK, or HT_ERROR */ PUBLIC int HTHost_addNet (HTHost * host, HTNet * net) { if (host && net) { int status = HT_OK; BOOL doit = (host->doit==net); /* ** If we don't have a socket already then check to see if we can get ** one. Otherwise we put the host object into our pending queue. */ if (!host->channel && HTNet_availableSockets() <= 0) { /* Create list for pending Host objects */ if (!PendHost) PendHost = HTList_new(); /* Add the host object ad pending if not already */ if (HTList_indexOf(PendHost, host) < 0) HTList_addObject(PendHost, host); /* ** Add the Net object to the Host object. If it is the current Net ** obejct holding the lock then add it to the beginning of the list. ** Otherwise add it to the end */ if (!host->pending) host->pending = HTList_new(); if (host->lock == net) HTList_appendObject(host->pending, net); else HTList_addObject(host->pending, net); HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) as pending on pending Host %p, %d requests made, %d requests in pipe, %d pending\n" _ net _ net->request _ host _ host->reqsMade _ HTList_count(host->pipeline) _ HTList_count(host->pending)); return HT_PENDING; } #if 0 /* ** First check whether the net object is already on either queue. ** Do NOT add extra copies of the HTNet object to ** the pipeline or pending list (if it's already on the list). */ if (HTList_indexOf(host->pipeline, net) >= 0) { HTTRACE(CORE_TRACE, "Host info... The Net %p (request %p) is already in pipe," " %d requests made, %d requests in pipe, %d pending\n" _ net _ net->request _ host->reqsMade _ HTList_count(host->pipeline) _ HTList_count(host->pending)); HTDEBUGBREAK("Net object %p registered multiple times in pipeline\n" _ net); return HT_OK; } if (HTList_indexOf(host->pending, net) >= 0) { HTTRACE(CORE_TRACE, "Host info... The Net %p (request %p) already pending," " %d requests made, %d requests in pipe, %d pending\n" _ net _ net->request _ host->reqsMade _ HTList_count(host->pipeline) _ HTList_count(host->pending)); HTDEBUGBREAK("Net object %p registered multiple times in pending queue\n" _ net); return HT_PENDING; } #endif /* ** Add net object to either active or pending queue. */ if (_roomInPipe(host) && (HTList_isEmpty(host->pending) || doit)) { if (doit) host->doit = NULL; if (!host->pipeline) host->pipeline = HTList_new(); HTList_addObject(host->pipeline, net); host->reqsMade++; HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) to pipe on Host %p, %d requests made, %d requests in pipe, %d pending\n" _ net _ net->request _ host _ host->reqsMade _ HTList_count(host->pipeline) _ HTList_count(host->pending)); /* ** If we have been idle then make sure we delete the timer */ if (host->timer) { HTTimer_delete(host->timer); host->timer = NULL; } /*JK: New CBF function ** Call any user-defined callback to say the request will ** be processed. */ HTHost_ActivateRequest (net); } else { if (!host->pending) host->pending = HTList_new(); HTList_addObject(host->pending, net); HTTRACE(CORE_TRACE, "Host info... Added Net %p (request %p) as pending on Host %p, %d requests made, %d requests in pipe, %d pending\n" _ net _ net->request _ host _ host->reqsMade _ HTList_count(host->pipeline) _ HTList_count(host->pending)); status = HT_PENDING; } return status; } return HT_ERROR; } PRIVATE BOOL HTHost_free (HTHost * host, int status) { if (host->channel) { /* Check if we should keep the socket open */ if (HTHost_isPersistent(host)) { int piped = HTList_count(host->pipeline); if (HTHost_closeNotification(host)) { HTTRACE(CORE_TRACE, "Host Object. got close notifiation on socket %d\n" _ HTChannel_socket(host->channel)); /* ** If more than a single element (this one) in the pipe ** then we have to recover gracefully */ if (piped > 1) { host->reqsPerConnection = host->reqsMade - piped; HTTRACE(CORE_TRACE, "%d requests made, %d in pipe, max %d requests pr connection\n" _ host->reqsMade _ piped _ host->reqsPerConnection); host->do_recover = YES; /* @@ JK: not clear yet if I need or not to set the channel to NULL. I think not */ /* HTChannel_delete(host->channel, status); */ if (HTChannel_delete(host->channel, status)) { HTTRACE(CORE_TRACE, "Host Event.. clearing channel on host %p (%s)\n" _ host _ host->hostname); host->channel = NULL; } } else { HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, status); } } else if (piped<=1 && host->reqsMade==host->reqsPerConnection) { HTTRACE(CORE_TRACE, "Host Object. closing persistent socket %d\n" _ HTChannel_socket(host->channel)); /* ** By lowering the semaphore we make sure that the channel ** is gonna be deleted */ HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, status); } else { HTTRACE(CORE_TRACE, "Host Object. keeping persistent socket %d\n" _ HTChannel_socket(host->channel)); if (HTChannel_delete(host->channel, status)) { HTDEBUGBREAK("Host Event.. Channel unexpected deleted from host %p (%s)\n" _ host _ host->hostname); host->channel = NULL; } /* ** If connection is idle then set a timer so that we close the ** connection if idle too long */ if (piped<=1 && HTList_isEmpty(host->pending) && !host->timer) { host->timer = HTTimer_new(NULL, IdleTimeoutEvent, host, HTActiveTimeout, YES, NO); HTTRACE(PROT_TRACE, "Host........ Object %p going idle...\n" _ host); } } return YES; } else { HTTRACE(CORE_TRACE, "Host Object. closing socket %d\n" _ HTChannel_socket(host->channel)); HTChannel_setSemaphore(host->channel, 0); HTHost_clearChannel(host, status); } } return NO; } PUBLIC BOOL HTHost_deleteNet (HTHost * host, HTNet * net, int status) { if (host && net) { HTTRACE(CORE_TRACE, "Host info... Remove %p from pipe\n" _ net); /* If the Net object is in the pipeline then also update the channel */ if (host->pipeline && HTList_indexOf(host->pipeline, net) >= 0) { HTHost_free(host, status); HTList_removeObjectAll(host->pipeline, net); } HTList_removeObjectAll(host->pending, net); /* just to make sure */ host->lock = HTList_firstObject(host->pending); return YES; } return NO; } /* ** Handle pending host objects. ** There are two ways we can end up with pending reqyests: ** 1) If we are out of sockets then register new host objects as pending. ** 2) If we are pending on a connection then register new net objects as ** pending ** This set of functions handles pending host objects and can start new ** requests as resources get available */ /* ** Check this host object for any pending requests and return the next ** registered Net object. */ PUBLIC HTNet * HTHost_nextPendingNet (HTHost * host) { HTNet * net = NULL; if (host && host->pending) { /*JK 23/Sep/96 Bug correction. Associated the following lines to the **above if. There was a missing pair of brackets. */ if ((net = (HTNet *) HTList_removeFirstObject(host->pending)) != NULL) { HTTRACE(CORE_TRACE, "Host info... Popping %p from pending net queue on host %p\n" _ net _ host); #if 0 { HTRequest * request = HTNet_request(net); char * uri = HTAnchor_address((HTAnchor *) HTRequest_anchor(request)); fprintf(stderr, "Popping '%s'\n", uri); } #endif host->doit = net; } } return net; } /* ** Return the current list of pending host objects waiting for a socket */ PUBLIC HTHost * HTHost_nextPendingHost (void) { HTHost * host = NULL; if (PendHost) { if ((host = (HTHost *) HTList_removeFirstObject(PendHost)) != NULL) HTTRACE(PROT_TRACE, "Host info... Popping %p from pending host queue\n" _ host); } return host; } /* ** Start the next pending request if any. First we look for pending ** requests for the same host and then we check for any other pending ** hosts */ PUBLIC BOOL HTHost_launchPending (HTHost * host) { HTNet * net = NULL; if (!host) { HTTRACE(PROT_TRACE, "Host info... Bad arguments\n"); return NO; } /* ** In pipeline we can only have one doing writing at a time. ** We therefore check that there are no other Net object ** registered for write */ if (host->mode == HT_TP_PIPELINE) { net = (HTNet *) HTList_lastObject(host->pipeline); if (net && net->registeredFor == HTEvent_WRITE) return NO; } /* ** Check the current Host object for pending Net objects */ if (_roomInPipe(host) && DoPendingReqLaunch && (net = HTHost_nextPendingNet(host))) { HTHost_ActivateRequest(net); HTTRACE(CORE_TRACE, "Launch pending net object %p with %d reqs in pipe (%d reqs made)\n" _ net _ HTList_count(host->pipeline) _ host->reqsMade); return HTNet_execute(net, HTEvent_WRITE); } /* ** Check for other pending Host objects */ if (DoPendingReqLaunch && HTNet_availableSockets() > 0) { HTHost * pending = HTHost_nextPendingHost(); if (pending && (net = HTHost_nextPendingNet(pending))) { if (!pending->pipeline) pending->pipeline = HTList_new(); HTList_addObject(pending->pipeline, net); host->reqsMade++; HTTRACE(CORE_TRACE, "Launch pending host object %p, net %p with %d reqs in pipe (%d reqs made)\n" _ pending _ net _ HTList_count(pending->pipeline) _ pending->reqsMade); HTHost_ActivateRequest(net); return HTNet_execute(net, HTEvent_WRITE); } } return YES; } PUBLIC HTNet * HTHost_firstNet (HTHost * host) { return (HTNet *) HTList_firstObject(host->pipeline); } PUBLIC int HTHost_numberOfOutstandingNetObjects (HTHost * host) { return host ? HTList_count(host->pipeline) : -1; } PUBLIC int HTHost_numberOfPendingNetObjects (HTHost * host) { return host ? HTList_count(host->pending) : -1; } /* ** The host event manager keeps track of the state of it's client engines ** (typically HTTPEvent), accepting multiple blocks on read or write from ** multiple pipelined engines. It then registers its own engine ** (HostEvent) with the event manager. */ PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url) { HTRequest * request = HTNet_request(net); int status = HT_OK; if (!host) { HTProtocol * protocol = HTNet_protocol(net); if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL) return HT_ERROR; /* ** If not already locked and without a channel ** then lock the darn thing with the first Net object ** pending. */ if (!host->lock && !host->channel) { HTNet * next_pending = NULL; host->forceWriteFlush = YES; host->lock = (next_pending = HTList_firstObject(host->pending)) ? next_pending : net; HTTRACE(CORE_TRACE, "Host connect Grabbing lock on Host %p with %p\n" _ host _ host->lock); } HTNet_setHost(net, host); } if (!host->lock || (host->lock && host->lock == net)) { status = HTDoConnect(net); if (status == HT_PENDING) return HT_WOULD_BLOCK; else if (status == HT_WOULD_BLOCK) { host->lock = net; return status; } else { /* ** See if there is already a new pending request that should ** take over the current lock */ HTNet * next_pending = NULL; if ((next_pending = HTList_firstObject(host->pending))) { HTTRACE(CORE_TRACE, "Host connect Changing lock on Host %p to %p\n" _ host _ next_pending); host->lock = next_pending; } else { HTTRACE(CORE_TRACE, "Host connect Unlocking Host %p\n" _ host); host->lock = NULL; } return status; } } else { HTTRACE(CORE_TRACE, "Host connect Host %p already locked with %p\n" _ host _ host->lock); if ((status = HTHost_addNet(host, net)) == HT_PENDING) { return HT_PENDING; } } return HT_ERROR; /* @@@ - some more deletion and stuff here? */ } PUBLIC int HTHost_listen (HTHost * host, HTNet * net, char * url) { HTRequest * request = HTNet_request(net); int status = HT_OK; if (!host) { HTProtocol * protocol = HTNet_protocol(net); if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL) return HT_ERROR; /* ** If not already locked and without a channel ** then lock the darn thing with the first Net object ** pending. */ if (!host->lock && !host->channel) { host->forceWriteFlush = YES; host->lock = net; } HTNet_setHost(net, host); } /* ** See if we already have a dedicated listen Net object. If not ** then create one. */ if (!host->listening) host->listening = HTNet_new(host); /* ** Start listening on the Net object */ status = HTDoListen(host->listening, net, HT_BACKLOG); if (status != HT_OK) { HTTRACE(CORE_TRACE, "Host listen. On Host %p resulted in %d\n" _ host _ status); return status; } return HT_OK; } PUBLIC int HTHost_accept (HTHost * host, HTNet * net, char * url) { int status = HT_OK; if (!host || !host->listening) { HTTRACE(CORE_TRACE, "Host accept. No host object or not listening on anything\n"); return HT_ERROR; } if (!host->lock || (host->lock && host->lock == net)) { status = HTDoAccept(host->listening, net); if (status == HT_PENDING) return HT_WOULD_BLOCK; else if (status == HT_WOULD_BLOCK) { host->lock = net; return status; } else { /* ** See if there is already a new pending request that should ** take over the current lock */ HTNet * next_pending = NULL; if ((next_pending = HTList_firstObject(host->pending))) { HTTRACE(CORE_TRACE, "Host connect Changing lock on Host %p to %p\n" _ host _ next_pending); host->lock = next_pending; } else { HTTRACE(CORE_TRACE, "Host connect Unlocking Host %p\n" _ host); host->lock = NULL; } return status; } } else { HTTRACE(CORE_TRACE, "Host connect Host %p already locked with %p\n" _ host _ host->lock); if ((status = HTHost_addNet(host, net)) == HT_PENDING) { return HT_PENDING; } } return HT_ERROR; } /* ** Rules: SINGLE: one element in pipe, either reading or writing ** PIPE: n element in pipe, n-1 reading, 1 writing */ PUBLIC int HTHost_register (HTHost * host, HTNet * net, HTEventType type) { HTEvent *event; if (host && net) { if (type == HTEvent_CLOSE) { /* ** Unregister this host for all events */ HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_READ); HTEvent_unregister(HTChannel_socket(host->channel), HTEvent_WRITE); host->registeredFor = 0; return YES; } else { /* net object may already be registered */ if (HTEvent_BITS(type) & net->registeredFor) return NO; net->registeredFor ^= HTEvent_BITS(type); /* host object may already be registered */ if (host->registeredFor & HTEvent_BITS(type)) return YES; host->registeredFor ^= HTEvent_BITS(type); #ifdef WWW_WIN_ASYNC /* Make sure we are registered for CLOSE on windows */ event = *(host->events+HTEvent_INDEX(HTEvent_CLOSE)); HTEvent_register(HTChannel_socket(host->channel), HTEvent_CLOSE, event); #endif /* WWW_WIN_ASYNC */ /* JK: register a request in the event structure */ event = *(host->events+HTEvent_INDEX(type)); event->request = HTNet_request (net); return HTEvent_register(HTChannel_socket(host->channel), type, event); } return YES; } HTTRACE(CORE_TRACE, "HTHost...... Don't register event with bad arguments\n"); return NO; } PUBLIC int HTHost_unregister (HTHost * host, HTNet * net, HTEventType type) { if (host && net) { /* net object may not be registered */ if (!(HTEvent_BITS(type) & net->registeredFor)) return NO; net->registeredFor ^= HTEvent_BITS(type); /* host object may not be registered */ if (!(host->registeredFor & HTEvent_BITS(type))) return YES; host->registeredFor ^= HTEvent_BITS(type); /* stay registered for READ to catch a socket close */ /* WRITE and CONNECT can be unregistered, though */ if ((type == HTEvent_WRITE && isLastInPipe(host, net)) || type == HTEvent_CONNECT) /* if we are blocked downstream, shut down the whole pipe */ HTEvent_unregister(HTChannel_socket(host->channel), type); return YES; } return NO; } /* ** The reader tells HostEvent that it's stream did not finish the data */ PUBLIC BOOL HTHost_setRemainingRead (HTHost * host, size_t remaining) { if (host == NULL) return NO; host->remainingRead = remaining; HTTRACE(PROT_TRACE, "Host........ %d bytes remaining \n" _ remaining); if (host->broken_pipe && remaining == 0) { HTTRACE(PROT_TRACE, "Host........ Emtied out connection\n"); } return YES; } PUBLIC size_t HTHost_remainingRead (HTHost * host) { return host ? host->remainingRead : (size_t)-1; } PUBLIC SockA * HTHost_getSockAddr (HTHost * host) { if (!host) return NULL; return &host->sock_addr; } PUBLIC BOOL HTHost_setHome (HTHost * host, int home) { if (!host) return NO; host->home = home; return YES; } PUBLIC int HTHost_home (HTHost * host) { if (!host) return 0; return host->home; } PUBLIC BOOL HTHost_setRetry (HTHost * host, int retry) { if (!host) return NO; host->retry = retry; return YES; } PUBLIC BOOL HTHost_decreaseRetry (HTHost * host) { if (!host) return NO; if (host->retry > 0) host->retry--; return YES; } PUBLIC int HTHost_retry (HTHost * host) { if (!host) return 0; return host->retry; } #if 0 /* Is a macro right now */ PRIVATE BOOL HTHost_setDNS5 (HTHost * host, HTdns * dns) { if (!host) return NO; host->dns = dns; return YES; } #endif PUBLIC BOOL HTHost_setChannel (HTHost * host, HTChannel * channel) { if (!host) return NO; host->channel = channel; return YES; } PUBLIC HTNet * HTHost_getReadNet(HTHost * host) { return host ? (HTNet *) HTList_firstObject(host->pipeline) : NULL; } PUBLIC HTNet * HTHost_getWriteNet(HTHost * host) { return host ? (HTNet *) HTList_lastObject(host->pipeline) : NULL; } /* ** Create the input stream and bind it to the channel ** Please read the description in the HTIOStream module for the parameters */ PUBLIC HTInputStream * HTHost_getInput (HTHost * host, HTTransport * tp, void * param, int mode) { if (host && host->channel && tp) { HTChannel * ch = host->channel; HTInputStream * input = (*tp->input_new)(host, ch, param, mode); HTChannel_setInput(ch, input); return HTChannel_getChannelIStream(ch); } HTTRACE(CORE_TRACE, "Host Object. Can't create input stream\n"); return NULL; } PUBLIC HTOutputStream * HTHost_getOutput (HTHost * host, HTTransport * tp, void * param, int mode) { if (host && host->channel && tp) { HTChannel * ch = host->channel; HTOutputStream * output = (*tp->output_new)(host, ch, param, mode); HTChannel_setOutput(ch, output); return output; } HTTRACE(CORE_TRACE, "Host Object. Can't create output stream\n"); return NULL; } PUBLIC HTOutputStream * HTHost_output (HTHost * host, HTNet * net) { if (host && host->channel && net) { HTOutputStream * output = HTChannel_output(host->channel); return output; } return NULL; } PUBLIC int HTHost_read(HTHost * host, HTNet * net) { HTInputStream * input = HTChannel_input(host->channel); if (net != HTHost_getReadNet(host)) { HTHost_register(host, net, HTEvent_READ); return HT_WOULD_BLOCK; } /* ** If there is no input channel then this can either mean that ** we have lost the channel or an error occurred. We return ** HT_CLOSED as this is a sign to the caller that we don't ** have a channel */ return input ? (*input->isa->read)(input) : HT_CLOSED; } PUBLIC BOOL HTHost_setConsumed(HTHost * host, size_t bytes) { HTInputStream * input; if (!host || !host->channel) return NO; if ((input = HTChannel_input(host->channel)) == NULL) return NO; HTTRACE(CORE_TRACE, "Host........ passing %d bytes as consumed to %p\n" _ bytes _ input); return (*input->isa->consumed)(input, bytes); } PUBLIC int HTHost_hash (HTHost * host) { return host ? host->hash : -1; } PUBLIC BOOL HTHost_setWriteDelay (HTHost * host, ms_t delay) { if (host /*&& delay >= 0*/) { host->delay = delay; return YES; } return NO; } PUBLIC ms_t HTHost_writeDelay (HTHost * host) { return host ? host->delay : 0; } PUBLIC int HTHost_findWriteDelay (HTHost * host, ms_t lastFlushTime, int buffSize) { #if 0 unsigned short mtu; int ret = -1; int socket = HTChannel_socket(host->channel); #ifndef WWW_MSWINDOWS ret = ioctl(socket, 666, (unsigned long)&mtu); #endif /* WWW_MSWINDOWS */ if ((ret == 0 && buffSize >= mtu) || host->forceWriteFlush) return 0; return host->delay; #else return host->forceWriteFlush ? 0 : host->delay; #endif } PUBLIC BOOL HTHost_setDefaultWriteDelay (ms_t delay) { /* if (delay >= 0) {*/ WriteDelay = delay; HTTRACE(CORE_TRACE, "Host........ Default write delay is %d ms\n" _ delay); return YES; /* } return NO;*/ } PUBLIC ms_t HTHost_defaultWriteDelay (void) { return WriteDelay; } PUBLIC int HTHost_forceFlush(HTHost * host) { HTNet * targetNet = (HTNet *) HTList_lastObject(host->pipeline); int ret; if (targetNet == NULL) return HT_ERROR; /* 2000/28/07 JK: The following test was proposed by Heiner Kallweit, as there's a problem ** while using SSL because of a recursive call to this function. We tested the ** fix and it doesn't seem to introduce any side effects... but one never knows, ** thus this comment. This seems more like a bug in the SSL code than here. */ if (host->inFlush) { HTTRACE(CORE_TRACE, "Host Event.. FLUSH requested for `%s\'\n, but ignoring it as we're already processing a flush in this host" _ HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet)))); return HT_OK; } HTTRACE(CORE_TRACE, "Host Event.. FLUSH passed to `%s\'\n" _ HTAnchor_physical(HTRequest_anchor(HTNet_request(targetNet)))); host->forceWriteFlush = YES; host->inFlush = YES; ret = (*targetNet->event.cbf)(HTChannel_socket(host->channel), targetNet->event.param, HTEvent_FLUSH); host->forceWriteFlush = NO; host->inFlush = NO; return ret; } /* ** Context pointer to be used as a user defined context */ PUBLIC void HTHost_setContext (HTHost * me, void * context) { if (me) me->context = context; } PUBLIC void * HTHost_context (HTHost * me) { return me ? me->context : NULL; } PUBLIC int HTHost_eventTimeout (void) { return EventTimeout; } PUBLIC void HTHost_setEventTimeout (int millis) { EventTimeout = millis; HTTRACE(CORE_TRACE, "Host........ Setting event timeout to %d ms\n" _ millis); } PUBLIC BOOL HTHost_setMaxPipelinedRequests (int max) { if (max > 1) { MaxPipelinedRequests = max; return YES; } return NO; } PUBLIC int HTHost_maxPipelinedRequests (void) { return MaxPipelinedRequests; } PUBLIC void HTHost_setActivateRequestCallback (HTHost_ActivateRequestCallback * cbf) { HTTRACE(CORE_TRACE, "HTHost...... Registering %p\n" _ cbf); ActivateReqCBF = cbf; } PRIVATE int HTHost_ActivateRequest (HTNet * net) { HTRequest * request = NULL; if (!ActivateReqCBF) { HTTRACE(CORE_TRACE, "HTHost...... No ActivateRequest callback handler registered\n"); return HT_ERROR; } request = HTNet_request(net); return (*ActivateReqCBF)(request); } PUBLIC void HTHost_disable_PendingReqLaunch (void) { DoPendingReqLaunch = NO; } PUBLIC void HTHost_enable_PendingReqLaunch (void) { DoPendingReqLaunch = YES; } jigdo-0.7.3/src/net/libwww.hh0000644000175000017500000000171407730450721015650 0ustar richardrichard/* $Id: libwww.hh,v 1.8 2003/09/12 23:08:01 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Include libwww headers, clean up namespace afterwards. */ #ifndef LIBWWW_HH #define LIBWWW_HH #include #undef PACKAGE extern "C" { #include #include #include #include #include #include #include // HTReqMan.h incorrectly defines the name as HTRequest_deleteRangeAll extern BOOL HTRequest_deleteRange (HTRequest * request); } #include #undef PACKAGE #define PACKAGE "jigdo" #undef _ #if ENABLE_NLS # define _(String) dgettext (PACKAGE, String) # else # define _(String) (String) #endif #endif jigdo-0.7.3/src/net/proxyguess-test.cc0000644000175000017500000002466210120704646017532 0ustar richardrichard/* $Id: proxyguess-test.cc,v 1.5 2004/09/11 23:26:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Redirects proxyguess's file accesses to in-memory stringstreams #test-deps */ #include #include #include #include #include #include #include #include //______________________________________________________________________ #if WINDOWS int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); Assert(false); // Test unimplemented return 0; } //______________________________________________________________________ #else namespace { time_t start = time(0); struct FakeFile { FakeFile(time_t s, const string& c) : content(c), stamp(s) { } string content; time_t stamp; }; // Mapping path -> file obj typedef map Map; Map files; string proxySettings; void testcaseStart() { proxySettings.clear(); files.clear(); } inline void addFile(const char* path, FakeFile* f) { files.insert(files.end(), make_pair(path, f)); } inline void addFile(const char* path, auto_ptr& f) { files.insert(files.end(), make_pair(path, f.get())); } // This is used in place of normal ifstream struct MyIfstream : istringstream { MyIfstream(const char* path) : istringstream(files.find(path) != files.end() ? files[path]->content : string()) { } }; // Return the last modification date of the file in question, 0 on error inline time_t fileModTime(const char* path) { Map::const_iterator i = files.find(path); if (i == files.end()) return 0; else return i->second->stamp; } } void glibcurl_add_proxy(const gchar *protocol, const gchar *proxy) { if (!proxySettings.empty()) proxySettings += ','; proxySettings += protocol; proxySettings += '='; proxySettings += proxy; } void glibcurl_add_noproxy(const gchar *host) { if (!proxySettings.empty()) proxySettings += ','; proxySettings += "noproxy="; proxySettings += host; } //______________________________________________________________________ #define TESTING_PROXYGUESS #include //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); string home = g_get_home_dir(); if (home[home.size() - 1] != DIRSEP) home += DIRSEP; // Set up a couple of simulated files auto_ptr lynxNone(new FakeFile(start - 1, "# lynx.cfg file.\n" "\n" ".h1 Auxiliary Facilities\n" ".ex\n" "#INCLUDE:~/lynx.cfg:COLOR VIEWER KEYMAP\n" "STARTFILE:file://localhost/usr/share/doc/lynx/lynx_help/lynx_help_main.html\n" "#\n" "HELPFILE:file://localhost/usr/share/doc/lynx/lynx_help/lynx_help_main.html\n" ".ex\n" ".nf\n" )); auto_ptr lynxAll(new FakeFile(start - 10, "# preserve lowercasing, and will outlive the Lynx image.\n" "#\n" ".ex 15\n" "http_proxy:http://1.com:port/\n" "https_proxy:http://2.com:port/\n" "ftp_proxy:http://3.com:port/\n" "gopher_proxy:http://4.com:port/\n" "news_proxy:http://5.com:port/\n" "newspost_proxy:http://6.com:port/\n" "newsreply_proxy:http://7.com:port/\n" "snews_proxy:http://8.com:port/\n" "snewspost_proxy:http://9.com:port/\n" "snewsreply_proxy:http://10.com:port/\n" "nntp_proxy:http://11.com:port/\n" "wais_proxy:http://12.com:port/\n" "finger_proxy:http://13.com:port/\n" "cso_proxy:http://14.com:port/\n" "no_proxy:host.15.com,foo,bar #baz\n" "\n" "\n" ".h2 NO_PROXY\n" "# The no_proxy variable can be a comma-separated list of strings defining\n" "# no-proxy zones in the DNS domain name space. If a tail substring of the\n" "# domain-path for a host matches one of these strings, transactions with that\n" "# node will not be proxied.\n" ".ex\n" "no_proxy:domain.path1,path2\n" "#\n" "# A single asterisk as an entry will override all proxy variables and no\n" "# transactions will be proxied.\n" ".ex\n" "no_proxy:*\n" // Is ignored completely (incorrectly) "# This is the only allowed use of * in no_proxy.\n" )); auto_ptr wget(new FakeFile(start - 9, "# You can set up other headers, like Accept-Language. Accept-Language\n" "# is *not* sent by default.\n" "header = Accept-Language: en\n" "\n" "# You can set the default proxies for Wget to use for http and ftp.\n" "# They will override the value in the environment.\n" "http_proxy = http://proxy.yoyodyne.com:18022/\n" "ftp_proxy=http://proxy.yoyodyne.com:18023/\n" "\n" "# If you do not want to use proxy at all, set this to off.\n" "use_proxy = off\n" // Incorrectly ignored "\n" "# You can customize the retrieval outlook. Valid options are default,\n" "# binary, mega and micro.\n" "#dot_style = default\n" )); auto_ptr netscape4(new FakeFile(start - 10, "// Netscape User Preferences\n" "// This is a generated file! Do not edit.\n" "\n" "user_pref(\"mail.signature_file\", \"/home/richard/.signature\");\n" "user_pref(\"mail.thread.win_height\", 0);\n" "user_pref(\"mail.thread.win_width\", 0);\n" "user_pref(\"mail.use_movemail\", false);\n" "user_pref(\"network.cookie.cookieBehavior\", 1);\n" "user_pref(\"network.cookie.warnAboutCookies\", true);\n" "user_pref(\"network.hosts.socks_serverport\", 0);\n" "user_pref(\"network.proxy.ftp\", \"localhost\");\n" "user_pref(\"network.proxy.ftp_port\", 8080);\n" "user_pref(\"network.proxy.http\", \"localhost\");\n" "user_pref(\"network.proxy.http_port\", 5865);\n" "user_pref(\"network.proxy.no_proxies_on\", \"lan localhost\");\n" "user_pref(\"network.proxy.type\", 1);\n" "user_pref(\"news.default_fcc\", \"/home/richard/nsmail/Sent\");\n" )); auto_ptr kde(new FakeFile(start - 10, "[Proxy Settings]\n" "NoProxyFor=lan localhost\n" "Proxy Config Script=\n" // Not supported "ProxyType=1\n" "ftpProxy=http://localhost1:8080\n" "httpProxy=http://localhost2:5865\n" "httpsProxy=http://localhost3:8080\n" )); auto_ptr galeon(new FakeFile(start - 10, "# Mozilla User Preferences\n" "// This is a generated file!\n" "user_pref(\"network.http.proxy.keep-alive\", false);\n" "user_pref(\"network.proxy.ftp\", \"x\");\n" "user_pref(\"network.proxy.ftp_port\", 8080);\n" "user_pref(\"network.proxy.http\", \"x\");\n" "user_pref(\"network.proxy.http_port\", 5865);\n" "user_pref(\"network.proxy.no_proxies_on\", \"lan x 127.0.0.1 nenya nenya.lan\");\n" "user_pref(\"network.proxy.socks_version\", 4);\n" "user_pref(\"network.proxy.ssl_port\", 8080);\n" "user_pref(\"network.proxy.type\", 1);\n" "user_pref(\"plugin.soname.list\", \"libXt.so:libXext.so\");\n" "user_pref(\"security.checkloaduri\", false);\n" "user_pref(\"security.warn_submit_insecure\", false);\n" )); auto_ptr mozillaEmpty(new FakeFile(start - 5, "// TryeType\n" "pref(\"font.FreeType2.enable\", true);\n" "pref(\"font.freetype2.shared-library\", \"libfreetype.so.6\");\n" "pref(\"font.FreeType2.autohinted\", false);\n" "pref(\"font.FreeType2.unhinted\", false);\n" "pref(\"font.antialias.min\", 10);\n" "pref(\"font.directory.truetype.1\", \"/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType\");\n" "pref(\"font.directory.truetype.2\", \"/usr/share/fonts/truetype\");\n" "pref(\"font.directory.truetype.3\", \"/usr/share/fonts/truetype/openoffice\");\n" )); //____________________ // Scan single files testcaseStart(); addFile("/etc/lynx.cfg", lynxNone); proxyGuess(); Assert(proxySettings == ""); testcaseStart(); string lynxRc = home + ".lynxrc"; addFile(lynxRc.c_str(), lynxAll); proxyGuess(); Assert(proxySettings == "http=http://1.com:port/,ftp=http://3.com:port/," "noproxy=host.15.com,noproxy=foo,noproxy=bar,noproxy=domain.path1," "noproxy=path2"); testcaseStart(); addFile("/etc/wgetrc", wget); proxyGuess(); Assert(proxySettings == "http=http://proxy.yoyodyne.com:18022/," "ftp=http://proxy.yoyodyne.com:18023/"); testcaseStart(); addFile("/etc/netscape4/defaults/preferences.js", netscape4); proxyGuess(); Assert(proxySettings == "http=http://localhost:5865," "ftp=http://localhost:8080,noproxy=lan,noproxy=localhost"); testcaseStart(); string kdeRc = home + ".kde/share/config/kioslaverc"; addFile(kdeRc.c_str(), kde); proxyGuess(); Assert(proxySettings == "noproxy=lan,noproxy=localhost," "ftp=http://localhost1:8080,http=http://localhost2:5865"); testcaseStart(); string galeonRc = home + ".galeon/mozilla/galeon/prefs.js"; addFile(galeonRc.c_str(), galeon); proxyGuess(); Assert(proxySettings == "http=http://x:5865,ftp=http://x:8080,noproxy=lan," "noproxy=x,noproxy=127.0.0.1,noproxy=nenya,noproxy=nenya.lan"); testcaseStart(); string mozRc = home + ".mozilla/default/prefs.js"; addFile(mozRc.c_str(), mozillaEmpty); proxyGuess(); Assert(proxySettings == ""); //____________________ // Scan several files, use most recent one which is not empty // general: scan several // proxyguess: Scan: 1 /etc/lynx.cfg // proxyguess: Scan: 5 /home/richard/.mozilla/default/prefs.js // proxyguess: Scan: 9 /etc/wgetrc // proxyguess: http proxy: http://proxy.yoyodyne.com:18022/ // proxyguess: ftp proxy: http://proxy.yoyodyne.com:18023/ // proxyguess: Ignr: 10 /home/richard/.lynxrc // general: "http=http://proxy.yoyodyne.com:18022/,ftp=http://proxy.yoyodyne.com:18023/" msg("scan several"); testcaseStart(); addFile("/etc/lynx.cfg", lynxNone); addFile(lynxRc.c_str(), lynxAll); addFile("/etc/wgetrc", wget); addFile("/etc/netscape4/defaults/preferences.js", netscape4); addFile(kdeRc.c_str(), kde); addFile(galeonRc.c_str(), galeon); addFile(mozRc.c_str(), mozillaEmpty); proxyGuess(); msg("\"%1\"", proxySettings); Assert(proxySettings == "http=http://proxy.yoyodyne.com:18022/," "ftp=http://proxy.yoyodyne.com:18023/"); return 0; } #endif /* WINDOWS */ jigdo-0.7.3/src/net/proxyguess.cc0000644000175000017500000003603210261546437016557 0ustar richardrichard/* $Id: proxyguess.cc,v 1.14 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Find proxy URLs by reading config files of various browsers. Warning, this code probably tries to be too clever: It compares the "last modified" timestamps of the various config files and chooses the configuration with the most recent timestamp. */ // This is what libcurl says about env vars (url.c:2326): /* If proxy was not specified, we check for default proxy environment * variables, to enable i.e Lynx compliance: * * http_proxy=http://some.server.dom:port/ * https_proxy=http://some.server.dom:port/ * ftp_proxy=http://some.server.dom:port/ * gopher_proxy=http://some.server.dom:port/ * no_proxy=domain1.dom,host.domain2.dom * (a comma-separated list of hosts which should * not be proxied, or an asterisk to override * all proxy variables) * all_proxy=http://some.server.dom:port/ * (seems to exist for the CERN www lib. Probably * the first to check for.) * * For compatibility, the all-uppercase versions of these variables are * checked if the lowercase versions don't exist. */ #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("proxyguess") #ifndef TESTING_PROXYGUESS #warning TODO glibcurl_add_proxy void glibcurl_add_proxy(const char*, const char*) { } void glibcurl_add_noproxy(const char*) { } #endif #if WINDOWS #include /* Windows: Read Internet Explorer's proxy settings. Doesn't work with .pac files, just with user-supplied servers. */ namespace { void proxyGuess_MSIE(HKEY internetSettings) { const unsigned BUFLEN = 256; DWORD len = BUFLEN; byte buf[BUFLEN]; DWORD type; if (RegQueryValueEx(internetSettings, "ProxyEnable", NULL, &type, buf, &len) == ERROR_SUCCESS && type == REG_BINARY && buf[0] == 0) { // User deselected the option "Use a proxy server", so don't continue debug("No proxies set up"); return; } len = BUFLEN; // List of servers not to use the proxy for if (RegQueryValueEx(internetSettings, "ProxyOverride", NULL, &type, buf, &len) == ERROR_SUCCESS && type == REG_SZ && buf[0] != 0) { // String has the form "lan;". Split at ; and ignore string host; const char* list = reinterpret_cast(buf); while (*list != '\0') { if (!(isalnum(*list) || *list == '.' || *list == '-')) { ++list; continue; } while (isalnum(*list) || *list == '.' || *list == '-') { host += *list; ++list; } if (host != "local") { debug("No proxy for %1", host); glibcurl_add_noproxy(host.c_str()); } host.erase(); } } len = BUFLEN; if (RegQueryValueEx(internetSettings, "ProxyServer", NULL, &type, buf, &len) == ERROR_SUCCESS && type == REG_SZ && len >= 2 && buf[0] != 0) { /* String has one of two formats. Either simple format, one proxy for all: "ox:8080", or per-protocol format: "ftp=ox:8081;gopher=ox:8080;http=ox:8080;https=ox:8080" */ string entry; const char* list = reinterpret_cast(buf); while (true) { while (*list != '\0' && *list != ';') { entry += *list; ++list; } string::size_type equals = entry.find('='); if (equals == string::npos) { // Simple proxy setting, assume it's both for HTTP and FTP string proxy = "http://"; proxy.append(reinterpret_cast(buf)); debug("General proxy: %1", proxy); glibcurl_add_proxy("http", proxy.c_str()); glibcurl_add_proxy("ftp", proxy.c_str()); } else { // Per-protocol proxy settings string proto(entry, 0, equals); if (proto == "http" || proto == "ftp") { string proxy = "http://"; proxy.append(entry, equals + 1, string::npos); debug("%1 proxy: %2", proto, proxy); glibcurl_add_proxy(proto.c_str(), proxy.c_str()); } } entry.erase(); if (*list == '\0') break; ++list; } // endwhile (true) } // endif (RegQueryValueEx("ProxyServer") == ERROR_SUCCESS) } } void proxyGuess() { HKEY software; if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ, &software) == ERROR_SUCCESS) { HKEY microsoft; if (RegOpenKeyEx(software, "Microsoft", 0, KEY_READ, µsoft) == ERROR_SUCCESS) { HKEY windows; if (RegOpenKeyEx(microsoft, "Windows", 0, KEY_READ, &windows) == ERROR_SUCCESS) { HKEY currentVersion; if (RegOpenKeyEx(windows, "CurrentVersion", 0, KEY_READ, ¤tVersion) == ERROR_SUCCESS) { HKEY internetSettings; if (RegOpenKeyEx(currentVersion, "Internet Settings", 0, KEY_READ, &internetSettings) == ERROR_SUCCESS) { proxyGuess_MSIE(internetSettings); RegCloseKey(internetSettings); } RegCloseKey(currentVersion); } RegCloseKey(windows); } RegCloseKey(microsoft); } RegCloseKey(software); } } //______________________________________________________________________ #else // Proxy guess code for Unix namespace { /** Local struct: Info about a browser configuration file */ struct BrowserConfig { /* Init filename and timestamp */ BrowserConfig(const string& name, time_t timestamp); void init(time_t timestamp); virtual ~BrowserConfig(); /* Reads config file, returns true if proxies could be set. */ virtual bool read() = 0; /* In order for things to work with time_t wraparound, record one recent timestamp "now" and always work with the "age" of files in seconds. */ static time_t now; string filename; signed age; // Allow future dates, e.g. happens with NFS + clock skew }; BrowserConfig::BrowserConfig(const string& name, time_t timestamp) { if (now == 0) time(&now); filename = name; age = now - timestamp; } BrowserConfig::~BrowserConfig() { } time_t BrowserConfig::now = 0; // Compare pointers to BrowserConfigs by comparing the objects' age struct ConfFilesCompare { bool operator()(const BrowserConfig* a, const BrowserConfig* b) { return a->age < b->age; } }; } //______________________________________________________________________ namespace { # ifndef TESTING_PROXYGUESS // Return the last modification date of the file in question, 0 on error inline time_t fileModTime(const char* path) { struct stat fileInfo; if (stat(path, &fileInfo) != 0) return 0; return fileInfo.st_mtime; } typedef ifstream MyIfstream; # endif // Add file info to set of candidate config files template void add(set* c, const char* name) { if (name == 0) return; time_t timestamp = fileModTime(name); if (timestamp == 0) return; c->insert(new Browser(string(name), timestamp)); } template void add(set* c, const string& name) { time_t timestamp = fileModTime(name.c_str()); if (timestamp == 0) return; c->insert(new Browser(name, timestamp)); } // E.g. protocol == "http", proxy == "http://localhost:8080/" // NB proxy must start with a scheme like "http:" inline bool addProxy(const char* protocol, const char* proxy) { while (*proxy == ' ' || *proxy == '\t' || *proxy == '=') ++proxy; if (*proxy == '#') return false; // Assuming comment, not setting proxy if (*proxy == '\0') return false; debug("%1 proxy: %2", protocol, proxy); glibcurl_add_proxy(protocol, proxy); return true; } void addNoProxy(const char* list) { string host; while (*list != '\0') { if (*list == '#') return; // Assuming start of comment if (!(isalnum(*list) || *list == '.' || *list == '-')) { ++list; continue; } while (isalnum(*list) || *list == '.' || *list == '-') { host += *list; ++list; } debug("No proxy for %1", host); glibcurl_add_noproxy(host.c_str()); host.erase(); } } } // namespace //______________________________________________________________________ namespace { struct Lynx : BrowserConfig { Lynx(const string& n, time_t t) : BrowserConfig(n, t) { } bool read(); }; bool Lynx::read() { bool result = false; static const string whitespace = " \t"; MyIfstream s(filename.c_str()); string line; while (s) { getline(s, line); string::size_type pos = line.find_first_not_of(whitespace); if (pos == string::npos) continue; const char* l = line.c_str(); if (strncmp(l + pos, "http_proxy:", 11) == 0) { if (addProxy("http", l + 11)) result = true; } else if (strncmp(l + pos, "ftp_proxy:", 10) == 0) { if (addProxy("ftp", l + 10)) result = true; } else if (strncmp(l + pos, "no_proxy:", 9) == 0) { addNoProxy(l + 9); } } return result; } } //______________________________________________________________________ namespace { struct Wget : BrowserConfig { Wget(const string& n, time_t t) : BrowserConfig(n, t) { } bool read(); }; bool Wget::read() { // Maybe this should react on "use_proxy = off", but it doesn't bool result = false; static const string whitespace = " \t"; MyIfstream s(filename.c_str()); string line; while (s) { getline(s, line); string::size_type pos = line.find_first_not_of(whitespace); if (pos == string::npos) continue; const char* l = line.c_str(); if (strncmp(l + pos, "http_proxy", 10) == 0) { if (addProxy("http", l + 10)) result = true; } else if (strncmp(l + pos, "ftp_proxy", 9) == 0) { if (addProxy("ftp", l + 9)) result = true; } else if (strncmp(l + pos, "no_proxy", 8) == 0) { addNoProxy(l + 8); } } return result; } } //______________________________________________________________________ namespace { struct Mozilla : BrowserConfig { Mozilla(const string& n, time_t t) : BrowserConfig(n, t) { } bool read(); void setString(string* dest, const char* src); }; void Mozilla::setString(string* dest, const char* src) { dest->erase(); while (*src == '"' || *src == ' ' || *src == '\t' || *src == ',') ++src; while (*src != '\0' && *src != ')' && *src != '"') { *dest += *src; ++src; } } // Works for Netscape 4.7, Galeon, Mozilla 5/6 bool Mozilla::read() { // Doesn't understand .pac proxy definitions MyIfstream s(filename.c_str()); string line, ftphost, ftpport, httphost, httpport, noproxy; while (s) { getline(s, line); const char USERPREF[] = "user_pref(\"network.proxy."; const char PREF[] = "pref(\"network.proxy."; const char* l = line.c_str(); if (strncmp(l, USERPREF, sizeof(USERPREF)-1) == 0) l += sizeof(USERPREF)-1; else if (strncmp(l, PREF, sizeof(PREF)-1) == 0) l += sizeof(PREF)-1; else continue; if (strncmp(l, "ftp\"", 4) == 0) setString(&ftphost, l + 4); else if (strncmp(l, "ftp_port\"", 9) == 0) setString(&ftpport, l + 9); else if (strncmp(l, "http\"", 5) == 0) setString(&httphost, l + 5); else if (strncmp(l, "http_port\"", 10) == 0) setString(&httpport, l + 10); else if (strncmp(l, "no_proxies_on\"", 14) == 0) setString(&noproxy, l + 14); } bool result = false; if (!httphost.empty() && !httpport.empty()) { httphost.insert(0, "http://"); httphost += ':'; httphost += httpport; if (addProxy("http", httphost.c_str())) result = true; } if (!ftphost.empty() && !ftpport.empty()) { ftphost.insert(0, "http://"); ftphost += ':'; ftphost += ftpport; if (addProxy("ftp", ftphost.c_str())) result = true; } if (!noproxy.empty()) addNoProxy(noproxy.c_str()); return result; } } //______________________________________________________________________ namespace { struct KDE : BrowserConfig { KDE(const string& n, time_t t) : BrowserConfig(n, t) { } bool read(); }; // Reads kioslaverc bool KDE::read() { // Maybe this should react on "use_proxy = off", but it doesn't bool result = false; static const string whitespace = " \t"; MyIfstream s(filename.c_str()); string line; while (s) { getline(s, line); string::size_type pos = line.find_first_not_of(whitespace); if (pos == string::npos) continue; const char* l = line.c_str(); if (strncmp(l + pos, "httpProxy", 9) == 0) { if (addProxy("http", l + 9)) result = true; } else if (strncmp(l + pos, "ftpProxy", 8) == 0) { if (addProxy("ftp", l + 8)) result = true; } else if (strncmp(l + pos, "NoProxyFor", 10) == 0) { addNoProxy(l + 10); } } return result; } } //______________________________________________________________________ void proxyGuess() { string home = g_get_home_dir(); if (home[home.size() - 1] != DIRSEP) home += DIRSEP; //____________________ typedef set ConfFiles; ConfFiles c; add(&c, "/etc/lynx.cfg"); add(&c, home + ".lynxrc"); add(&c, getenv("LYNX_CFG")); add(&c, "/etc/wgetrc"); add(&c, home + ".wgetrc"); add(&c, getenv("WGETRC")); add(&c, "/etc/netscape4/defaults/preferences.js"); add(&c, "/etc/mozilla/prefs.js"); add(&c, home + ".netscape/preferences.js"); add(&c, home + ".galeon/mozilla/galeon/prefs.js"); add(&c, home + ".mozilla/default/prefs.js"); add(&c, home + ".netscape6/default/prefs.js"); // Is this correct? add(&c, home + ".kde/share/config/kioslaverc"); bool finished = false; while (!finished && !c.empty()) { ConfFiles::iterator first = c.begin(); ConfFiles::iterator second = first; ++second; BrowserConfig* bc = *first; debug("Scan: %1 %2", bc->age, bc->filename); finished = bc->read(); delete bc; c.erase(first, second); } // Most recent browser config was found, just delete rest while (!c.empty()) { ConfFiles::iterator first = c.begin(); ConfFiles::iterator second = first; ++second; BrowserConfig* bc = *first; debug("Ignr: %1 %2", bc->age, bc->filename); delete bc; c.erase(first, second); } } #endif /* WINDOWS */ jigdo-0.7.3/src/net/proxyguess.hh0000644000175000017500000000135110226060300016542 0ustar richardrichard/* $Id: proxyguess.hh,v 1.2 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Find proxy URLs by reading config files of various browsers. */ #ifndef PROXYGUESS_HH #define PROXYGUESS_HH #include #include #include /** Sets http and ftp to the selected proxy, or clears it if using direct internet connection. Accumulates noproxy entries from all sources. Call this after glibwww_init(). */ void proxyGuess(); #endif jigdo-0.7.3/src/net/uri-test.cc0000644000175000017500000000405210261546437016100 0ustar richardrichard/* $Id: uri-test.cc,v 1.4 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. #test-deps glibcurl/glibcurl.o net/uri.o compat.o #test-ldflags $(CURLLIBS) $(LDFLAGS_WINSOCK) */ #define DEBUG 1 #include #include #include namespace { void testUriJoin(const char* base, const char* rel, const char* expected) { string s = "anything", b = base, r = rel; uriJoin(&s, b, r); msg("base=%1, rel=%2, result=%3, expected=%4", b, r, s, expected); Assert(s == expected); } } int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); testUriJoin("foo", "bar", "foo/bar"); testUriJoin("http://base/", "rel", "http://base/rel"); testUriJoin("http://base/boo.html", "rel", "http://base/rel"); testUriJoin("http://base/", "http://wah/", "http://wah/"); testUriJoin("http://base/", "telnet://", "telnet://"); /* Should ideally eliminate ../ if possible. */ testUriJoin("http://base/", "../../x", "http://base/x"); testUriJoin("http://base/a/b/", "../x/./y", "http://base/a/x/y"); testUriJoin("http://cdimage.debian.org/pub/cdimage-testing/cd/jigdo-area/" "i386/sarge-i386-1.jigdo", "/debian-cd/debian-servers.jigdo", "http://cdimage.debian.org/debian-cd/debian-servers.jigdo"); testUriJoin("http://cdimage.debian.org", "/debian-cd/debian-servers.jigdo", "http://cdimage.debian.org/debian-cd/debian-servers.jigdo"); testUriJoin("http://host/leaf", "foo", "http://host/foo"); testUriJoin("http://host/leaf", "../../foo", "http://host/foo"); testUriJoin("http://host/leaf", "../../foo/", "http://host/foo/"); testUriJoin("http://host/a/b/c", "/bar", "http://host/bar"); testUriJoin("http://host/a/b/c", "/", "http://host/"); msg("Exit"); return 0; } jigdo-0.7.3/src/net/uri.cc0000644000175000017500000000636510117633635015131 0ustar richardrichard/* $Id: uri.cc,v 1.6 2004/09/08 16:47:25 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ #include #include #include #include #include //______________________________________________________________________ //DEBUG_UNIT("uri") /* RFC 1738 sez: ://:@:/ Some or all of the parts ":@", ":", ":", and "/" may be excluded. */ /** Create a new URI from an absolute base URI and a relative URI. (rel can also be absolute, in this case, the result in dest equals rel.) */ void uriJoin(string* dest, const string& base, const string& rel) { if (isRealUrl(rel) || base.empty()) { *dest = rel; return; } /* Parse base URL. We can assume that that URL is in a valid format, so the url-path starts after the 3rd '/', if that is present */ string::size_type hostSlash = base.find('/'); if (hostSlash != string::npos) { hostSlash = base.find('/', hostSlash + 1); if (hostSlash != string::npos) hostSlash = base.find('/', hostSlash + 1); } *dest = base; if (hostSlash == string::npos) { hostSlash = dest->length(); *dest += '/'; } // hostSlash points to the '/' after the hostname // Remove leafname from dest string::size_type n = dest->find_last_of('/'); dest->erase(n + 1); string::size_type r = 0; // Offset from which onward to append rel to dest if (rel.length() > 0 && rel[0] == '/') { // rel is server-absolute dest->erase(hostSlash + 1); ++r; } // Scan through rel, applying path components to dest while (r < rel.length()) { //debug("url='%1', rel='%2'", *dest, rel.c_str() + r); if (compat_compare(rel, r, 2, "./", 2) == 0) { r += 2; } else if (compat_compare(rel, r, 3, "../", 3) == 0) { //debug("a"); r += 3; string::size_type n = dest->length() - 1; Assert((*dest)[n] == '/'); n = dest->rfind('/', n - 1); if (n != string::npos && n >= hostSlash) dest->erase(n + 1); } else { string::size_type n = rel.find('/', r); if (n != string::npos) ++n; else n = rel.length(); //debug("%1 %2 %3", rel, r, n); dest->append(rel, r, n - r); r = n; } } } //______________________________________________________________________ unsigned findLabelColon(const string& s) { string::const_iterator i = s.begin(), e = s.end(); while (i != e) { if (*i == '/' || static_cast(*i) <= ' ') return 0; if (*i == ':') return i - s.begin(); ++i; } return 0; } //______________________________________________________________________ bool isRealUrl(const string& s) { unsigned l = findLabelColon(s); return l > 0 && s.length() >= l + 3 && s[l + 1] == '/' && s[l + 2] == '/'; } //______________________________________________________________________ bool isLabelUrl(const string& s) { unsigned l = findLabelColon(s); return l > 0 && s.length() >= l + 3 && s[l + 1] != '/' && s[l + 2] != '/'; } jigdo-0.7.3/src/net/uri.hh0000644000175000017500000000266510226060300015122 0ustar richardrichard/* $Id: uri.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Helper functions for dealing with URLs. */ #ifndef URI_HH #define URI_HH #include #include /** Create a new URI from an absolute base URI and a relative URI. (rel can also be absolute, in this case, the result in dest equals rel.) */ void uriJoin(string* dest, const string& base, const string& rel); //______________________________________________________________________ /** Return offset of first ':' in string if it is preceded by characters other than '/', space or control characters, otherwise return 0. */ unsigned findLabelColon(const string& s); //______________________________________________________________________ /** Return true iff the absolute URL is a "real" HTTP/FTP/.. url, as opposed to a label name followed by a path. */ bool isRealUrl(const string& s); //______________________________________________________________________ /** Return true iff the absolute URL is a Label:some/path URL, i.e. there's a colon before the first '/', and that colon is not followed by two slashes. */ bool isLabelUrl(const string& s); #endif jigdo-0.7.3/src/partialmatch.cc0000644000175000017500000000173407701377672016223 0ustar richardrichard/* $Id: partialmatch.cc,v 1.1.1.1 2003/07/04 22:29:14 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Helper class for mktemplate - queue of partially matched files */ #include #include #include #include //______________________________________________________________________ #if DEBUG void MkTemplate::PartialMatchQueue::consistencyCheck() const { int count = 0; for (PartialMatch* i = head; i != 0 && count <= MAX_MATCHES; i = i->next()) { Assert(i->next() == 0 || *i <= *(i->next())); ++count; } for (PartialMatch* i = freeHead; i != 0 && count <= MAX_MATCHES; i = i->next()) ++count; Assert(count == MAX_MATCHES); } #endif jigdo-0.7.3/src/partialmatch.hh0000644000175000017500000001434310121135314016204 0ustar richardrichard/* $Id: partialmatch.hh,v 1.5 2004/09/12 21:08:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Helper class for MkTemplate - queue of partially matched files */ #ifndef PARTIALMATCH_HH #include #include #define PARTIALMATCH_HH #ifndef INLINE # ifdef NOINLINE # define INLINE # else # define INLINE inline # endif #endif /** One object for each offset in image where any file /might/ match. Class interface is tailored towards mktemplate's needs, hence the unusual methods... */ class MkTemplate::PartialMatch { friend class MkTemplate::PartialMatchQueue; public: /** Offset in image at which this match starts */ uint64 startOffset() const { return startOff; } void setStartOffset(uint64 o) { startOff = o; } /** Next value of off at which to finish() sum & compare */ uint64 nextEvent() const { return nextEv; } /** Move x's position in the queue depending on the new value of its nextEvent. Uses linear search. */ INLINE void setNextEvent(PartialMatchQueue* matches, uint64 newNextEvent); /** Offset in buf of start of current MD5 block */ size_t blockOffset() const { return blockOff; } void setBlockOffset(size_t b) { blockOff = b; } /** Number of block in file, i.e. index into file()->sums[] */ size_t blockNumber() const { return blockNr; } void setBlockNumber(size_t b) { blockNr = b; } /** File whose sums matched so far */ FilePart* file() const { return filePart; } void setFile(FilePart* f) { filePart = f; } /** Next in list, or null */ PartialMatch* next() const { return nextPart; } private: PartialMatch() { } // Only to be instantiated by PartialMatchQueue uint64 startOff; // Offset in image at which this match starts uint64 nextEv; // Next value of off at which to finish() sum & compare size_t blockOff; // Offset in buf of start of current MD5 block size_t blockNr; // Number of block in file, i.e. index into file->sums[] FilePart* filePart; // File whose sums matched so far PartialMatch* nextPart; bool operator<=(const PartialMatch& x) { return nextEvent() <= x.nextEvent(); } }; //________________________________________ /** Queue of PartialMatch objects, always kept sorted by ascending nextEvent. This may seem to make many operations inefficient, but this sort order is best for the central mktemplate loop. */ class MkTemplate::PartialMatchQueue { friend class MkTemplate::PartialMatch; public: inline PartialMatchQueue(); bool empty() const { return head == 0; } bool full() const { return freeHead == 0; } /* If true is returned, the queue is not only full, findDropCandidate() is also going to always return 0 from now on. Intended to be used for optimisation: As long as crammed()==true, needn't even check for further prospective matches. The value returned by this function is set to true by findDropCandidate(), and set to false by erase(), eraseFront(), eraseStartOffsetLess() and setNextEvent(). (NB not by setStartOffset()) */ //bool crammed() const { return crammedVal; } PartialMatch* front() const { return head; } /** Add a new entry to the front of the queue. The queue must not be full. The new entry has all members set to 0, including its startOffset(). Use the setter methods to change this. @return new object at front() */ INLINE PartialMatch* addFront(); /** Return pointer to element with the lowest startOff value, or null if queue empty. */ INLINE PartialMatch* lowestStartOffset() const; /** Return lowest nextEvent() of all queue entries, which is always the nextEvent() of the first queue entry. Queue must not be empty. */ INLINE uint64 nextEvent() const; /** Return first matching entry with startOffset()==off, or null if none found. */ INLINE PartialMatch* findStartOffset(uint64 off) const; /** Return entry in list with lowest startOffset() value. List must not be empty. */ INLINE PartialMatch* findLowestStartOffset() const; /** Remove all entries from list. */ inline void erase(); /** Remove first entry from list. List must not be empty. */ INLINE void eraseFront(); /** Remove all entries whose startOffset is strictly less than off */ INLINE void eraseStartOffsetLess(uint64 off); /** If the queue is full, use some heuristics to find a PartialMatch in the queue which is "unlikely to lead to an actual match", or 0 if none exists. @param sectorLength assumed "sector size" @param newStartOffset start offset of the new match which is to replace the object returned by this function. The heuristics favours offsets which are multiples of the assumed "sector size". */ INLINE PartialMatch* findDropCandidate(unsigned* sectorLength, uint64 newStartOffset); # if DEBUG void consistencyCheck() const; # else void consistencyCheck() const { } # endif private: /* The size of the linked list of MD5 blocks awaiting matching must be limited for cases where there are lots of overlapping matches, e.g. both image and a file are all zeroes. */ static const int MAX_MATCHES = 2048; PartialMatch* head; // First entry of queue, or null if queue empty PartialMatch* freeHead; // First elem of linked list of free slots, or null PartialMatch data[MAX_MATCHES]; /* If true, queue is full, and furthermore findDropCandidate() will always return 0 */ //bool crammedVal; }; //______________________________________________________________________ void MkTemplate::PartialMatchQueue::erase() { head = 0; freeHead = &data[0]; //crammedVal = false; // Put all elements in free list data[MAX_MATCHES - 1].nextPart = 0; for (int i = MAX_MATCHES - 2; i >= 0; --i) data[i].nextPart = &data[i + 1]; consistencyCheck(); } MkTemplate::PartialMatchQueue::PartialMatchQueue() { erase(); } //______________________________________________________________________ #ifndef NOINLINE # include /* NOINLINE */ #endif #endif jigdo-0.7.3/src/partialmatch.ih0000644000175000017500000002040507717143066016225 0ustar richardrichard/* $Id: partialmatch.ih,v 1.5 2003/08/15 11:38:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Helper class for mktemplate - queue of partially matched files */ #ifndef PARTIALMATCH_IH #define PARTIALMATCH_IH #include #include #include #include #include #include //______________________________________________________________________ /** Add a new entry to the front of the queue. The queue must not be full. The new entry has all members set to 0, including its startOffset(). Use the setter methods to change this. @return new object at front() */ MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue::addFront() { //cerr << "PartialMatchQueue::addFront" << endl; Assert(!full()); // Move entry from list of free entries to list of used entries PartialMatch* result = freeHead; freeHead = freeHead->nextPart; result->nextPart = head; head = result; // Initialize new entry result->startOff = 0; result->nextEv = 0; result->blockOff = 0; result->blockNr = 0; result->filePart = 0; return result; } /** Return pointer to element with the lowest startOff value, or null if queue empty. */ MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue::lowestStartOffset() const { //cerr << "PartialMatchQueue::lowestStartOffset" << endl; Paranoid(!empty()); PartialMatch* lowest = head; PartialMatch* i = head; while (true) { i = i->next(); if (i == 0) return lowest; if (lowest->startOff > i->startOff) lowest = i; } } /** Return lowest nextEvent() of all queue entries, which is always the nextEvent() of the first queue entry. Queue must not be empty. */ uint64 MkTemplate::PartialMatchQueue::nextEvent() const { //cerr << "PartialMatchQueue::nextEvent" << endl; Paranoid(!empty()); // Queue must not be empty return front()->nextEvent(); } /** Return first matching entry with startOffset()==off, or null if none found. */ MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue::findStartOffset( uint64 off) const { //cerr << "PartialMatchQueue::findStartOffset" << endl; for (PartialMatch* i = head; i != 0; i = i->next()) if (i->startOff == off) return i; return 0; } /** Return entry in list with lowest startOffset() value. List must not be empty. */ MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue:: findLowestStartOffset() const { //cerr << "PartialMatchQueue::findLowestStartOffset" << endl; Paranoid(!empty()); // Queue must not be empty PartialMatch* lowest = front(); PartialMatch* i = lowest->next(); while (i != 0) { if (lowest->startOffset() > i->startOffset()) lowest = i; i = i->next(); } return lowest; } /** Remove first entry from list. List must not be empty. */ void MkTemplate::PartialMatchQueue::eraseFront() { //cerr << "PartialMatchQueue::eraseFront" << endl; Paranoid(!empty()); // Queue must not be empty //crammedVal = false; PartialMatch* x = head; head = head->nextPart; x->nextPart = freeHead; freeHead = x; consistencyCheck(); } /** Remove all entries whose startOffset is strictly less than off */ void MkTemplate::PartialMatchQueue::eraseStartOffsetLess(uint64 off) { //cerr << "PartialMatchQueue::eraseStartOffsetLess" << endl; //crammedVal = false; PartialMatch** xIndir = &head; PartialMatch* x = *xIndir; while (x != 0) { if (x->startOff >= off) { xIndir = &(x->nextPart); x = *xIndir; continue; } // Remove x from normal list and insert it into free list *xIndir = x->nextPart; // delete from list x->nextPart = freeHead; freeHead = x; // x new head of free list x = *xIndir; } consistencyCheck(); } /** Move x's position in the queue depending on the new value of its nextEvent. Uses linear search. */ void MkTemplate::PartialMatch::setNextEvent( PartialMatchQueue* matches, uint64 newNextEvent) { //cerr << "PartialMatch::setNextEvent" << endl; //matches->crammedVal = false; Paranoid(!matches->empty()); // Delete "this" from list PartialMatch** xIndir = &(matches->head); PartialMatch* x = *xIndir; while (x != this) { Paranoid(x != 0); // "this" must be present in matches! xIndir = &(x->nextPart); x = *xIndir; } *xIndir = x->nextPart; // Find right position and re-insert "this" there xIndir = &(matches->head); x = *xIndir; while (x != 0 && x->nextEv < newNextEvent) { xIndir = &(x->nextPart); x = *xIndir; } // Insert "this" before x nextPart = x; *xIndir = this; nextEv = newNextEvent; matches->consistencyCheck(); } MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue::findDropCandidate( unsigned* sectorLength, uint64 newStartOffset) { Paranoid(full()); // Queue must be full const PartialMatch* oldestMatch = findLowestStartOffset(); unsigned sectorMask; while (true) { sectorMask = *sectorLength - 1; // Don't drop any match in favour of an unaligned one if ((newStartOffset & sectorMask) != 0) return 0; for (PartialMatch* i = front(); i != 0; i = i->next()) { // Never return oldestMatch if (i == oldestMatch) continue; // Only drop match which is not sector-aligned if ((i->startOffset() & sectorMask) != 0) return i; } /* Don't increase sectorLength indefinitely - this would lead to an infinite loop if the queue is filled with entries with startOffset==0. */ if (*sectorLength == MkTemplate::MAX_SECTOR_LENGTH) return 0; // All of the matches are sector-aligned - increase sector size & retry *sectorLength *= 2; LOCAL_DEBUG_TO(MkTemplate::debug); debug("Increased sector length to %1", *sectorLength); } } #if 0 /** Find the FilePart object which is referenced most from entries of the PartialMatchQueue, then return the PartialMatch with the highest startOffset which references it. Return null if no FilePart is ref'd more than once. */ /* NB Whatever the heuristics, *never* return as drop candidate the match with the lowest startOffset of all the list! */ MkTemplate::PartialMatch* MkTemplate::PartialMatchQueue::findDropCandidate( uint64 newStartOffset) { if (crammed()) return 0; typedef map Map; Map m; // FileParts whose startOffset is not a multiple of 2048. See below. const int SECTOR_SIZE = 2048; // a power of 2 PartialMatch* oddOffset1 = 0; PartialMatch* oddOffset2 = 0; // Find FilePart which is referenced most often, and at least twice int highestCount = 0; FilePart* highestFile = 0; for (PartialMatch* i = front(); i != 0; i = i->next()) { if ((i->startOffset() & (SECTOR_SIZE-1)) != 0) { oddOffset2 = oddOffset1; oddOffset1 = i; } Map::iterator pos = m.find(i->file()); if (pos == m.end()) { // Insert new entry with counter 1 m.insert(pos, make_pair(i->file(), 1)); } else { // Increase existing counter ++(pos->second); Paranoid(pos->second >= 2); if (pos->second > highestCount) { highestCount = pos->second; highestFile = i->file(); } } } /* Of the PartialMatch entries referencing highestFile, return the one with the highest startOffset() */ PartialMatch* result = 0; if (highestFile != 0) { for (PartialMatch* i = front(); i != 0; i = i->next()) { if (i->file() != highestFile) continue; if (result == 0 || i->startOffset() > result->startOffset()) result = i; } Assert(result != 0); } if (result != 0) return result; /* The queue is full of matches of distinct files. As a last resort, drop file matches which do not start at a full sector offset and replace them with ones that do. Take care not to return the FileMatch with the lowest startOffset. */ if ((newStartOffset & (SECTOR_SIZE-1)) != 0) return 0; if (findLowestStartOffset() == oddOffset1) oddOffset1 = oddOffset2; if (optDebug()) { if (oddOffset1 == 0) debug("QUEUE CRAMMED"); else debug("DROPPING match with odd start offset"); } if (oddOffset1 == 0) crammedVal = true; return oddOffset1; } #endif /*0*/ #endif jigdo-0.7.3/src/recursedir-test.cc0000644000175000017500000000243607701377676016702 0ustar richardrichard/* $Id: recursedir-test.cc,v 1.1.1.1 2003/07/04 22:29:18 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Create recursive directory listing, avoiding symlink loops */ #include #include #define DEBUG 1 #include //______________________________________________________________________ #include #include //______________________________________________________________________ int main(int argc, const char* argv[]) { RecurseDir r; for (int i = 1; i < argc; ++i) { const char* arg = argv[i]; if (arg[0] == '-') { cerr << "Reading filenames from `" << (arg + 1) << '\'' << endl; r.addFilesFrom(arg + 1); } else { r.addFile(arg); } } cerr << "Begin" << endl; string obj; while (true) { try { bool status = r.getName(obj); if (status) break; cout << obj << endl; } catch (RecurseError e) { cerr << "Caught RecurseError: " << e.message << endl; } } cerr << "Finished" << endl; return 0; } jigdo-0.7.3/src/recursedir.cc0000644000175000017500000002165310431672643015711 0ustar richardrichard/* $Id: recursedir.cc,v 1.10 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Create recursive directory listing, avoiding symlink loops Problem: The user might use "find -f", which does not follow symlinks, but they might also preload the jigdo cache with "jigdo-file scan", which does. Thus, if we were to recurse into symlinks to diretories before recursing into the real directories themselves, the files in those directories would only be entered into the cache with a name via a symlink, not their real name. Queries to the cache use the filename, so when the user used "find -f", there will be a query for blah/foo/somefile, whereas if RecurseDir didn't handle symlinks specially, only be a cache entry for blah/symlink-to-foo/somefile would be present, causing the cache lookup to fail. This goes into great contortions in order to first access non-symlink objects and then symlinks. */ #include #include #include #include #include #include #include #include //______________________________________________________________________ namespace { void throw_RecurseError_forObject(const string& name) { string err = subst(_("Skipping object `%1' (%2)"), name, strerror(errno)); throw RecurseError(err); } void throw_RecurseError_forDir(const string& name) { string err = subst(_("Error reading from directory `%1' (%2)"), name, strerror(errno)); throw RecurseError(err); } } // local namespace //______________________________________________________________________ /* Assign the next object name to result. Returns FAILURE if no more names available. Note: An object name is immediately removed from the start of "objects" when it is copied to "result". The name of a file from which filenames are read, in contrast, stays at the head of "objectsFrom" until the file is closed, to allow for proper error messages to be generated. */ bool RecurseDir::getNextObjectName(string& result) throw(RecurseError) { while (true) { // Try to assign the name of an object (name or dir) to result if (!objects.empty()) { // Out of single list of object names result = objects.front(); //cerr << "getNextObjectName: single " << result << endl; objects.pop(); return SUCCESS; //________________________________________ } else if (listStream != 0) { // Read line from open file or stdin if (listStream->eof()) { if (listStream != &cin) fileList.close(); objectsFrom.pop(); listStream = 0; continue; } if (!listStream->good()) { string err; if (listStream == &cin) { err = subst( _("Error reading filenames from standard input (%1)"), strerror(errno)); } else { err = subst(_("Error reading filenames from `%1' (%2)"), objectsFrom.front(), strerror(errno)); fileList.close(); } objectsFrom.pop(); listStream = 0; throw RecurseError(err); } Paranoid(listStream == &cin || fileList.is_open()); getline(*listStream, result); /* An empty line terminates the list of files - this allows both the list and the image data to be fed to stdin with jigdo */ if (result.empty()) { if (listStream != &cin) fileList.close(); objectsFrom.pop(); listStream = 0; continue; } return SUCCESS; //________________________________________ } else if (!objectsFrom.empty()) { // Open new file to read lines from //cerr << "getNextObjectName: opening list of filenames"<< endl; Paranoid(listStream == 0); //cerr<<"getNextObjectName: open new " << objectsFrom.front()<< endl; if (objectsFrom.front().size() == 0) { listStream = &cin; continue; } fileList.open(objectsFrom.front().c_str(), ios::binary); listStream = &fileList; continue; //________________________________________ } else { // Out of luck - no more filenames left to supply to caller return FAILURE; } } } //________________________________________ bool RecurseDir::getName(string& result, struct stat* fileInfo, bool checkFiles) throw(RecurseError, bad_alloc) { static struct stat fInfo; if (fileInfo == 0) fileInfo = &fInfo; while (true) { //cerr << "Recurse: stack size " << recurseStack.size() << ", `" // << curDir << "'" << endl; if (!recurseStack.empty()) { // Continue recursing through directories Level& level = recurseStack.top(); struct dirent* entry; while (true) { entry = readdir(level.dir); # if UNIX || WINDOWS if (entry == 0) break; const char* n = entry->d_name; if (n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0))) { //cerr << "Recurse: Skip `" << n << "'" << endl; continue; } # endif break; } if (entry == 0) { // End-of-directory reached, continue one dir level up //cerr << "Recurse: End of dir `" << curDir << "'" << endl; level.close(); recurseStack.pop(); if (!recurseStack.empty()) curDir.erase(recurseStack.top().dirNameLen); // Remove leafname continue; } //____________________ // Valid object name was read from directory result = curDir; result += entry->d_name; if (lstat(result.c_str(), fileInfo) != 0) throw_RecurseError_forObject(result); if (isSymlink(fileInfo)) { // Do not handle object now, push at end of queue objects.push(result); //cerr << "Recurse: Symlink `" << result << "' pushed" << endl; continue; } //____________________ // Skip object if this inode has already been visited if (alreadyVisited(fileInfo)) { # if DEBUG //cerr << "Recurse: `" << result << "' ignored" << endl; # endif continue; } if (!S_ISDIR(fileInfo->st_mode)) { // Object is regular file (or device/other non-directory) return SUCCESS; } // Object is a directory - recurse //cerr << "Recurse: into `" << result << "'" << endl; DIR* dir = opendir(result.c_str()); if (dir == 0) throw_RecurseError_forDir(result); curDir += entry->d_name; curDir += DIRSEP; recurseStack.push(Level(dir, curDir.length())); continue; } // endif (!recurseStack.empty()) //________________________________________ // Finished recursing - any more input objects? if (getNextObjectName(result)) return FAILURE; # if WINDOWS /* Allow trailing '\' in input args, by removing it before passing it on - but don't remove for "C:\" or "\". */ size_t len = result.length(); if (result[len - 1] == DIRSEP && !(len == 3 && isalpha(result[0]) && result[1] == ':') && !(len == 1)) result.erase(len - 1); # endif /* FIXME: "make-template --no-check-files" should 1) scan any files given on the cmd line, 2) later, while scanning through the image, only operate on the cache contents; if a matching md5sum block is found, it could be from an old cache entry (which is not verified if checkFiles==true) or a new entry that was set up/updated during step 1). Advantage: Unlike the way it is now, *all* files in the cache will then be used during the image scan, not just those from the cmd line. */ # warning TODO hackish // This also ignores directories given on the "make-template" command line: if (!checkFiles) return SUCCESS; /* 'result' now holds the name of a file (=> we output it) or a directory (=> we recurse into it). There is no distinction between symlinks and non-symlinks here. */ if (stat(result.c_str(), fileInfo) != 0) throw_RecurseError_forObject(result); if (alreadyVisited(fileInfo)) { # if DEBUG //cerr << "Recurse: arg `" << result << "' ignored" << endl; # endif continue; } //____________________ if (!S_ISDIR(fileInfo->st_mode)) { // Object is regular file (or device/whatever - just output) return SUCCESS; } // Object is directory - recurse DIR* dir = opendir(result.c_str()); if (dir == 0) throw_RecurseError_forDir(result); curDir = result; if (curDir[curDir.size() - 1] != DIRSEP) curDir += DIRSEP; recurseStack.push(Level(dir, curDir.length())); continue; } // endwhile (true) } jigdo-0.7.3/src/recursedir.fh0000644000175000017500000000073107701377704015721 0ustar richardrichard/* $Id: recursedir.fh,v 1.1.1.1 2003/07/04 22:29:24 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Create recursive directory listing, avoiding symlink loops */ struct RecurseError; class RecurseDir; jigdo-0.7.3/src/recursedir.hh0000644000175000017500000001204710431672643015720 0ustar richardrichard/* $Id: recursedir.hh,v 1.7 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Create recursive directory listing, avoiding symlink loops */ #ifndef RECURSEDIR_HH #define RECURSEDIR_HH #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ /** Errors which occur during RecurseDir's work */ struct RecurseError : Error { explicit RecurseError(const string& m) : Error(m) { } explicit RecurseError(const char* m) : Error(m) { } }; /** A filename generator; feed it names of single files or directories, it will output them, recursing depth-first through directories. It does follow symlinks, but will not output more than one name for an inode, and avoid symlink loops. If an inode can be reached both through its "normal" name and through symlinks during recursion into one directory, it is guaranteed that the normal name will be listed. */ class RecurseDir { public: RecurseDir() : curDir(), recurseStack(), objects(), objectsFrom(), fileList(), listStream(0) { } inline ~RecurseDir(); /** Provide single file/directory name to output or recurse into */ void addFile(const char* name) { objects.push(string(name)); } /** Provide single file/directory name to output or recurse into */ void addFile(const string& name) { objects.push(name); } /** To read filenames from stdin, pass an empty string */ void addFilesFrom(const char* name) { objectsFrom.push(string(name)); } void addFilesFrom(const string& name) { objectsFrom.push(name); } /** Are there no filename sources present at all? */ bool empty() const { return objects.empty() && objectsFrom.empty(); } /** Returns FAILURE if no more names. After a RecurseError has been thrown, it is no problem to continue using the object. @param result Returned filename (output only) @param fileInfo getName() must call stat() for any filename it returns, in order to determine whether it is a directory. If the information returned by stat() is useful for you, supply your own struct stat for getName() to use. @param checkFiles if true, normal operation, check whether files exist before using cache entries. If false, only check whether cache entry present and return it if so. */ bool getName(string& result, struct stat* fileInfo = 0, bool checkFiles = true) throw(RecurseError, bad_alloc); /** Flush list of "already visited device/inode pairs", which would otherwise be skipped during further scanning of filenames. */ private: // Insert fileInfo in beenThere, return true if it was already there inline bool alreadyVisited(const struct stat* fileInfo); // For recording device/inode of objects already visited struct DevIno { DevIno(const struct stat* s) : dev(s->st_dev), ino(s->st_ino) { } bool operator<(const DevIno& d) const { return (ino < d.ino) || (ino == d.ino && dev < d.dev); } dev_t dev; ino_t ino; }; // One stack entry (recursion level) when recursing into directories struct Level { Level(DIR* d, size_t l) : dir(d), dirNameLen(l) { } void close() { if (dir == 0) return; closedir(dir); dir = 0; } DIR* dir; // Handle for readdir() size_t dirNameLen; // Length to shorten curDir to to get this dir's name }; string curDir; stack recurseStack; inline bool getNextObjectName(string& result) throw(RecurseError); queue objects; // Queue of filenames to output/dirs to recurse into queue objectsFrom; // Files containing filenames ifstream fileList; // Was head of objectsFrom once, now has been opened istream* listStream; // null if not open, else &fileList, or &cin # if HAVE_LSTAT set beenThere; // Already visited inodes, for loop prevention # endif }; //______________________________________________________________________ #if HAVE_LSTAT inline bool isSymlink(struct stat* buf) { return S_ISLNK(buf->st_mode); } bool RecurseDir::alreadyVisited(const struct stat* fileInfo) { pair::iterator, bool> ins = beenThere.insert(DevIno(fileInfo)); return !ins.second; } #else inline int lstat(const char* filename, struct stat* buf) { return stat(filename, buf); } inline bool isSymlink(struct stat*) { return false; } bool RecurseDir::alreadyVisited(const struct stat*) { return false; } #endif //____________________ RecurseDir::~RecurseDir() { if (listStream != 0 && listStream != &cin) fileList.close(); while (!recurseStack.empty()) { recurseStack.top().close(); recurseStack.pop(); } } #endif jigdo-0.7.3/src/scan.cc0000644000175000017500000004076610261607620014466 0ustar richardrichard/* $Id: scan.cc,v 1.11 2005/07/02 22:05:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Scanning of input files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("scan") void JigdoCache::ProgressReporter::error(const string& message) { cerr << message << endl; } void JigdoCache::ProgressReporter::info(const string& message) { cerr << message << endl; } void JigdoCache::ProgressReporter::scanningFile(const FilePart*, uint64) { } JigdoCache::ProgressReporter JigdoCache::noReport; struct stat JigdoCache::fileInfo; //______________________________________________________________________ #if HAVE_LIBDB /* Interpret a string of bytes (out of the file cache) like this: 4 blockLength (of rsync sum) 4 md5BlockLength 4 blocks (number of valid md5 blocks in this entry), curr. always >0 8 rsyncSum of file start (only valid if blocks > 0) 16 fileMD5Sum (only valid if blocks == (fileSize+md5BlockLength-1)/md5BlockLength ) followed by n entries: 16 md5sum of block of size md5BlockLength If stored md5BlockLength doesn't match supplied length, do nothing. Otherwise, restore *this from cached data and return cached blockLength (0 if not cached). The caller needs to make sure the blockLength matches. This is not a standard unserialize() member of FilePart because it does not create a complete serialization - e.g. the location path iter is missing. It only creates a cache entry. */ size_t FilePart::unserializeCacheEntry(const byte* data, size_t dataSize, size_t md5BlockLength){ Assert(dataSize > PART_MD5SUM); // The resize() must have been made by the caller Paranoid(sums.size() == (size() + md5BlockLength - 1) / md5BlockLength); size_t cachedBlockLength; data = unserialize4(cachedBlockLength, data); size_t cachedMd5BlockLength; data = unserialize4(cachedMd5BlockLength, data); if (cachedMd5BlockLength != md5BlockLength) return 0; size_t blocks; data = unserialize4(blocks, data); // Ignore strange-looking entries if (blocks * serialSizeOf(md5Sum) != dataSize - PART_MD5SUM || blocks == 0) { if (blocks == 0) debug("ERR #blocks == 0"); else debug("ERR wrong entry size (%1 vs %2)", blocks * 16, dataSize - PART_MD5SUM); return 0; } Paranoid(serialSizeOf(rsyncSum) == 8); data = unserialize(rsyncSum, data); Paranoid(serialSizeOf(md5Sum) == 16); // All blocks of file present? if (blocks == sums.size()) { setFlag(MD_VALID); data = unserialize(md5Sum, data); } else { clearFlag(MD_VALID); data += 16; } // Read md5 sums of individual chunks of file vector::iterator sum = sums.begin(); for (size_t i = blocks; i > 0; --i) { data = unserialize(*sum, data); ++sum; } return cachedBlockLength; } #endif //______________________________________________________________________ #if HAVE_LIBDB /** Opposite of unserializeCacheEntry; create byte stream from object */ struct FilePart::SerializeCacheEntry { SerializeCacheEntry(const FilePart& f, JigdoCache* c, size_t blockLen, size_t md5Len) : file(f), cache(c), blockLength(blockLen), md5BlockLength(md5Len) { } const FilePart& file; JigdoCache* cache; size_t blockLength; size_t md5BlockLength; size_t serialSizeOf() { return PART_MD5SUM + (file.mdValid() ? file.sums.size() * 16 : 16); } void operator()(byte* data) { Paranoid(file.getFlag(TO_BE_WRITTEN)); // If empty(), shouldn't have been marked TO_BE_WRITTEN: Assert(!file.sums.empty()); data = serialize4(blockLength, data); data = serialize4(md5BlockLength, data); // Nr of valid blocks - either 1 or all size_t blocks = (file.mdValid() ? file.sums.size() : 1); data = serialize4(blocks, data); data = serialize(file.rsyncSum, data); data = serialize(file.md5Sum, data); // Write md5 sums of individual chunks of file vector::const_iterator sum = file.sums.begin(); for (size_t i = blocks; i > 0; --i) { data = serialize(*sum, data); ++sum; } } }; #endif //______________________________________________________________________ #if HAVE_LIBDB JigdoCache::JigdoCache(const string& cacheFileName, size_t expiryInSeconds, size_t bufLen, ProgressReporter& pr) : blockLength(0), md5BlockLength(0), checkFiles(true), files(), nrOfFiles(0), locationPaths(), readAmount(bufLen), buffer(), reporter(pr), cacheExpiry(expiryInSeconds) { cacheFile = 0; try { if (!cacheFileName.empty()) cacheFile = new CacheFile(cacheFileName.c_str()); } catch (DbError e) { string err = subst(_("Could not open cache file: %L1"), e.message); reporter.error(err); } } #else JigdoCache::JigdoCache(const string&, size_t, size_t bufLen, ProgressReporter& pr) : blockLength(0), md5BlockLength(0), files(), nrOfFiles(0), locationPaths(), readAmount(bufLen), buffer(), reporter(pr) { } #endif //______________________________________________________________________ JigdoCache::~JigdoCache() { # if HAVE_LIBDB if (cacheFile) { // Write out any cache entries that need it for (list::const_iterator i = files.begin(), e = files.end(); i != e; ++i) { if (i->deleted() || !i->getFlag(FilePart::TO_BE_WRITTEN)) continue; debug("Writing %1", i->leafName()); FilePart::SerializeCacheEntry serializer(*i, this, blockLength, md5BlockLength); try { cacheFile->insert(serializer, serializer.serialSizeOf(), i->leafName(), i->mtime(), i->size()); } catch (DbError e) { reporter.error(e.message); } } if (cacheExpiry > 0) { // Expire old cache entries from cache time_t expired = time(0); Paranoid(expired != static_cast(-1)); expired -= cacheExpiry; try { cacheFile->expire(expired); } catch (DbError e) { string err = subst(_("Error during cache expiry: %1. The cache " "file may be corrupt, consider deleting it."), e.message); reporter.error(err); } } // Close db object, flushing changes to disc delete cacheFile; } # endif } //______________________________________________________________________ /* Either reads data for the first MD5 block and creates sums[0] and rsyncSum, or reads whole file and creates all sums[] entries and rsyncSum and the whole file's MD5 sum. */ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) { // Should do this check before calling: Paranoid((blockNr == 0 && sums.empty()) || !mdValid()); // Do not forget to setParams() before calling this! Assert(c->md5BlockLength != 0); const size_t thisBlockLength = c->blockLength; sums.resize((size() + c->md5BlockLength - 1) / c->md5BlockLength); //____________________ # if HAVE_LIBDB // Can we maybe get the info from the cache? if (c->cacheFile != 0 && !getFlag(WAS_LOOKED_UP)) { setFlag(WAS_LOOKED_UP); const byte* data; size_t dataSize; try { /* Unserialize will do nothing if md5BlockLength differs. If md5BlockLength matches, but returned blockLength doesn't, we need to re-read the first block. */ if (c->cacheFile->find(data, dataSize, leafName(), size(), mtime()) .ok()) { debug("%1 found, want block#%2", leafName(), blockNr); size_t cachedBlockLength = unserializeCacheEntry(data, dataSize, c->md5BlockLength); // Was all necessary data in cache? Yes => return it now. if (cachedBlockLength == thisBlockLength && (blockNr == 0 || mdValid())) { debug("%1 loaded, blockLen (%2) matched, %3/%4 in cache", leafName(), thisBlockLength, (mdValid() ? sums.size() : 1), sums.size()); return &sums[blockNr]; } /* blockLengths didn't match and/or the cache only contained the first md5 sum while we asked for a later one. It's as if we never queried the cache, except for the case when we need to re-read the first block because the blockLength changed, but *all* blocks' md5sums were in the cache. */ debug("%1 loaded, NO match (blockLen %2 vs %3), %4/%5 in cache", leafName(), cachedBlockLength, thisBlockLength, (mdValid() ? sums.size() : 1), sums.size()); } } catch (DbError e) { string err = subst(_("Error accessing cache: %1"), e.message); c->reporter.error(err); } } # endif /* HAVE_LIBDB */ //____________________ // Open input file string name(getPath()); name += leafName(); bifstream input(name.c_str(), ios::binary); if (!input) { string err; if (name == "-") { /* Actually, stdin /would/ be allowed /here/, but it isn't possible with mktemplate. */ err = _("Error opening file `-' " "(using standard input not allowed here)"); } else { err = subst(_("Could not open `%L1' for input - excluded"), name); if (errno != 0) { err += " ("; err += strerror(errno); err += ')'; } } markAsDeleted(c); c->reporter.error(err); // might throw return 0; } //____________________ // We're going to write this to the cache later on setFlag(TO_BE_WRITTEN); // Allocate or resize buffer, or do nothing if already right size c->buffer.resize(c->readAmount > c->md5BlockLength ? c->readAmount : c->md5BlockLength); //______________________________ // Read data and create sums uint64 off = 0; // File offset of first byte in buf // Nr of bytes before we are to reset() md size_t mdLeft = c->md5BlockLength; /* Call reporter once off reaches this value - only report something if scanning >1 md5 block */ uint64 nextReport = mdLeft; MD5Sum md; md5Sum.reset(); vector::iterator sum = sums.begin(); //____________________ // Calculate RsyncSum of head of file and MD5Sums for all blocks Assert(thisBlockLength <= c->md5BlockLength); byte* buf = &c->buffer[0]; byte* bufpos = buf; byte* bufend = buf + (c->readAmount > thisBlockLength ? c->readAmount : thisBlockLength); while (input && static_cast(bufpos - buf) < thisBlockLength) { readBytes(input, bufpos, bufend - bufpos); size_t nn = input.gcount(); bufpos += nn; debug("Read %1", nn); } size_t n = bufpos - buf; // Create RsyncSum of 1st bytes of file, or leave at 0 if file too small rsyncSum.reset(); if (n >= thisBlockLength) rsyncSum.addBack(buf, thisBlockLength); //__________ while (true) { // Will break out if error or whole file read // n is number of valid bytes in buf[] off += n; if (off > size()) break; // Argh - file size changed if (off >= nextReport) { c->reporter.scanningFile(this, off); nextReport += REPORT_INTERVAL; } // Create MD5 for chunks of size md5BlockLength if (n < mdLeft) { md.update(buf, n); mdLeft -= n; } else { md.update(buf, mdLeft); byte* cur = buf + mdLeft; size_t nn = n - mdLeft; do { md.finishForReuse(); debug("%1: mdLeft (0), switching to next md at off %2, left %3, " "writing sum#%4: %5", name, off - n + cur - buf, nn, sum - sums.begin(), md.toString()); Paranoid(sum != sums.end()); *sum = md; ++sum; size_t m = (nn < c->md5BlockLength ? nn : c->md5BlockLength); md.reset().update(cur, m); cur += m; nn -= m; mdLeft = c->md5BlockLength - m; } while (nn > 0); } md5Sum.update(buf, n); // Create MD5 for the whole file if (blockNr == 0 && sum != sums.begin()) break; // Only wanted 1st block if (!input) break; // End of file or error // Read more data readBytes(input, buf, c->readAmount); n = input.gcount(); debug("%1: read %2", name, n); } // Endwhile (true), will break out if error or whole file read Paranoid(sum != sums.end() // >=1 trailing bytes || mdLeft == c->md5BlockLength); // 0 trailing bytes if (off == size() && input.eof()) { // Whole file was read c->reporter.scanningFile(this, size()); // 100% scanned if (mdLeft < c->md5BlockLength) { (*sum) = md.finish(); // Digest of trailing bytes debug("%1: writing trailing sum#%2: %3", name, sum - sums.begin(), md.toString()); } md5Sum.finish(); // Digest of whole file setFlag(MD_VALID); return &sums[blockNr]; } else if (blockNr == 0 && sum != sums.begin()) { // Only first md5 block of file was read debug("%1: file header read, sum#0 written", name); # if DEBUG md5Sum.finish(); // else failed assert in FilePart::SerializeCacheEntry # else md5Sum.abort(); // Saves the memory until whole file is read # endif return &sums[0]; } //____________________ // Some error happened string err = subst(_("Error while reading `%1' - file will be ignored " "(%2)"), name, strerror(errno)); markAsDeleted(c); c->reporter.error(err); return 0; } //______________________________________________________________________ const MD5Sum* FilePart::getMD5SumRead(JigdoCache* c) { if (getSumsRead(c, (fileSize + c->md5BlockLength - 1) / c->md5BlockLength - 1) == 0) return 0; Paranoid(mdValid()); return &md5Sum; } //______________________________________________________________________ void JigdoCache::setParams(size_t blockLen, size_t md5BlockLen) { if (blockLen == blockLength && md5BlockLen == md5BlockLength) return; blockLength = blockLen; md5BlockLength = md5BlockLen; Assert(blockLength <= md5BlockLength); for (list::iterator file = files.begin(), end = files.end(); file != end; ++file) { file->sums.resize(0); } } //______________________________________________________________________ void JigdoCache::addFile(const string& name) { // Do not forget to setParams() before calling this! Assert(md5BlockLength != 0); // Assumes nonempty filenames Paranoid(name.length() > 0); // Find "//" in path and if present split name there string::size_type pathLen = name.rfind(SPLITSEP); string fileUri("file:"); string path, nameRest; if (pathLen == string::npos) { size_t splitAfter = 0; # if WINDOWS // Split after "\" or ".\" or "C:" or "C:\" or "C:.\" if (name.length() > 1 && isalpha(name[0]) && name[1] == ':') splitAfter = 2; if (name.length() > splitAfter && name[splitAfter] == '\\') { // If an absolute path, split after leading '\' ++splitAfter; } else if (name.length() > splitAfter + 1 && name[splitAfter] == '.' && name[splitAfter + 1] == '\\') { // Otherwise, also split after ".\" at start splitAfter += 2; } # else // If an absolute path, split after leading '/' if (name[0] == DIRSEP) splitAfter = 1; # endif path.assign(name, 0, splitAfter); fileUri += path; nameRest.assign(name, splitAfter, string::npos); } else { // e.g. for name = "dir//file" path.assign(name, 0, pathLen + 1); // path = "dir/" fileUri.append(name, 0, pathLen + 1); // fileUri = "file:dir/" // nameRest = "file" nameRest.assign(name, pathLen + sizeof(SPLITSEP) - 1, string::npos); } compat_swapFileUriChars(fileUri); // Directory separator is always '/' ConfigFile::quote(fileUri); //____________________ // If necessary, create a label for the path before "//" static string emptylabel; LocationPath tmp(path, emptylabel, fileUri); LocationPathSet::iterator i = locationPaths.find(tmp); if (i == locationPaths.end()) i = locationPaths.insert(tmp).first; // Any new entry has a "" label Paranoid(i != locationPaths.end()); // Append new obj at end of list FilePart fp(i, nameRest, fileInfo.st_size, fileInfo.st_mtime); files.push_back(fp); } jigdo-0.7.3/src/scan.fh0000644000175000017500000000072607701377664014507 0ustar richardrichard/* $Id: scan.fh,v 1.1.1.1 2003/07/04 22:29:08 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Scanning of input files */ class LocationPath; class FilePart; class JigdoCacheError; class JigdoCache; jigdo-0.7.3/src/scan.hh0000644000175000017500000004254310433355101014466 0ustar richardrichard/* $Id: scan.hh,v 1.6 2006/05/19 14:46:25 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Scanning of input files, cache of information for each scanned file. */ #ifndef SCAN_HH #define SCAN_HH #include #include #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ /** First part of the filename of a "part", a directory on the local filesystem. It will not appear in the location list - it is just prepended to the filename to actually access the file data. */ class LocationPath { /** Objects are only created by JigdoCache */ friend class JigdoCache; public: /** Sort LocationPaths by the directory name */ bool operator<(const LocationPath& x) const { return path < x.path; } const string& getPath() const { return path; } const string& getLabel() const { return label; } LocationPath& setLabel(const string& l) { label = l; return *this; } const string& getUri() const { return uri; } private: template inline LocationPath(const StringP& p, const StringL& l, const StringU& u = ""); // Using default dtor & copy ctor // The data members are accessed directly by JigdoCache methods string path; // name of directory string label; // short label to identify path in location list string uri; // e.g. URL for directory on FTP server }; typedef set LocationPathSet; //______________________________________________________________________ /** One of the "parts" (=single files) that the image (=big file) consists of. The object is also responsible for scanning the file and creating a) an RsyncSum64 of the first blockLength bytes (as determined by JigdoCache). Caching is done in a "lazy" manner; first, only the file size and name is read, then the MD5 sum of the file's first block as well as the Rsync sum is calculated, and only when the 2nd MD5 sum or the entire file's sum is requested, the file is scanned til EOF. */ class FilePart { /** Objects are only created by JigdoCache */ friend class JigdoCache; public: /** Sort FileParts by RsyncSum of first bytes */ inline const string& getPath() const; LocationPathSet::iterator getLocation() { return path; } /** @return The further dir names and the leafname, after what getPath() returns. */ inline const string& leafName() const; inline uint64 size() const; inline time_t mtime() const; /** Returns null ptr if error and you don't throw it in your JigdoCache error handler */ inline const MD5* getSums(JigdoCache* c, size_t blockNr); /** Returns null ptr if error and you don't throw it */ inline const MD5Sum* getMD5Sum(JigdoCache* c); /** Returns null ptr if error and you don't throw it */ inline const RsyncSum64* getRsyncSum(JigdoCache* c); /** Mark the FilePart as deleted. Unlike the STL containers' erase(), this means that any iterator pointing to the element stays valid, you just cannot access the element. TODO: Check whether this feature is really needed anywhere anymore - I suspect not, maybe we should just delete the element. */ inline void markAsDeleted(JigdoCache* c); /** True if this file will be ignored (but not destroyed with "delete") by JigdoCache because attempts to open/read it failed, or other reasons. */ bool deleted() const { return fileSize == 0; } /** Do not call - this is public only because list<> must be able to delete FileParts */ ~FilePart() { } // default copy ctor / assignment op //__________ private: inline FilePart(LocationPathSet::iterator p, string rest, uint64 fSize, time_t fMtime); /* Called when the methods getSums/getMD5Sum() need data read from file. Might return null. */ const MD5* getSumsRead(JigdoCache* c, size_t blockNr); const MD5Sum* getMD5SumRead(JigdoCache* c); //__________ /* There are 3 states of a FilePart: a) sums.empty(): File has not been read from so far b) !sums.empty() && !mdValid: sums[0] and rsyncSum are valid c) !sums.empty() && mdValid: all sums[] and rsyncSum and md5Sum valid*/ LocationPathSet::iterator path; string pathRest; // further dir names after "path", and leafname of file uint64 fileSize; time_t fileMtime; /* RsyncSum64 of the first MkTemplate::blockLength bytes of the file. */ RsyncSum64 rsyncSum; bool rsyncValid() const { return sums.size() > 0; } /* File is split up into chunks of length md5BlockLength (the last one may be smaller) and the MD5 checksum of each is calculated. */ vector sums; /* Hash of complete file contents. mdValid is true iff md5Sum is cached and valid. NB, it is possible that mdValid==true, but sums.size()==0, after md5BlockSize has been changed. If sums.size()==fileSize/md5ChunkSize, then not necessarily mdValid==true */ MD5Sum md5Sum; enum Flags { EMPTY = 0, // Bit flag is set iff md5Sum contains the whole file's md5 sum MD_VALID = 1, /* This file was looked up in the cache file (whether successfully or not doesn't matter) - don't look it up again. */ WAS_LOOKED_UP = 2, // Write this file's info into the cache file during ~JigdoCache() TO_BE_WRITTEN = 4 }; Flags flags; bool getFlag(Flags f) const { return (flags & f) != 0; } inline void setFlag(Flags f); inline void clearFlag(Flags f); bool mdValid() const { return getFlag(MD_VALID); } # if HAVE_LIBDB // Offsets for binary representation in database (see cachefile.hh) enum { BLOCKLEN = 0, MD5BLOCKLEN = 4, MD5BLOCKS = 8, RSYNCSUM = 12, FILE_MD5SUM = 20, PART_MD5SUM = 36 }; size_t unserializeCacheEntry(const byte* data, size_t dataSize, size_t md5BlockLength); // Byte stream => FilePart struct SerializeCacheEntry; // FilePart => byte stream friend struct SerializeCacheEntry; # endif }; //______________________________________________________________________ /** If a JigdoCacheError happens during any call to a FilePart or JigdoCache method, repeating that call is possible to continue. */ struct JigdoCacheError : Error { explicit JigdoCacheError(const string& m) : Error(m) { } explicit JigdoCacheError(const char* m) : Error(m) { } }; /** A list of FileParts that is "lazy": Nothing is actually read from the files passed to it until that is really necessary. JigdoCache behaves like a list. A JigdoCache cannot hold zero-length files. They will be silently skipped when you try to add them. Important note: If you decide to throw the errors handed to your errorHandler, JigdoCacheErrors can be thrown by some methods. The default cerrPrint (guess what) only outputs the error message to cerr. */ class JigdoCache { friend class FilePart; public: class ProgressReporter; /** cacheFileName can be "" for no file cache */ explicit JigdoCache(const string& cacheFileName, size_t expiryInSeconds = 60*60*24*30, size_t bufLen = 128*1024, ProgressReporter& pr = noReport); /** The dtor will try to write cached data to the cache file */ ~JigdoCache(); /** Read a list of filenames from the object and store them in the JigdoCache. Only the file size is read during the call, Rsync/MD5 sums are calculated later, if/when needed. */ template inline void readFilenames(RecurseDir& rd); /** Set the sizes of cache's blockLength and md5BlockLength parameters. This means that the checksum returned by a FilePart's getRsyncSum() method will cover the specified size. NB: If the cache already contains files and the new parameters are not the same as the old, the start of the files will (eventually) have to be re-read to re-calculate the checksums for blockLength, and the *whole* file will have to be re-read for a changed md5BlockLength. */ void setParams(size_t blockLen,size_t md5BlockLen); size_t getBlockLen() const { return blockLength; } size_t getMD5BlockLen() const { return md5BlockLength; } /** Amount of data that JigdoCache will attempt to read per call to ifstream::read(), and size of buffer allocated. Minimum: 64k */ inline void setReadAmount(size_t bytes); /** To speed things up, JigdoCache by default keeps one file open and a read buffer allocated. You can close the file and deallocate the buffer with a call to this method. They will be re-opened/re-allocated automatically if/when needed. */ void deallocBuffer() { buffer.resize(0); } /** Return reporter supplied by JigdoCache creator */ ProgressReporter* getReporter() { return &reporter; } /** Set and get whether to check if files exist in the filesystem */ inline void setCheckFiles(bool check) { checkFiles = check; } inline bool getCheckFiles() const { return checkFiles; } /** Returns number of files in cache */ inline size_t size() const { return nrOfFiles; } /** Make a label for a certain directory name known. E.g. addLabel("pub/debian/","deb") will cause the file pub/debianf;/dists/stable/x.deb to appear as deb:dists/stable/x.deb in the location list. Each of the paramters can bei either string or const char*. Note: If no labels are defined, a name will automatically be created later: dirA, dirB, ..., dirZ, dirAA, ... */ template const LocationPathSet::iterator addLabel( const StringP& path, const StringL& label, const StringU& uri = ""); /** Access to the members like for a list<> */ typedef FilePart value_type; typedef FilePart& reference; //____________________ /** Only part of the iterator functionality implemented ATM */ class iterator { friend class JigdoCache; friend class FilePart; public: iterator() { } inline iterator& operator++(); // might throw(RecurseError, bad_alloc) FilePart& operator*() { return *part; } FilePart* operator->() { return &*part; } // Won't compare cache members - their being different is usu. a bug bool operator==(const iterator& i) const { Paranoid(cache == i.cache); return part == i.part; } bool operator!=(const iterator& i) const { return !(*this == i); } // Default dtor private: iterator(JigdoCache* c, const list::iterator& p) : cache(c), part(p) { } JigdoCache* cache; list::iterator part; }; friend class JigdoCache::iterator; /** First element of the JigdoCache */ inline iterator begin(); /** NB the list auto-extends, so the value of end() may change while you iterate over a JigdoCache. */ iterator end() { return iterator(this, files.end()); } //____________________ private: // Read one filename from recurseDir and (if success) add entry to "files" void addFile(const string& name); /// Default reporter: Only prints error messages to stderr static ProgressReporter noReport; size_t blockLength, md5BlockLength; /* Check if files exist in the filesystem */ bool checkFiles; /* List of files in the cache (not vector<> because jigdo-file keeps ptrs, and if a vector realloc()s, all elements' addresses may change) */ list files; // Equal to files.size() less any files that are deleted() size_t nrOfFiles; // Temporarily used during readFilenames() static struct stat fileInfo; // Look up LocationPath by directory name LocationPathSet locationPaths; size_t readAmount; vector buffer; ProgressReporter& reporter; # if HAVE_LIBDB CacheFile* cacheFile; size_t cacheExpiry; # endif }; //______________________________________________________________________ /** Class allowing JigdoCache to convey information back to the creator of a JigdoCache object. */ class JigdoCache::ProgressReporter { public: virtual ~ProgressReporter() { } /** General-purpose error reporting. NB: With JigdoCache, you can throw an exception inside this to abort whatever operation JigdoCache is doing. May be called during all checksum-returning methods of FilePart */ virtual void error(const string& message); /** Like error(), but for purely informational messages. Default implementation, just like that of error(), prints message to cerr */ virtual void info(const string& message); /** Called when the individual files are read. JigdoCache sometimes reads only the first md5BlockLength bytes and sometimes the whole file. This is *only* called when the whole file is read (if the file only consists of one MD5 block, it is also called). @param file File being scanned, or null if not applicable (Contains filename info). Default version does nothing at all - you need to supply an object of a derived class to JigdoCache() to act on this. @param offInFile Offset in this file */ virtual void scanningFile(const FilePart* file, uint64 offInFile); }; //______________________________________________________________________ FilePart::FilePart(LocationPathSet::iterator p, string rest, uint64 fSize, time_t fMtime) : path(p), pathRest(rest), fileSize(fSize), fileMtime(fMtime), rsyncSum(), sums(), md5Sum(), flags(EMPTY) { //pathRest.reserve(0); } const string& FilePart::getPath() const { Paranoid(!deleted()); return path->getPath(); } const string& FilePart::leafName() const { Paranoid(!deleted()); return pathRest; } uint64 FilePart::size() const { Paranoid(!deleted()); return fileSize; } time_t FilePart::mtime() const { Paranoid(!deleted()); return fileMtime; } const MD5* FilePart::getSums(JigdoCache* c, size_t blockNr) { Paranoid(!deleted()); if (mdValid() || (blockNr == 0 && !sums.empty())) return &sums[blockNr]; else return getSumsRead(c, blockNr); } const MD5Sum* FilePart::getMD5Sum(JigdoCache* c) { Paranoid(!deleted()); if (mdValid()) return &md5Sum; else return getMD5SumRead(c); } const RsyncSum64* FilePart::getRsyncSum(JigdoCache* c) { Paranoid(!deleted()); if (!rsyncValid()) { if (getSumsRead(c, 0) == 0) return 0; } Paranoid(rsyncValid()); return &rsyncSum; } void FilePart::markAsDeleted(JigdoCache* c) { fileSize = 0; sums.resize(0); --(c->nrOfFiles); } void FilePart::setFlag(Flags f) { flags = static_cast(static_cast(flags) | static_cast(f)); } void FilePart::clearFlag(Flags f) { flags = static_cast(static_cast(flags) & ~static_cast(f)); } //________________________________________ void JigdoCache::setReadAmount(size_t bytes) { if (bytes < 64*1024) bytes = 64*1024; readAmount = bytes; } template void JigdoCache::readFilenames(RecurseDir& rd) { string name; while (true) { bool status = rd.getName(name, &fileInfo, checkFiles); // Might throw error if (status == FAILURE) return; // No more names off_t stSize = fileInfo.st_size; # if HAVE_LIBDB if (!checkFiles) { const byte* data; size_t dataSize; try { if (cacheFile->findName(data, dataSize, name, stSize, fileInfo.st_mtime).failed()) continue; } catch (DbError e) { string err = subst(_("Error accessing cache: %1"), e.message); reporter.error(err); } } # endif if (stSize == 0) continue; // Skip zero-length files addFile(name); } } JigdoCache::iterator JigdoCache::begin() { list::iterator i = files.begin(); while (i != files.end() && i->deleted()) ++i; return iterator(this, i); } JigdoCache::iterator& JigdoCache::iterator::operator++() { list::iterator end = cache->files.end(); do ++part; while (part != end && part->deleted()); return *this; } //______________________________________________________________________ template LocationPath::LocationPath(const StringP& p, const StringL& l, const StringU& u) : path(p), label(l), uri(u) { //path.reserve(0); label.reserve(0); uri.reserve(0); } //________________________________________ /* NB: If an entry with the given path already exists, we must not delete it and create another, because it is referenced from at least one FilePart. Because of our version of LocationPath::operator<, only the path matters during the find(). */ template const LocationPathSet::iterator JigdoCache::addLabel(const StringP& path, const StringL& label, const StringU& uri) { LocationPath tmp(path, label, uri); if (!tmp.path.empty() && tmp.path[tmp.path.length() - 1] != DIRSEP) tmp.path += DIRSEP; LocationPathSet::iterator i = locationPaths.find(tmp); if (i == locationPaths.end()) { return locationPaths.insert(tmp).first; // Create new entry } else { implicit_cast(i->label) = tmp.label; // Overwrite old entry implicit_cast(i->uri) = tmp.uri; return i; } } #endif jigdo-0.7.3/src/serialize.hh0000644000175000017500000001571410262207206015533 0ustar richardrichard/* $Id: serialize.hh,v 1.5 2005/07/04 10:25:10 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ /** @file Convert objects into byte streams and vice versa Classes that support serialization should implement the indicated member functions; serialize(), unserialize() and serialSizeOf(). It is assumed that 'byte' has been typdef'd to an unsigned type which represents one byte.
  MyClass {
    template
    inline Iterator serialize(Iterator i) const;
    template
    inline ConstIterator unserialize(ConstIterator i);
    inline size_t serialSizeOf() const;
  };
*/ #ifndef SERIALIZE_HH #define SERIALIZE_HH #include #include /* byte, size_t */ //______________________________________________________________________ /** Store serialized object via iterator. The iterator could be byte* or vector::iterator or SerialOstreamIterator - anything in which you can store bytes. There must be enough room to store serialSizeOf() bytes, implementers of serialize() need not check this. The implementation of serialize() must both modify i and return its value after the last modification. */ template inline Iterator serialize(const Object& o, Iterator i) { return o.serialize(i); } /** Assign the contents of the byte stream pointed to by i to the object. template */ template inline ConstIterator unserialize(Object& o, ConstIterator i) { return o.unserialize(i); } /** Return number of bytes needed by this object when serialized. If a whole tree of objects is serialized, this may include the accumulated serialized sizes of the child objects, too. */ template inline size_t serialSizeOf(const Object& o) { return o.serialSizeOf(); } //______________________________________________________________________ /** Slight variation (and simplification) of std::istream_iterator and std::ostream_iterator. You *MUST* use this when reading binary data for (un)serializing. The reason why the ordinary iterator does not work is that it uses "mystream >> byteToRead" which skips over whitespace (!) - this one uses "mystream.get(byteToRead)". Additionally, because it makes use of peek() this implementation allows mixing of Serial*streamIterator with calls to read() on the respective stream, without any problems. (This does not work with GCC's version, which uses get().) */ class SerialIstreamIterator { public: typedef bistream istream_type; typedef byte value_type; typedef const byte* pointer; typedef const byte& reference; SerialIstreamIterator() : stream(0), val(0) { } SerialIstreamIterator(istream_type& s) : stream(&s), val(0) { } SerialIstreamIterator& operator++() { stream->get(); return *this; } SerialIstreamIterator operator++(int) { SerialIstreamIterator i = *this; stream->get(); return i; } reference operator*() const { val = static_cast(stream->peek()); return val; } pointer operator->() const { val = static_cast(stream->peek()); return &val; } private: istream_type* stream; mutable byte val; }; //____________________ /** @see SerialIstreamIterator */ class SerialOstreamIterator { public: typedef bostream ostream_type; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; SerialOstreamIterator(ostream_type& s) : stream(&s) { } SerialOstreamIterator& operator=(const byte val) { stream->put(val); return *this; } SerialOstreamIterator& operator*() { return *this; } SerialOstreamIterator& operator++() { return *this; } SerialOstreamIterator& operator++(int) { return *this; } private: ostream_type* stream; }; //______________________________________________________________________ // Serializations of common data types. Stores in little-endian. //________________________________________ /** @name Numeric types - append the number of bytes to use (e.g. 4 for 32-bit) */ //@{ template inline Iterator serialize1(NumType x, Iterator i) { *i = x & 0xff; ++i; return i; } template inline ConstIterator unserialize1(NumType& x, ConstIterator i) { x = static_cast(*i); ++i; return i; } template inline Iterator serialize2(NumType x, Iterator i) { *i = x & 0xff; ++i; *i = (x >> 8) & 0xff; ++i; return i; } template inline ConstIterator unserialize2(NumType& x, ConstIterator i) { x = static_cast(*i); ++i; x |= static_cast(*i) << 8; ++i; return i; } template inline Iterator serialize4(NumType x, Iterator i) { *i = x & 0xff; ++i; *i = (x >> 8) & 0xff; ++i; *i = (x >> 16) & 0xff; ++i; *i = (x >> 24) & 0xff; ++i; return i; } template inline ConstIterator unserialize4(NumType& x, ConstIterator i) { x = static_cast(*i); ++i; x |= static_cast(*i) << 8; ++i; x |= static_cast(*i) << 16; ++i; x |= static_cast(*i) << 24; ++i; return i; } template inline Iterator serialize6(NumType x, Iterator i) { *i = x & 0xff; ++i; *i = (x >> 8) & 0xff; ++i; *i = (x >> 16) & 0xff; ++i; *i = (x >> 24) & 0xff; ++i; *i = (x >> 32) & 0xff; ++i; *i = (x >> 40) & 0xff; ++i; return i; } template inline ConstIterator unserialize6(NumType& x, ConstIterator i) { x = static_cast(*i); ++i; x |= static_cast(*i) << 8; ++i; x |= static_cast(*i) << 16; ++i; x |= static_cast(*i) << 24; ++i; x |= static_cast(*i) << 32; ++i; x |= static_cast(*i) << 40; ++i; return i; } template inline Iterator serialize8(NumType x, Iterator i) { *i = x & 0xff; ++i; *i = (x >> 8) & 0xff; ++i; *i = (x >> 16) & 0xff; ++i; *i = (x >> 24) & 0xff; ++i; *i = (x >> 32) & 0xff; ++i; *i = (x >> 40) & 0xff; ++i; *i = (x >> 48) & 0xff; ++i; *i = (x >> 56) & 0xff; ++i; return i; } template inline ConstIterator unserialize8(NumType& x, ConstIterator i) { x = static_cast(*i); ++i; x |= static_cast(*i) << 8; ++i; x |= static_cast(*i) << 16; ++i; x |= static_cast(*i) << 24; ++i; x |= static_cast(*i) << 32; ++i; x |= static_cast(*i) << 40; ++i; x |= static_cast(*i) << 48; ++i; x |= static_cast(*i) << 56; ++i; return i; } //@} //______________________________________________________________________ #endif jigdo-0.7.3/src/torture.cc0000644000175000017500000005074610261546437015255 0ustar richardrichard/* $Id: torture.cc,v 1.9 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. A "crashme" for jigdo: Throws test data at it. ./torture Will always create the same test case for the same . *Only* creates the files, doesn't work on them. ./torture Create a range of test cases *and run a MkTemplate op on them*, checking the result for correctness. Next, re-creates the image, then checks the created image's MD5Sum. Causes no end of disc thrashing - using a RAM disc is *highly* recommended for longer runs. Creates many files named `ironmaiden/part' and one file `ironmaiden/image', runs a MkTemplate::run() over them and lets it write `ironmaiden/image.template'. During the run(), the information on which files matched must be the same as calculated. After the run(), a verify() is done to check whether the template is really OK. Sorry, this is a quick hack. In very rare cases, this will incorrectly report a failure because it doesn't take into account that concatenated parts of an all-zeroes file may be large enough for the whole file to fit. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // mingw doesn't define SSIZE_MAX (maximum amount to read() at a time) #ifndef SSIZE_MAX # include # define SSIZE_MAX (UINT_MAX) #endif LOCAL_DEBUG_UNIT("torture") //______________________________________________________________________ /* I sometimes run torture on machines on which 'niced', the nice daemon, is running. It can be used to stop/kill processes which need unusually much memory/CPU time. If you define CHEAT_MR_NICE=1, torture will change its pid every so often (by forking, then exiting in the parent), which prevents its being stopped/killed by niced. Use at own risk, and please ensure you're not violating usage terms by cheating 'niced'. */ #ifndef CHEAT_MR_NICE # define CHEAT_MR_NICE 0 #endif #define TORTURE_DIR "ironmaiden" DIRSEPS string cacheFile; //______________________________________________________________________ #if HAVE_MMAP # include #else // Dummy implementation of mmap which actually loads the data into memory void* mmap(void *start, size_t length, int /*prot*/, int /*flags*/, int fd, off_t offset) { Assert(start == 0 && offset == 0); byte* buf = new byte[length]; byte* bufPtr = buf; size_t toRead = length; while (toRead > 0) { size_t didRead = read(fd, bufPtr, (toRead < SSIZE_MAX ? toRead : SSIZE_MAX)); if (didRead == 0) break; toRead -= didRead; bufPtr += didRead; } return buf; } // Dummy munmap frees the memory int munmap(void *start, size_t /*length*/) { delete[] static_cast(start); return 0; } #define PROT_READ 0 #define MAP_SHARED 0 #endif //______________________________________________________________________ namespace { # if CHEAT_MR_NICE # include pid_t parentPid; /* Calling this may have the side-effect of closing all file streams! */ void cheatMrNice() { pid_t pid = fork(); if (pid == -1 || pid == 0) return; // if error or child //cerr << " [changed pid to " << pid << ']' << endl; if (getpid() == parentPid) pause(); // Wait until killed by last child exit(0); } # else /*________________________________________*/ inline void cheatMrNice() { } # endif //______________________________________________________________________ // approximate maximum accumulated size of files const size_t MAX_MEM = 8*1024*1024; // approximate size of created image const size_t MAX_IMAGE = 16*1024*1024; void update(MD5Sum& md, uint32 x) { md.update(static_cast(x)); md.update(static_cast(x >> 8)); md.update(static_cast(x >> 16)); md.update(static_cast(x >> 24)); } struct Rand { MD5Sum md; struct { uint32 nr; uint32 serial; MD5 r; } hashData; byte* rptr; // points to one of hashData.r's elements byte* rend; uint32 res; // Bit reservoir size_t bitsInRes; bool msg; Rand(uint32 nr, bool printMessages = false) { hashData.nr = nr; hashData.serial = 0; hashData.r.clear(); rptr = rend = &hashData.r.sum[0] + 16; res = 0; bitsInRes = 0; msg = printMessages; } // Create another 128 semi-random bits in md void thumbScrew(); // Return n semi-random bits, n <= 24 uint32 get(size_t n) { while (bitsInRes < n) { if (rptr == rend) thumbScrew(); res |= (*rptr++) << bitsInRes; bitsInRes += 8; } uint32 r = res & ((1 << n) - 1); res >>= n; bitsInRes -= n; return r; } // Return an integer in the range 0...n-1 uint32 rnd(size_t n) { return static_cast( static_cast(get(24)) * n / 0x1000000); } }; void Rand::thumbScrew() { md.reset(); update(md, hashData.nr); update(md, hashData.serial); md.update(&hashData.r.sum[0], 16 * sizeof(byte)); md.finishForReuse(); hashData.r = md; ++hashData.serial; rptr = &hashData.r.sum[0]; //cout << '<' << hashData.r << '>' << endl; } //______________________________________________________________________ class File { public: explicit File(const char* fileName, size_t s = 0, size_t n = 0); File() : data(0) { } inline File(const File& f); inline File& operator=(const File& f); inline ~File(); size_t size; size_t nr; byte* data; private: int* refCount; }; struct Match { Match(uint64 o = 0, size_t n = 0) : off(o), nr(n) { } uint64 off; // Offset in image of match size_t nr; // Nr of file that matched }; vector files; uint32 lastFilesNr = 1; // Nr of test case vector imageMatches; // matches written with last call to mkimage() //____________________ File::File(const char* fileName, size_t s, size_t n) : size(s), nr(n) { refCount = new int; *refCount = 1; int fd = open(fileName, O_RDONLY); if (fd == -1) { cerr << "Couldn't open " << fileName << " (" << strerror(errno) << ')' << endl; abort(); } // mmap the file data = reinterpret_cast(mmap(0, s, PROT_READ, MAP_SHARED, fd, 0)); if (data == ((byte*)-1)) { cerr << "Couldn't mmap (" << strerror(errno) << ')' << endl; abort(); } //cerr << "mmapped " << fileName << " at " << (void*)data << "..." // << (void*)(data+s) << endl; close(fd); } File& File::operator=(const File& f) { if (this == &f) return *this; if (data != 0 && --*refCount == 0) { munmap(reinterpret_cast(data), size); //cerr << "munmapped " << (void*)data << endl; delete refCount; } size = f.size; nr = f.nr; data = f.data; refCount = f.refCount; if (data != 0) ++*refCount; return *this; } File::File(const File& f) { size = f.size; nr = f.nr; data = f.data; refCount = f.refCount; if (data != 0) ++*refCount; } File::~File() { if (data != 0 && --*refCount == 0) { munmap(reinterpret_cast(data), size); //cerr << "munmapped " << (void*)data << endl; delete refCount; } } //____________________ void mkfiles(uint32 nr) { nr &= ~127; // Files only change every 128 test cases if (lastFilesNr == nr) return; lastFilesNr = nr; Rand rand(nr); size_t mem = 0; files.resize(0); //int zeroFile = -1; // Only one file of zeroes per test case // Disable all-zeroes file - causes too many false FAIL messages int zeroFile = 0; ifstream rev(TORTURE_DIR "rev"); if (rev) { uint32 revNr; if ((rev >> revNr) && revNr == nr) { size_t size; while (rev >> size) { cerr << "Loading file #" << files.size() << ", " << size << " bytes" << '\n'; string name(TORTURE_DIR "part"); append(name, files.size()); files.push_back(File(name.c_str(), size, files.size())); } return; } } rev.close(); cheatMrNice(); ofstream orev(TORTURE_DIR "rev"); static const size_t BUF_SIZE = 65536; byte buf[BUF_SIZE]; orev << nr << ' '; while (mem < MAX_MEM) { // Add another file. Size usually 512 0 && o) { size_t n = (sizeLeft < BUF_SIZE ? sizeLeft : BUF_SIZE); if (!zeroes) for (size_t i = 0; i < n; ++i) buf[i] = rand.get(8); writeBytes(o, buf, n); if (!o) cerr << "Argh - write() failed! (" << strerror(errno) << ')' << endl; sizeLeft -= n; } // 1 out of 8 files contains (parts of) other files int subparts = 0; if (files.size() > 1 && rand.get(3) == 0) { ++subparts; // likelihood of further parts decreases exponentially while (rand.get(1) == 0) ++subparts; } // Overwrite parts with parts of other files, but never with whole file int prevOtherFile = -1; for (int n = 0; n < subparts; ++n) { int otherFile; while ((otherFile = rand.rnd(files.size())) == prevOtherFile) { } size_t otherOff = (rand.get(4) == 0 ? 0 : rand.rnd(1 + rand.rnd(size - 256))); size_t otherLen = (size_t)rand.rnd(min(size - otherOff, files[otherFile].size) - 1); cerr << "Will overwrite with " << otherLen << " bytes from file #" << otherFile << " at offset " << otherOff << '\n'; //cerr << (void*)files[otherFile].data << "..." // << (void*)(files[otherFile].data+otherLen) << endl; o.seekp(static_cast(otherOff), ios::beg); writeBytes(o, files[otherFile].data, otherLen); if (!o) cerr << "Aargh - write() failed! (" << strerror(errno) << ')' << endl; prevOtherFile = otherFile; } o.close(); // Now mmap the file we just wrote files.push_back(File(name.c_str(), size, files.size())); mem += size; } } //______________________________________________________________________ void mkimage(uint32 nr) { Rand rand(nr); cheatMrNice(); bofstream img(TORTURE_DIR "image", ios::binary); ofstream info(TORTURE_DIR "image"EXTSEPS"info"); size_t mem = 0; imageMatches.resize(0); while (mem < MAX_IMAGE) { uint32 x = rand.get(8); if (x < 85) { // an area of random data size_t size = static_cast(rand.get(18)); for (size_t i = 0; i < size; ++i) img.put(static_cast(rand.get(8) ^ 0xff)); mem += size; } else if (x < 2*85) { // a complete file int i = static_cast(rand.rnd(files.size())); info << mem << ' ' << i << '\n'; writeBytes(img, files[i].data, files[i].size); imageMatches.push_back(Match(mem, i)); debug("%1: Complete: #%2", mem, i); mem += files[i].size; } else { // an incomplete file (some bytes missing at the end) int i = static_cast(rand.rnd(files.size())); size_t size = static_cast(rand.rnd(files[i].size-1)); debug("%1: Incomplete: %2 bytes from #%3", mem, size, i); writeBytes(img, &files[i].data[0], size); mem += size; } } } //______________________________________________________________________ struct TortureReport : public MkTemplate::ProgressReporter, public JigdoDesc::ProgressReporter, public MD5Sum::ProgressReporter, public JigdoConfig::ProgressReporter { virtual ~TortureReport() { } virtual void matchFound(const FilePart* file, uint64 offInImage) { size_t n = 0; const string& leaf = file->leafName(); for (string::const_iterator i = leaf.begin(), e = leaf.end(); i != e; ++i) if (*i >= '0' && *i <= '9') n = 10 * n + (*i - '0'); else n = 0; matches.push_back(Match(offInImage, n)); } void info(const string&) { } vector matches; }; //______________________________________________________________________ } // namespace //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc < 2 || argc > 4) { cout << "Syntax: " << argv[0] << " to create files for " "one test case\n " << argv[0] << " " "[debugOptions] to create test cases [1..2) AND RUN THEM" << endl; exit(2); } if (argc == 2) { uint32 nr = static_cast(atol(argv[1])); mkfiles(nr); mkimage(nr); return 0; } else { # if CHEAT_MR_NICE parentPid = getpid(); # endif uint32 nr1 = static_cast(atol(argv[1])); uint32 nr2 = static_cast(atol(argv[2])); if (argc == 4) Logger::scanOptions(argv[3], argv[0]); ofstream report(TORTURE_DIR "report"); bool returnStatus = true; for (uint32 tc = nr1; tc < nr2; ++tc) { Rand rand(tc ^ 0x1234); mkfiles(tc); // NB will only generate new files every 128 test cases cerr << "==================== TEST CASE #" << tc << ", " << files.size() << " files ====================" << endl; mkimage(tc); // Run MkTemplate operation over it // Try default parameters plus 2 other random cases size_t blockLen = 4096; size_t md5BlockLen = 128*1024U - 55; size_t readAmount = 128U*1024; for (int i = 0; i < 2; ++i) { cerr << " case=" << tc << static_cast('a' + i) << " blockLen=" << blockLen << " md5BlockLen=" << md5BlockLen << " readAmount=" << readAmount << endl; cheatMrNice(); TortureReport reporter; bifstream image(TORTURE_DIR "image", ios::binary); auto_ptr cfDel(new ConfigFile()); JigdoConfig jc(TORTURE_DIR "image"EXTSEPS"jigdo", cfDel.release(), reporter); bofstream templ(TORTURE_DIR "image"EXTSEPS"template", ios::binary); Assert(templ); RecurseDir fileNames; for (size_t i = 0; i < files.size(); ++i) { // Add files string f(TORTURE_DIR "part"); append(f, i); fileNames.addFile(f); } JigdoCache cache(cacheFile, 60*60*24, readAmount); cache.setParams(blockLen, md5BlockLen); while (true) { try { cache.readFilenames(fileNames); } catch (RecurseError e) { cerr << e.message << endl; continue; } break; } //____________________ // CREATE TEMPLATE MkTemplate op(&cache, &image, &jc, &templ, reporter, 0, readAmount); op.run("image", "image"EXTSEPS"template"); // Write out reported offsets for comparison with image.info ofstream imageReport(TORTURE_DIR "image"EXTSEPS"reported"); for (vector::iterator i = reporter.matches.begin(), e = reporter.matches.end(); i != e; ++i) imageReport << i->off << ' ' << i->nr << endl; // Check whether reported offsets are correct /* To see the complete set of differences between the files that were in the image and the files that mktemplate /reported/ to be there, use "diff ironmaiden/image.info ironmaiden/image.reported". */ bool offsetCheckOK = true; bool allChecksOK = true; vector::iterator j = reporter.matches.begin(); for (vector::iterator i = imageMatches.begin(), e = imageMatches.end(); i != e && offsetCheckOK; ++i) { if (files[i->nr].size < blockLen) continue; if (j == reporter.matches.end() || i->off != j->off || i->nr != j->nr) { cerr << "(info: " << i->off << ' ' << i->nr << ") != (reported: " << j->off << ' ' << j->nr << ')' << endl; offsetCheckOK = false; break; } ++j; } if (offsetCheckOK) { cerr << "OK make-template" << endl; } else { cerr << "FAIL make-template" << endl; allChecksOK = false; returnStatus = FAILURE; } // Write jigdo ofstream jigdo(TORTURE_DIR "image"EXTSEPS"jigdo", ios::binary); jigdo << jc.configFile(); image.close(); jigdo.close(); templ.close(); //____________________ // RE-CREATE IMAGE cheatMrNice(); bifstream templIn(TORTURE_DIR "image"EXTSEPS"template", ios::binary); bool mkImageOK = true; try { if (JigdoDesc::makeImage(&cache, string(TORTURE_DIR "image"EXTSEPS"out"), string(TORTURE_DIR "image"EXTSEPS"tmp"), string(TORTURE_DIR "image"EXTSEPS"template"), &templIn, true, reporter, readAmount, true) > 0) mkImageOK = false; } catch (Error e) { cerr << e.message << endl; mkImageOK = false; returnStatus = FAILURE; } if (mkImageOK) { cerr << "OK make-image" << endl; } else { cerr << "FAIL make-image" << endl; allChecksOK = false; returnStatus = FAILURE; } //____________________ // VERIFY CREATED IMAGE bool verifyOK = false; JigdoDescVec contents; JigdoDesc::ImageInfo* info; templIn.close(); cheatMrNice(); templIn.open(TORTURE_DIR "image"EXTSEPS"template", ios::binary); try { if (JigdoDesc::isTemplate(templIn) == false) cerr << "not a template file?!" << endl; JigdoDesc::seekFromEnd(templIn); templIn >> contents; if (!templIn) cerr << "couldn't read from template" << endl; info = dynamic_cast(contents.back()); if (info == 0) cerr << "verify: Invalid template data - corrupted file?" << endl; } catch (JigdoDescError e) { cerr << e.message << endl; info = 0; } MD5Sum md; // MD5Sum of image bifstream imageVer(TORTURE_DIR "image"EXTSEPS"out", ios::binary); md.updateFromStream(imageVer, info->size(), readAmount, reporter); md.finish(); if (info != 0 && imageVer) { imageVer.get(); if (imageVer.eof() && md == info->md5()) verifyOK = true; } if (verifyOK) { cerr << "OK verify" << endl; } else { cerr << "FAIL verify" << endl; allChecksOK = false; returnStatus = FAILURE; } templIn.close(); //____________________ report << (allChecksOK ? "OK" : "FAIL") << " case=" << tc << ", blockLen=" << blockLen << ", md5BlockLen=" << md5BlockLen << ", readAmount=" << readAmount << endl; blockLen = 1024 + rand.get(16); md5BlockLen = 1024 + rand.get(18); if (md5BlockLen <= blockLen) md5BlockLen = blockLen + 1; readAmount = 16384 + rand.get(19); } } # if CHEAT_MR_NICE // Kill parent process if (getpid() != parentPid) { kill(parentPid, SIGINT); } # endif if (returnStatus) return 0; else return 1; } } jigdo-0.7.3/src/util/autonullptr-test.cc0000644000175000017500000000205010120166076020044 0ustar richardrichard/* $Id: autonullptr-test.cc,v 1.2 2004/09/09 23:50:22 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. A pointer which gets set to null if the pointed-to object is deleted #test-deps */ #include #include //______________________________________________________________________ namespace { struct X : public AutoNullPtrBase { int memb; }; } int main() { X* x = new X(); AutoNullPtr ptr(x); Assert(ptr.get() == x); Assert(ptr == x); Assert(ptr); // non-null Assert(ptr != 0); // non-null (*ptr).memb = 0; Assert(ptr->memb == 0); { AutoNullPtr ptr2 = ptr; Assert(ptr2 == x && ptr2 == ptr); delete x; Assert(ptr == 0); Assert(ptr2 == 0); x = new X(); ptr = x; } // Destroy ptr2 delete x; return 0; } jigdo-0.7.3/src/util/autonullptr.hh0000644000175000017500000001046210262207206017105 0ustar richardrichard/* $Id: autonullptr.hh,v 1.5 2005/07/04 10:25:10 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file A pointer which gets set to null if the pointed-to object is deleted. Somewhat equivalent to "weak references" in Java.
  class MyClass : public AutoNullPtrBase {
    ... your class members here ...
  } myClass;
  AutoNullPtr ptr = &myClass;
Implementation: AutoNullPtrBase contains the head of a linked list of AutoNullPtrs, sets them all to 0 from its dtor. ~AutoNullPtr removes itself from that list. */ #ifndef AUTONULLPTR_HH #define AUTONULLPTR_HH #include //______________________________________________________________________ template class AutoNullPtrBase; /** A pointer which gets set to null if the pointed-to object is deleted */ template class AutoNullPtr : public IListBase { public: AutoNullPtr() : IListBase(), p(0) { } AutoNullPtr(T* ptr) : IListBase(), p(ptr) { addSelf(); } AutoNullPtr(const AutoNullPtr& b) : IListBase(), p(b.get()) { addSelf(); } ~AutoNullPtr() { iList_remove(); } AutoNullPtr& operator=(const AutoNullPtr& b) { iList_remove(); p = b.get(); addSelf(); return *this; } AutoNullPtr& operator=(T* ptr) { iList_remove(); p = ptr; addSelf(); return *this; } T* get() const { return p; } T& operator*() const { return *p; } T* operator->() const { return p; } operator bool() const { return p != 0; } private: void addSelf() { if (p != 0) p->list.push_back(*this); } T* p; }; //______________________________________________________________________ /** Derive from this class to make your class instances referenceable by AutoNullPtr */ template class AutoNullPtrBase { public: AutoNullPtrBase() { } ~AutoNullPtrBase() { while (!list.empty()) list.front() = 0; } private: friend class AutoNullPtr; IList > list; }; //______________________________________________________________________ /** @name The regular pointer tests */ //@{ template inline bool operator==(const AutoNullPtr& a, const T* b) { return a.get() == b; } template inline bool operator==(const T* b, const AutoNullPtr& a) { return a.get() == b; } template inline bool operator==(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() == b.get(); } template inline bool operator!=(const AutoNullPtr& a, const T* b) { return a.get() != b; } template inline bool operator!=(const T* b, const AutoNullPtr& a) { return a.get() != b; } template inline bool operator!=(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() != b.get(); } template inline bool operator<(const AutoNullPtr& a, const T* b) { return a.get() < b; } template inline bool operator<(const T* b, const AutoNullPtr& a) { return a.get() < b; } template inline bool operator<(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() < b.get(); } template inline bool operator>(const AutoNullPtr& a, const T* b) { return a.get() > b; } template inline bool operator>(const T* b, const AutoNullPtr& a) { return a.get() > b; } template inline bool operator>(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() > b.get(); } template inline bool operator<=(const AutoNullPtr& a, const T* b) { return a.get() <= b; } template inline bool operator<=(const T* b, const AutoNullPtr& a) { return a.get() <= b; } template inline bool operator<=(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() <= b.get(); } template inline bool operator>=(const AutoNullPtr& a, const T* b) { return a.get() >= b; } template inline bool operator>=(const T* b, const AutoNullPtr& a) { return a.get() >= b; } template inline bool operator>=(const AutoNullPtr& a, const AutoNullPtr& b) { return a.get() >= b.get(); } //@} //______________________________________________________________________ #endif jigdo-0.7.3/src/util/autoptr.hh0000644000175000017500000000274610226060300016210 0ustar richardrichard/* $Id: autoptr.hh,v 1.2 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file A variant of auto_ptr for pointers to arrays */ #ifndef AUTOPTR_HH #define AUTOPTR_HH /** A variant of std::auto_ptr for pointers to arrays. Aside note: Does not implement the "const auto_ptr copy protection". */ template class ArrayAutoPtr { public: typedef X element_type; explicit ArrayAutoPtr(X* p = 0) throw() : ptr(p) { } ArrayAutoPtr(ArrayAutoPtr& a) throw() : ptr(a.release()) { } template ArrayAutoPtr(ArrayAutoPtr& a) throw() : ptr(a.release()) { } ArrayAutoPtr& operator=(ArrayAutoPtr& a) throw() { if (&a != this) { delete[] ptr; ptr = a.release(); } return *this; } template ArrayAutoPtr& operator=(ArrayAutoPtr& a) throw() { if (a.get() != this->get()) { delete[] ptr; ptr = a.release(); } return *this; } ~ArrayAutoPtr() throw() { delete[] ptr; } X& operator*() const throw() { return *ptr; } X* operator->() const throw() { return ptr; } X* get() const throw() { return ptr; } X* release() throw() { X* tmp = ptr; ptr = 0; return tmp; } void reset(X* p = 0) throw() { delete[] ptr; ptr = p; } private: X* ptr; }; #endif jigdo-0.7.3/src/util/bstream-counted.hh0000644000175000017500000000142010226060300017572 0ustar richardrichard/* $Id: bstream-counted.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Reference-counted bfstream */ #ifndef BSTREAM_COUNTED_HH #define BSTREAM_COUNTED_HH #include #include #include #include /** A bfstream which can be referenced with a SmartPtr. */ class BfstreamCounted : virtual public SmartPtrBase, public bfstream { public: BfstreamCounted(const char* name, ios::openmode mode) : bfstream(name, mode) { } }; #endif jigdo-0.7.3/src/util/bstream.cc0000644000175000017500000000434110164123562016141 0ustar richardrichard/* $Id: bstream.cc,v 1.2 2004/12/28 00:23:14 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. I/O streams for bytes (byte is unsigned char, not regular char) */ #include #include #include #if !HAVE_WORKING_FSTREAM bistream bcin(stdin); bostream bcout(stdout); bostream& bostream::seekp(off_t off, ios::seekdir dir) { if (fail()) return *this; int whence; if (dir == ios::beg) whence = SEEK_SET; else if (dir == ios::end) whence = SEEK_END; else if (dir == ios::cur) whence = SEEK_CUR; else { Assert(false); whence = SEEK_SET; } /*int r =*/ fseeko(f, off, whence); // Fails: Assert((r == -1) == (ferror(f) != 0)); return *this; } bistream& bistream::seekg(off_t off, ios::seekdir dir) { if (fail()) return *this; int whence; if (dir == ios::beg) whence = SEEK_SET; else if (dir == ios::end) whence = SEEK_END; else if (dir == ios::cur) whence = SEEK_CUR; else { Assert(false); whence = SEEK_SET; } /*int r =*/ fseeko(f, off, whence); // Fails: Assert((r == -1) == (ferror(f) != 0)); return *this; } void bistream::getline(string& l) { gcountVal = 0; l.clear(); while (true) { int c = fgetc(f); if (c >= 0) ++gcountVal; if (c == EOF || c == '\n') break; l += static_cast(c); } } void bofstream::open(const char* name, ios::openmode m) { Paranoid((m & ios::binary) != 0 && f == 0); Paranoid((m & ios::ate) == 0); if ((m & ios::trunc) != 0) { f = fopen(name, "w+b"); return; } // Open existing file for reading and writing f = fopen(name, "r+b"); if (f == NULL && errno == ENOENT) f = fopen(name, "w+b"); // ...or create new, empty file } bfstream::bfstream(const char* name, ios::openmode m) : biostream() { Paranoid((m & ios::binary) != 0); Paranoid((m & ios::ate) == 0); if ((m & ios::out) == 0) f = fopen(name, "rb"); else if ((m & ios::trunc) != 0) f = fopen(name, "w+b"); else f = fopen(name, "r+b"); } #endif jigdo-0.7.3/src/util/bstream.hh0000644000175000017500000001262110226060300016140 0ustar richardrichard/* $Id: bstream.hh,v 1.19 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file I/O streams for bytes (byte is unsigned char, not regular char). This was first solved with typedefs like "typedef basic_istream bistream;". That turns out to be difficult, though, since you need to supply your own implementation for char_traits and basic_fstream. The current typedefs aren't very useful except to indicate in the source: "This is /intended/ to be used for binary data, not text". */ #ifndef BSTREAM_HH #define BSTREAM_HH #include //#include /* Needed for fstream prototypes! */ #include #include #include #include //____________________ #if HAVE_WORKING_FSTREAM typedef istream bistream; typedef ostream bostream; typedef iostream biostream; typedef ifstream bifstream; typedef ofstream bofstream; typedef fstream bfstream; #else //____________________ /* With GCC 3.x (x < 4), libstdc++ is broken for files >2GB. We need to re-implement the basic functionality to work around this. :-/ */ #if !defined _FILE_OFFSET_BITS || _FILE_OFFSET_BITS != 64 # warning "_FILE_OFFSET_BITS not defined, big file support may not work" #endif /* The subtle differences between bad() and fail() are not taken into account here - beware! */ class bios { public: operator void*() const { return fail() ? (void*)0 : (void*)(-1); } int operator!() const { return fail(); } bool fail() const { return f == 0 || feof(f) != 0 || ferror(f) != 0; } //Incorrect: bool bad() const { return fail(); } bool eof() const { return f == 0 || feof(f) != 0; } bool good() const { return !fail(); } // Incorrect? void close() { if (f != 0) fclose(f); f = 0; } ~bios() { if (f != 0) fclose(f); } protected: bios() : f(0) { } bios(FILE* stream) : f(stream) { } FILE* f; }; class bostream : virtual public bios { public: inline int put(int c) { return putc(c, f); } bostream& seekp(off_t off, ios::seekdir dir = ios::beg); inline bostream& write(const char* p, streamsize n); bostream(FILE* stream) : bios(stream) { } protected: bostream() { } }; class bistream : virtual public bios { public: inline int get(); inline int peek(); bistream& seekg(off_t off, ios::seekdir dir = ios::beg); inline uint64 tellg() const; void getline(string& l); inline bistream& read(char* p, streamsize n); inline uint64 gcount() { uint64 r = gcountVal; gcountVal = 0; return r; } inline void sync() const { } // NOP bistream(FILE* stream) : bios(stream) { } protected: bistream() : gcountVal(0) { } private: uint64 gcountVal; }; extern bistream bcin; extern bostream bcout; class biostream : virtual public bistream, virtual public bostream { protected: biostream() : bistream(), bostream() { } biostream(FILE* stream) : bistream(stream), bostream(stream) { } }; //____________________ class bifstream : public bistream { public: bifstream(FILE* stream) : bistream(stream) { Paranoid(stream == stdin); } inline bifstream(const char* name, ios::openmode m = ios::in); inline void open(const char* name, ios::openmode m = ios::in); }; class bofstream : public bostream { public: bofstream(FILE* stream) : bostream(stream) { Paranoid(stream == stdout); } inline bofstream(const char* name, ios::openmode m = ios::out); void open(const char* name, ios::openmode m = ios::out); }; class bfstream : public biostream { public: bfstream(const char* path, ios::openmode); }; //____________________ int bistream::get() { int r = getc(f); gcountVal = (r >= 0 ? 1 : 0); return r; } int bistream::peek() { int r = getc(f); gcountVal = (r >= 0 ? 1 : 0); ungetc(r, f); return r; } uint64 bistream::tellg() const { return ftello(f); } bostream& bostream::write(const char* p, streamsize n) { Paranoid(f != 0); fwrite(p, 1, n, f); return *this; } bistream& bistream::read(char* p, streamsize n) { gcountVal = fread(p, 1, n, f); return *this; } inline void getline(bistream& bi, string& l) { bi.getline(l); } bifstream::bifstream(const char* name, ios::openmode m) : bistream() { open(name, m); } void bifstream::open(const char* name, ios::openmode DEBUG_ONLY_PARAM(m)) { Paranoid((m & ios::binary) != 0 && f == 0); f = fopen(name, "rb"); } bofstream::bofstream(const char* name, ios::openmode m) : bostream() { // When opened this way, any existing file is truncated open(name, m | ios::trunc); } #endif /* HAVE_WORKING_FSTREAM */ //______________________________________________________________________ // Avoid lots of ugly reinterpret_casts in the code itself inline bistream& readBytes(bistream& s, byte* buf, streamsize count) { return s.read(reinterpret_cast(buf), count); } inline biostream& readBytes(biostream& s, byte* buf, streamsize count) { s.read(reinterpret_cast(buf), count); return s; } inline bostream& writeBytes(bostream& s, const byte* buf, streamsize count) { return s.write(reinterpret_cast(buf), count); } inline biostream& writeBytes(biostream& s, const byte* buf, streamsize count) { s.write(reinterpret_cast(buf), count); return s; } #endif /* BSTREAM_HH */ jigdo-0.7.3/src/util/configfile-test.cc0000644000175000017500000000573307735400630017600 0ustar richardrichard/* $Id: configfile-test.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Access to Gnome/KDE/ini-style configuration files #test-deps util/configfile.o */ #include #include #include #include #include #include //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 1) { cerr << "Syntax: " << argv[0] << " \n" " " << argv[0] << " \n" " " << argv[0] << " " "" << endl; return 1; } ConfigFile cfg; ifstream cfgFile(argv[1]); cfgFile >> cfg; cfgFile.close(); if (argc == 2) { //________________________________________ // Go once through file and precede [subject] lines with "#" cout << "File `" << argv[1] << "' has " << cfg.size() << " lines:" << endl; for (ConfigFile::iterator i = cfg.begin(), e = cfg.end(); i != e; ++i) { if (i.isSection()) cout << '#'; else cout << '-'; cout << *i << endl; } // Output section headers cout << "Sections:" << endl; for (ConfigFile::iterator i = cfg.firstSection(), e = cfg.end(); i != e; i.nextSection()) cout << *i << endl; //________________________________________ } else if (argc == 3) { // Output all [section] lines for the section named string sectName = argv[2]; cout << "Section lines for `[" << sectName << "]':" << endl; ConfigFile::iterator i = cfg.firstSection(sectName); while (i != cfg.end()) { cout << *i << endl; i.nextSection(sectName); } //________________________________________ } else if (argc == 4) { // Output all matching label lines string sectionName = argv[2]; string labelName = argv[3]; cout << "Lines matching section `[" << sectionName << "]', label `" << labelName << '\'' << endl; size_t off; for (ConfigFile::Find f(&cfg, sectionName, labelName, &off); !f.finished(); off = f.next()) { cout << &*f.section() << ' '; // Address of section line string::iterator x = f.label()->begin(); x += off - 1; *x = '|'; // Overwrite '=' with '|' cout << *f.label() << endl; (*f.label())[off - 1] = '='; // Also split value into words vector words; ConfigFile::split(words, *f.label(), off); cout << " "; for (vector::iterator i = words.begin(), e = words.end(); i != e; ++i) cout << " \x1b[7m" << *i << "\x1b[27m"; cout << endl; } //________________________________________ } return 0; } jigdo-0.7.3/src/util/configfile.cc0000644000175000017500000003426507735400630016625 0ustar richardrichard/* $Id: configfile.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Access to Gnome/KDE/ini-style configuration files Quite simple, inefficient implementation - but more flexible WRT updating the config file before writing it back to disc. */ #include #include #include #include #include //______________________________________________________________________ void ConfigFile::ProgressReporter::error(const string& message, const size_t lineNr) { if (lineNr > 0) cerr << lineNr << ": "; cerr << message << endl; } void ConfigFile::ProgressReporter::info(const string& message, const size_t lineNr) { if (lineNr > 0) cerr << lineNr << ": "; cerr << message << endl; } ConfigFile::ProgressReporter ConfigFile::noReport; //______________________________________________________________________ ConfigFile::~ConfigFile() { Line* l = endElem.next; while (l != &endElem) { Line* tmp = l; l = l->next; delete tmp; } } //______________________________________________________________________ /* Update the linked list formed by the nextSect members after the list of sections in the file has been modified. This must be called by the user, it cannot be handled fully automatically (with a flag "needs rescan"), because the user could assign "[newsection]" anytime to any of the strings accessible via iterators. */ void ConfigFile::rescan(bool printErrors) { firstSect() = &endElem; Line** previous = &firstSect(); size_t thisLine = 0; // Current line number bool inComment = false; // true if currently inside a [Comment] section for (iterator i = begin(), e = end(); i != e; ++i) { ++thisLine; //cerr << " " << thisLine << ' ' << *i << endl; // Insert into linked list any "[sectionname]" lines string::const_iterator x = i->begin(); string::const_iterator end = i->end(); i.nextSect() = 0; // Empty line, or only contains '#' comment if (advanceWhitespace(x, end)) continue; if (*x != '[') { if (inComment || !printErrors) continue; // If printErrors, do extra syntax check for label name while (true) { if (x == end) { reporter->error(_("Label name is not followed by `='"), thisLine); break; } if (*x == '=') break; if (*x == '[' || *x == ']' || *x == '#') { string err = subst(_("Label name contains invalid character `%1'"), *x); reporter->error(err, thisLine); break; } ++x; } continue; // Next line } if (printErrors) { /* Scan label name. At the same time, check whether the name is "Comment" or "comment", ignore the whole section if it is. */ ++x; // Advance beyond the '[' if (advanceWhitespace(x, end)) { // Skip space after '[' reporter->error(_("No closing `]' for section name"), thisLine); continue; } string::const_iterator s1 = x; // s1 points to start of section name while (x != end && *x != ']' && !isWhitespace(*x) && *x != '[' && *x != '=' && *x != '#') ++x; string::const_iterator s2 = x; // s2 points to end of section name if (advanceWhitespace(x, end)) { reporter->error(_("No closing `]' for section name"), thisLine); continue; } // In special case of "Include", format differs: URL after section name if (s2 - s1 == 7 && strncmp(i->c_str() + (s1 - i->begin()), "Include", 7) == 0) { while (x != end && *x != ']') ++x; // Skip URL } if (*x != ']') { string err = subst(_("Section name invalid at character `%1'"), *x); reporter->error(err, thisLine); continue; } ++x; // Advance beyond the ']' if (!advanceWhitespace(x, end)) { reporter->error(_("Invalid characters after closing `]'"), thisLine); continue; } // Check for "comment"/"Comment" if (s2 - s1 == 7 && (*s1 == 'C' || *s1 == 'c')) { string::const_iterator s = s1; static const char* const omment = "comment"; int i = 1; do { if (*++s != omment[i]) break; ++i; } while (i < 7); inComment = (i == 7); } } // Line is a [section] line // Prepare for insertion into list of [section] lines *previous = i.p; previous = &(i.nextSect()); } *previous = &endElem; // Close ring lineCount = thisLine; } //______________________________________________________________________ ConfigFile::iterator ConfigFile::find(const string& sectName, const string& line) { iterator i = firstSection(sectName); while (i != end()) { ++i; while (!i.isSection()) { // &&!i.isEnd() unnecessary if (*i == line) return i; ++i; } if (i.p->isEnd()) return i; if (!i.isSection(sectName)) i.nextSection(sectName); } return i; } //______________________________________________________________________ istream& ConfigFile::get(istream& s) { string text; while (true) { getline(s, text); // Tolerate Doze "CRLF"-style line endings under Unix if (text[text.length() - 1] == '\r') text.resize(text.length() - 1); if (!s) break; push_back(); swap(text, back()); Paranoid(text.empty()); } rescan(true); return s; } //______________________________________________________________________ ostream& ConfigFile::put(ostream& s) const { // Too lazy to implement all the const_iterator stuff, so cast const away ConfigFile* self = const_cast(this); for (iterator i = self->begin(), e = self->end(); i != e; ++i) s << *i << '\n'; return s; } //______________________________________________________________________ bool ConfigFile::iterator::isSection(const string& sectName) const { if (!isSection()) return false; // Skip whitespace at start of line const string::const_iterator send = sectName.end(); string::const_iterator x = p->text.begin(); string::const_iterator xend = p->text.end(); bool emptyLine = advanceWhitespace(x, xend); Assert(!emptyLine); Assert(*x == '['); ++x; bool emptyAfterOpeningBracket = advanceWhitespace(x, xend); Assert(!emptyAfterOpeningBracket); // Compare section names string::const_iterator s = sectName.begin(); while (x != xend && s != send && *x == *s) { ++x; ++s; } if (s == send // End of sectName; now only ']' and whitespace may follow && !advanceWhitespace(x, xend) // advance til ']' && *x == ']') { // check for ']' ++x; bool extraCharsAfterClosingBracket = advanceWhitespace(x, xend); Assert(extraCharsAfterClosingBracket); return true; // Found matching [section] line } return false; } //______________________________________________________________________ ConfigFile::iterator& ConfigFile::iterator::nextSection( const string& sectName) { // Advance to next [section] line nextSection(); // Look for matching section name while (!p->text.empty()) { // i.e. while end() not reached if (isSection(sectName)) return *this; // No match, advance to next [section] line nextSection(); } // End of file reached, nothing found return *this; } //______________________________________________________________________ size_t ConfigFile::iterator::nextLabel(const string& labelName) { string::const_iterator x; string::const_iterator xend; const string::const_iterator lend = labelName.end(); while (true) { // Advance to next line ++*this; if (isSection()) return 0; // End of section or end of file // Next line is superfluous cos endElem.isSection() == true //if (p->isEnd()) return 0; x = p->text.begin(); xend = p->text.end(); // Skip whitespace at start of line if (advanceWhitespace(x, xend)) continue; // Skip empty lines // Compare label names string::const_iterator l = labelName.begin(); while (x != xend && l != lend && *x == *l) { ++x; ++l; } if (l == lend // End of labelName; now only '=' may follow && !advanceWhitespace(x, xend) // advance til '=' && *x == '=') // check for '=' return x - p->text.begin() + 1; // Found matching label= line } } //______________________________________________________________________ /* No full syntax check; e.g. label names may (incorrectly) contain '['. NB an empty label name is allowed. */ bool ConfigFile::iterator::setLabelOffsets(size_t& begin, size_t& end, size_t& value) { if (isSection()) return false; string::const_iterator xbeg = p->text.begin(); string::const_iterator xend = p->text.end(); string::const_iterator x = xbeg; if (advanceWhitespace(x, xend)) return false; begin = x - xbeg; end = begin; // Search forward to '=' while (x != xend) { if (*x == '=') { value = x - xbeg + 1; return true; } // When calculating end offset, ignore whitespace before the '=' if (*x != ' ' && *x != '\t') end = x - xbeg + 1; ++x; } return false; } //______________________________________________________________________ ConfigFile::Find::Find(ConfigFile* c, const string& sectName, const string& labelName, const iterator i, size_t* offset) : configFile(c), sectionStr(sectName), labelStr(labelName), sectionIter(i), rightSection(false) { --sectionIter; labelIter = sectionIter; // Special-case for implicit 0th section with empty section name if (labelIter == configFile->end() && sectionStr.empty()) rightSection = true; size_t o = next(); if (offset != 0) *offset = o; } ConfigFile::Find::Find(ConfigFile* c, const string& sectName, const string& labelName, size_t* offset) : configFile(c), sectionStr(sectName), labelStr(labelName), sectionIter(c->begin()), rightSection(false) { --sectionIter; labelIter = sectionIter; // Special-case for implicit 0th section with empty section name if (labelIter == configFile->end() && sectionStr.empty()) rightSection = true; size_t o = next(); if (offset != 0) *offset = o; } //______________________________________________________________________ /* The Find implementation uses the knowledge that for ConfigFile objects, --begin() == end() and also ++--begin() == begin(). Note that we do not strictly /need/ the additional "sectionIter" data member, but it is necessary for users to detect whether the search has jumped over from one section to the next (identically named) section, and to efficiently restart the search in the same section. */ size_t ConfigFile::Find::next() { while (true) { /* If the line pointed to by section() is not named sectName, advance both section() and label() to the next section of that name. */ if (!rightSection) { if (sectionIter.nextSection(sectionStr) == configFile->end()) { // No more sections found labelIter = configFile->end(); return 0; } labelIter = sectionIter; rightSection = true; } /* Next, advance label() to the next label called labelName. Repeat the process with further sections if no label of that name in this section. */ size_t o = labelIter.nextLabel(labelStr); if (o > 0 // Success, label found || labelIter == configFile->end()) // End of search, no more matches return o; // End of section - look for more sections with given name rightSection = false; } } //______________________________________________________________________ // Returns true if s has been set to a valid word (which have length zero) bool ConfigFile::split1Word(string* word, const string& s, string::const_iterator& e) { string::const_iterator end = s.end(); enum { none, // Outside any quotes dbl, // Inside double quotes single // Inside single quotes } state = none; // Skip whitespace while (e != end && (*e == ' ' || *e == '\t')) ++e; if (e == end || *e == '#') return false; do { if (*e == '"') { // Double quotes, enter/leave "" mode switch (state) { case none: state = dbl; break; case dbl: state = none; break; case single: *word += '"'; break; } } else if (*e == '\'') { // Single quotes, enter/leave '' mode switch (state) { case none: state = single; break; case dbl: *word += '\''; break; case single: state = none; break; } } else if (*e == ' ' || *e == '\t' || *e == '#') { // Whitespace - end word? if (state == none) return true; *word += *e; } else if (*e == '\\') { if (state != single) { ++e; if (e == end) return true; } *word += *e; } else { *word += *e; } ++e; } while (e != end); return true; } //______________________________________________________________________ string& ConfigFile::quote(string& s) { bool singleQuote = false; bool needQuotes = false; // Need to enclose s either in '' or "" size_t escNeeded = 0; // Nr of \ to insert for "" quoting for (string::const_iterator i = s.begin(), e = s.end(); i != e; ++i) { if (*i == '\'') singleQuote = true; else if (*i == ' ' || *i == '\t' || *i == '#') needQuotes = true; else if (*i == '"' || *i == '\\') ++escNeeded; } if (!singleQuote && !needQuotes && escNeeded == 0) // No quoting necessary return s; if (!singleQuote) { // Can enclose string in single quotes s.insert(s.begin(), '\''); s += '\''; return s; } // Must use double quotes and escape with \ where necessary ++escNeeded; s.insert(s.begin(), escNeeded, '"'); // Make enough room at start of s string::iterator out = s.begin() + 1; for (string::iterator i = s.begin()+escNeeded, e = s.end(); i != e; ++i) { if (*i == '"' || *i == '\\') { *out = '\\'; ++out; } *out = *i; ++out; } Paranoid(out == s.end()); s += '"'; return s; } jigdo-0.7.3/src/util/configfile.hh0000644000175000017500000004445010226060300016615 0ustar richardrichard/* $Id: configfile.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Access to Gnome/KDE/ini-style configuration files. Allow reading and writing of configuration files. The files consist of a number of sections, introduced with "[SectionName]" on a line by itself. Within each section, there are entries of the form "Label=value". Example for a .jigdo file: ________________________________________

  [Jigdo]
  Version=1.0
  Generator=jigdo-file/0.5.1

  # Comment
  [Image] # Comment
  Filename=image
  Template=http://edit.this.url/image.template
  ShortInfo=This is a CD image
  Info=Some more info about the image.
   Whee, this entry extends over more than one line!
  ,
   It even contains an empty line, above this one.
  # ^^^^^^ multi-line values UNIMPLEMENTED at the moment

  [Parts]
  QrxELOWvjQ2JgkFhlkT74w=A:ironmaiden/part88
  jKVYd3dxh68ROwI6NSQxGA=A:ironmaiden/part87
  
________________________________________ Whitespace is removed at the start of lines, to the left of the "=" in an entry line and at the start and end of a section name, but nowhere else. This means that a section name or label may contain spaces, possibly even multiple consecutive spaces. Section and label names cannot contain the characters []=# Entries appearing before the first section name are added to a 0th section named "" (empty string). Empty label names are also allowed. Comments are introduced with "#" and extend to the end of the line. They may appear after a "[SectionName]" or on a separate line, but not in entry lines (if they do, they're considered part of the entry's value). Furthermore, multi-line comments are possible because a section named [Comment] or [comment] is treated specially; errors about incorrect entries are not reported. In practice, this means that any text can be written in these sections, as long as no line begins with '['. Searches for sections/labels are case-sensitive. Multi-line entry values are not implemented ATM. */ #ifndef CONFIGFILE_HH #define CONFIGFILE_HH #include #include #include #include //______________________________________________________________________ /** General approach: Reading/changes/writing of config should be possible, and all formatting and comments made by any human editing the file should be preserved. Consequently, a ConfigFile behaves like a list simply containing the raw data as read from the file. Access is possible via a subset of the list<> methods, or higher-level methods to find sections/entries. NB: Changing/writing to disc of config not currently supported. */ class ConfigFile { public: class ProgressReporter; class iterator; inline ConfigFile(ProgressReporter& pr = noReport); ~ConfigFile(); /** This must be called after *sections* have been added/removed/ renamed, to update the list of sections present in the config file. No need to call it after insertion/deletion of lines, whitespace/comment changes of [section] lines, or any changes to entries. @param printErrors If true, perform extra syntax checks and call ProgressReporter object for syntax errors. */ void rescan(bool printErrors = false); /** Change reporter for error messages */ void setReporter(ProgressReporter& pr) { reporter = ≺ } /** Input from file, append to this. Makes a call to rescan(true). */ istream& get(istream& s); /** Output to file */ ostream& put(ostream& s) const; //______________________________ /** Return iterator to first real [section] line in the file */ inline iterator firstSection() { return iterator(*firstSect()); } /** Return iterator to first section with given name, or to end() */ inline iterator firstSection(const string& sectName); /** Standard list interface */ typedef string& reference; typedef const string& const_reference; size_t size() const { return lineCount; } bool empty() const { return size() == 0; } inline iterator begin(); //inline const_iterator begin() const; inline iterator end(); //inline const_iterator end() const; inline reference front(); //inline const_reference front() const; inline reference back(); //inline const_reference back() const; // Return iterator to first such line in section, or end() iterator find(const string& sectName, const string& line); // Insert empty line before pos inline iterator insert(iterator pos); // Insert s before pos inline iterator insert(iterator pos, const_reference s); inline iterator insert(iterator pos, const char* s); // Delete line at pos from list; pos becomes invalid inline iterator erase(iterator pos); //inline iterator erase(iterator first, iterator last); // Add line at end of list inline void push_back(); // Empty line inline void push_back(const string& s); inline void push_back(const char* s); //______________________________ /** Helper function: Advance x until it points to end or a non-space, non-tab character. Returns true if at end of string (or '#' comment). */ static inline bool advanceWhitespace(string::const_iterator& x, const string::const_iterator& end); static inline bool advanceWhitespace(string::iterator& x, const string::const_iterator& end); /// Is character a space or tab? static inline bool isWhitespace(char x) { return x == ' ' || x == '\t'; } /** Helper function, useful to post-process entry values: Given a string and an offset in it, extract the entry value (everything starting with the specified offset) and break it into whitespace-separated words, which are appended to out. This does many things that a shell does: - Allow quoting with "" or ''. Whitespace between quotes does not cause the word to be split there. - Except inside '', escaping double quote, space, # or backslash with \ is possible. - A comment can be added at the end of the line. Escapes like \\012, \\xff, \\n, \\t are *not* supported, behaviour is undefined. (Possible future extension, TODO: Allow \\ at end of line for multi-line entries?) */ template // E.g. vector; anything with push_back() static void split(Container& out, const string& s, size_t offset = 0); /** Related to above; if necessary, modifies s in such a way that it stays one word: If it contains whitespace, " or \ then enclose it in '' and if it contains ' then enclose it in "" and additionally escape other problematical characters with \. Returns reference to s. */ static string& quote(string& s); private: struct Line { Line() : prev(), next(), nextSect(0), text() { } Line(const string& s) : prev(), next(), nextSect(0), text(s) { } Line(const char* s) : prev(), next(), nextSect(0), text(s) { } // Returns true if *this is the end() element bool isEnd() const { return nextSect != 0 && text.empty(); } Line* prev; Line* next; // Linked ring of [section] lines, or null for non-section lines Line* nextSect; string text; }; ProgressReporter* reporter; // Disallow copying - copy ctor is never defined inline ConfigFile(const ConfigFile&); /* Dummy element for end(). Its "next" ptr points to the first, its "prev" ptr to the last element, i.e. doubly linked ring of Line objects. Its nextSect is ptr to linked ring of [section] lines, or ptr to endElem itself if no sections present. */ Line endElem; size_t lineCount; Line*& firstSect() { return endElem.nextSect; } /// Non-template helper function for split() that does the actual work static bool split1Word(string* word, const string& s, string::const_iterator& e); /// Default reporter: Only prints error messages to stderr static ProgressReporter noReport; public: //______________________________ /** The iterators hide the fact that a ConfigFile is not a list. */ class iterator { friend class ConfigFile; public: iterator() { } iterator(const iterator& i) : p(i.p) { } iterator& operator=(const iterator& i) { p = i.p; return *this; } // Default dtor reference operator*() const { return p->text; } reference operator*() { return p->text; } string* operator->() const { return &p->text; } string* operator->() { return &p->text; } iterator& operator++() { p = p->next; return *this; } iterator& operator--() { p = p->prev; return *this; } bool operator==(const iterator i) const { return p == i.p; } bool operator!=(const iterator i) const { return p != i.p; } /// Is this line a [section] line? bool isSection() const { return p->nextSect != 0; } /// Is this line a [section] line with the given name? bool isSection(const string& sectName) const; /** Advance iterator to next [section] line. Efficient only if current line is also a [section] line - otherwise, does linear search. Results in *this == end() if no more sections. Does not look at the string; only relies on the info created during rescan(). */ inline iterator& nextSection(); /** Advance iterator to next [section] line with given section name. Assumes that rescan() has been called, if it hasn't then assertions will fail. sectName should be a correct section name, i.e. no whitespace at start or end. */ iterator& nextSection(const string& sectName); /** Advance iterator to next non-empty, non-comment line. If it is a label line, return true. Otherwise (a [section] line, or at end()), return false. */ inline bool nextLabel(); /// Advance to previous non-empty, non-comment line inline bool prevLabel(); /** Advance iterator to next label line with given label name, or to next [section] line, whichever comes first. Results in *this == end() if no more sections and label not found. Returns 0 if unsuccessful, i.e. iterator points to [section] line or to end(). Otherwise, returns offset to the entry's value; offset in line of first character after '='. labelName must not start or end with spaces. */ size_t nextLabel(const string& labelName); /** Overwrite arguments with offset of first character of label name, offset of first character after label name, and offset of first character after the '='. Returns false if this is not a label line. */ bool setLabelOffsets(size_t& begin, size_t& end, size_t& value); private: iterator(Line& l) : p(&l) { } Line*& nextSect() { return p->nextSect; } Line* p; }; //______________________________ /** Class to enumerate all lines in the config file which match a given section & label name. NB: Only references are maintained to the section/label name, but it is assumed that these strings remain unchanged as long as the Find object is used. WARNING WARNING this means that you *must*not* pass string constants to the Find ctor, only strings - the lifetime of the temporary strings created from string constants is usually too short!!! Usage: for (ConfigFile::Find f(c, sectionStr, labelStr); !f.finished(); f.next()) { // f.section() points to "[section]" line, or end() if 0th section // f.label() points to "label=..." line, or end() if f.finished() } size_t off; for (ConfigFile::Find f(c, sectionStr, labelStr, &off); !f.finished(); off = f.next()) { // f.section() points to "[section]" line, or end() if 0th section // f.label() points to "label=..." line, or end() if f.finished() // off is offset of part after "label=", or 0 } */ class Find { public: // Default copy ctor, dtor /** @param c The ConfigFile to search in @param sectName Section name @param labelName Label name @param i where to start searching. If i points to a [section] line, the search will start there, if it doesn't, the search will start beginning with the next section after i. If you search for the implicit section with an empty name (for labels before the first [section] line in the file), you _must_ supply sectName=="" and i==begin() (or omit i to use the second form). @param offset If non-null, is overwritten with 0 (unsuccessful) or the offset in the line to the character after '=', i.e. the offset to the entry's value. */ Find(ConfigFile* c, const string& sectName, const string& labelName, const iterator i, size_t* offset = 0); Find(ConfigFile* c, const string& sectName, const string& labelName, size_t* offset = 0); /** If the line pointed to by section() is not named sectName, advance both section() and label() to the next section of that name. Next, advance label() to the next label called labelName. Repeat the process with further sections if no label of that name in this section. */ size_t next(); /// Current section line. NB takes a copy, can't change the Find object iterator section() const { return sectionIter; } /// Current line with label iterator label() const { return labelIter; } /// Returns true if finished enumerating bool finished() const { return labelIter == configFile->end(); } private: ConfigFile* configFile; const string& sectionStr; const string& labelStr; iterator sectionIter; // For section line iterator labelIter; // For label line bool rightSection; }; // TODO: Find_const, which works on a const ConfigFile... }; //______________________________________________________________________ /** Class allowing ConfigFile to convey information back to the creator of a ConfigFile object. */ class ConfigFile::ProgressReporter { public: virtual ~ProgressReporter() { } /** General-purpose error reporting. lineNr==0 is a special case for "don't report any line number" */ virtual void error(const string& message, const size_t lineNr = 0); /** Like error(), but for purely informational messages. Default implementation, just like that of error(), prints message to cerr */ virtual void info(const string& message, const size_t lineNr = 0); }; //______________________________________________________________________ ConfigFile::ConfigFile(ProgressReporter& pr) : reporter(&pr), endElem(), lineCount(0) { endElem.prev = &endElem; endElem.next = &endElem; endElem.nextSect = &endElem; } bool ConfigFile::advanceWhitespace(string::const_iterator& x, const string::const_iterator& end) { while (true) { if (x == end || *x == '#') return true; if (*x != ' ' && *x != '\t') return false; ++x; } } bool ConfigFile::advanceWhitespace(string::iterator& x, const string::const_iterator& end) { while (true) { if (x == end || *x == '#') return true; if (*x != ' ' && *x != '\t') return false; ++x; } } ConfigFile::iterator& ConfigFile::iterator::nextSection() { // p automatically ends up pointing to end() if (isSection()) { p = p->nextSect; } else { do p = p->next; while (!isSection()); } return *this; } bool ConfigFile::iterator::nextLabel() { while (true) { ++*this; if (isSection()) return false; // Next line is superfluous cos endElem.isSection() == true //if (p->isEnd()) return false; /* Skip any empty line. If there is *any* character on this line, it must be a label line; it cannot be a section line because isSection() == false above */ string::const_iterator x = p->text.begin(); if (!advanceWhitespace(x, p->text.end())) return true; } } bool ConfigFile::iterator::prevLabel() { while (true) { --*this; if (isSection()) return false; string::const_iterator x = p->text.begin(); if (!advanceWhitespace(x, p->text.end())) return true; } } ConfigFile::iterator ConfigFile::firstSection(const string& sectName) { iterator i = end(); i.nextSection(sectName); return i; } ConfigFile::iterator ConfigFile::begin() { return iterator(*endElem.next); } ConfigFile::iterator ConfigFile::end() { return iterator(endElem); } ConfigFile::reference ConfigFile::front() { return endElem.next->text; } ConfigFile::reference ConfigFile::back() { return endElem.prev->text; } ConfigFile::iterator ConfigFile::insert(iterator pos) { Line* x = new Line(); x->prev = pos.p->prev; x->next = pos.p; pos.p->prev->next = x; pos.p->prev = x; ++lineCount; return pos; } ConfigFile::iterator ConfigFile::insert(iterator pos, const_reference s) { Line* x = new Line(s); x->prev = pos.p->prev; x->next = pos.p; pos.p->prev->next = x; pos.p->prev = x; ++lineCount; return pos; } ConfigFile::iterator ConfigFile::insert(iterator pos, const char* s) { Line* x = new Line(s); x->prev = pos.p->prev; x->next = pos.p; pos.p->prev->next = x; pos.p->prev = x; ++lineCount; return pos; } ConfigFile::iterator ConfigFile::erase(iterator pos) { Paranoid(pos.p != 0); // Don't erase an element twice Paranoid(!pos.p->isEnd()); // Don't c.erase(c.end()) pos.p->next->prev = pos.p->prev; pos.p->prev->next = pos.p->next; --lineCount; delete pos.p; pos.p = 0; return pos; } void ConfigFile::push_back() { insert(iterator(endElem)); } void ConfigFile::push_back(const string& s) { insert(iterator(endElem)); back() = s; } void ConfigFile::push_back(const char* s) { insert(iterator(endElem)); back() = s; } //______________________________________________________________________ inline ostream& operator<<(ostream& s, const ConfigFile& c) { return c.put(s); } inline istream& operator>>(istream& s, ConfigFile& c) { return c.get(s); } //______________________________________________________________________ template void ConfigFile::split(Container& out, const string& s, size_t offset) { string word; string::const_iterator e = s.begin() + offset; while (split1Word(&word, s, e)) { out.push_back(string()); swap(word, out.back()); } } #endif jigdo-0.7.3/src/util/debug.cc0000644000175000017500000000300410261546437015575 0ustar richardrichard/* $Id: debug.cc,v 1.9 2005/07/02 17:21:35 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 1999-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ #include #if defined DEBUG && defined HAVE_UNISTD_H # include /* for sleep() */ #endif #include #include #include #include //______________________________________________________________________ #undef debug namespace { Logger debug("assert", true); } bool Debug::abortAfterFailedAssertion = true; //______________________________________________________________________ int Debug::assertFail(const char* assertion, const char* file, unsigned int line) { debug("%1:%2: `%3' failed", file, line, assertion); if (abortAfterFailedAssertion) abort(); return 0; } //______________________________________________________________________ #if DEBUG && UNIX #include #include /** In order for memprof to be used, the process needs to sleep after exiting from main(). ~DebugSingleton() must be called after all other singleton dtors. This is ensured (non-portably) through link order. */ struct DebugSingleton { ~DebugSingleton() { if (getenv("_MEMPROF_SOCKET") != 0) sleep(60*60); } }; namespace { DebugSingleton ds; } #endif jigdo-0.7.3/src/util/debug.hh0000644000175000017500000000662310226060300015576 0ustar richardrichard/* $Id: debug.hh,v 1.9 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 1999-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Debugging aids, including Error objects and assertions. The Error class is just a convenient base class for all kinds of errors which result in a text error message for printing to the user. 'Paranoid()' is only compiled in if DEBUG is defined. 'Assert()' is always compiled in. General rule of thumb: 'Assert' is used to check things possibly passed from other modules, 'Paranoid' for consistency checks where only the current module changes the state. ('Module' = e.g. source file and accompanying header, but may be >1 source file if appropriate.) */ #ifndef DEBUG_HH #define DEBUG_HH #include #include #include //______________________________________________________________________ // class OptDebug { // public: // static bool optDebug() { return d; } // static void setOptDebug(bool dd) { d = dd; } // private: // OptDebug(); // Do not instantiate // static bool d; // }; // inline bool optDebug() { return OptDebug::optDebug(); } //______________________________________________________________________ /* For C (not C++), GCC 2.95 gives an error if a function param name is not given. GCC 3.4 gives a warning if it is given, but unused. Define param name to nothing for 3.4 */ #if defined __GNUC__ && __GNUC__ < 3 # define UNUSED_PARAM(_p) _p #else # define UNUSED_PARAM(_p) #endif /** To avoid warnings if a function argument is only used by Paranoid(), i.e. only if DEBUG is defined */ #if DEBUG # define DEBUG_ONLY_PARAM(_p) _p #else # define DEBUG_ONLY_PARAM(_p) #endif //______________________________________________________________________ namespace Debug { /** True if to call abort() after failed assertion. Default is true. */ extern bool abortAfterFailedAssertion; /** Print an "Assertion failed" message */ extern int assertFail(const char* assertion, const char* file, unsigned int line); // extern int assertMessage(const char* assertion, const char* file, // unsigned int line); } #if DEBUG # define Paranoid(_expr) \ ((void) ((_expr) ? 0 : \ Debug::assertFail(#_expr, __FILE__, __LINE__))) #else # define Paranoid(expr) #endif #define Assert(_expr) \ ((void) ((_expr) ? 0 : \ Debug::assertFail(#_expr, __FILE__, __LINE__))) #define Message(_str) Debug::assertFail((_str), __FILE__, __LINE__) //______________________________________________________________________ /** The messages are always in UTF-8! */ struct Error { explicit Error(const string& m) : message(m) { } explicit Error(const char* m) : message(m) { } # if DEBUG explicit Error(string& m, bool flag) { Assert(flag == true); swap(message, m); } # else explicit Error(string& m, bool) { swap(message, m); } # endif string message; }; /** Thrown to indicate: Don't report anything, but unwind stack and return specified value from main() */ struct Cleanup { explicit Cleanup(int r) : returnValue(r) { } int returnValue; }; //______________________________________________________________________ #endif jigdo-0.7.3/src/util/glibc-getopt.c0000644000175000017500000010321010261546437016724 0ustar richardrichard/* Taken from glibc 2.1.3 */ /* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO # define _NO_PROTO #endif #include #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ # ifndef const # define const # endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 # include # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION # define ELIDE_CODE # endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ # include # include #endif /* GNU C library. */ #ifdef VMS # include # if HAVE_STRING_H - 0 # include # endif #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. When compiling libc, the _ macro is predefined. */ # ifdef HAVE_LIBINTL_H # include # define _(msgid) gettext (msgid) # else # define _(msgid) (msgid) # endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ /*[RA] #include "getopt.h" */ #include /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ # include # define my_index strchr #else # if HAVE_STRING_H # include # else # include # endif /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv extern char *getenv (); #endif static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ # if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); # endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; static int original_argc; static char *const *original_argv; /* Make sure the environment variable bash 2.0 puts in the environment is valid for the getopt call we must make sure that the ARGV passed to getopt is that one passed to the process. */ static void __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv) { /* XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ original_argc = argc; original_argv = argv; } # ifdef text_set_element text_set_element (__libc_subinit, store_args_and_env); # endif /* text_set_element */ # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined __STDC__ && __STDC__ static void exchange (char **); #endif static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #ifdef _LIBC /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset (__mempcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined __STDC__ && __STDC__ static const char *_getopt_initialize (int, char *const *, const char *); #endif static const char * _getopt_initialize (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #ifdef _LIBC if (posixly_correct == NULL && argc == original_argc && argv == original_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen (orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int print_errors = opterr; if (optstring[0] == ':') print_errors = 0; optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #ifdef _LIBC # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (print_errors) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (print_errors) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ jigdo-0.7.3/src/util/glibc-getopt.h0000644000175000017500000001430307701377775016751 0ustar richardrichard/* Taken from glibc 2.1.3 */ /* Declarations for getopt. Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GLIBC_GETOPT_H #ifndef __need_getopt # define GLIBC_GETOPT_H 1 #endif #include #if HAVE_GETOPT_LONG # include # define ELIDE_CODE #else /* RA: Need this for statically linked binaries on Solaris */ #define opterr my_opterr #define optind my_optind #define optopt my_optopt #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { # if defined __STDC__ && __STDC__ const char *name; # else char *name; # endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #if defined __STDC__ && __STDC__ # ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int __argc_, char *const *__argv_, const char *__shortopts); # else /* not __GNU_LIBRARY__ */ /* [RA] breaks on Solaris: extern int getopt (); */ # endif /* __GNU_LIBRARY__ */ # ifndef __need_getopt extern int getopt_long (int __argc_, char *const *__argv_, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only (int __argc_, char *const *__argv_, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int __argc_, char *const *__argv_, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); # endif #else /* not __STDC__ */ extern int getopt (); # ifndef __need_getopt extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); # endif #endif /* __STDC__ */ #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* HAVE_GETOPT_LONG */ #endif /* getopt.h */ jigdo-0.7.3/src/util/glibc-getopt1.c0000644000175000017500000001154707701377765017033 0ustar richardrichard/* Taken from glibc 2.1.3 */ /* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif /*[RA] #include "getopt.h" */ #include #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ jigdo-0.7.3/src/util/glibc-md5.cc0000644000175000017500000003623010120704646016251 0ustar richardrichard// Taken from glibc 2.1.3, updated from 2.2.5 /* md5.c - Functions to compute MD5 message digest of files or memory blocks according to the definition of MD5 in RFC 1321 from April 1992. Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written by Ulrich Drepper , 1995. */ #ifdef HAVE_CONFIG_H # include #endif #include #if STDC_HEADERS || defined _LIBC # include # include #else # ifndef HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # endif #endif /* [RA] #include "md5.h" */ #include #include #ifdef _LIBC # include # if __BYTE_ORDER == __BIG_ENDIAN # define WORDS_BIGENDIAN 1 # endif /* We need to keep the namespace clean so define the MD5 function protected using leading __ . */ /* [RA] # define md5_init_ctx __md5_init_ctx */ /* [RA] # define md5_process_block __md5_process_block */ /* [RA] # define md5_process_bytes __md5_process_bytes */ /* [RA] # define md5_finish_ctx __md5_finish_ctx */ /* [RA] # define md5_read_ctx __md5_read_ctx */ /* [RA] # define md5_stream __md5_stream */ /* [RA] # define md5_buffer __md5_buffer */ #endif #ifdef WORDS_BIGENDIAN # define SWAP(n) \ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) #else # define SWAP(n) (n) #endif /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */ static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; /* Initialize structure containing state of computation. (RFC 1321, 3.3: Step 3) */ void MD5Sum::md5_init_ctx (md5_ctx *ctx) { ctx->A = 0x67452301; ctx->B = 0xefcdab89; ctx->C = 0x98badcfe; ctx->D = 0x10325476; ctx->total[0] = ctx->total[1] = 0; ctx->buflen = 0; } /* Put result from CTX in first 16 bytes following RESBUF. The result must be in little endian byte order. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ byte* MD5Sum::md5_read_ctx(const md5_ctx *ctx, byte* resbuf) { ((uint32 *) resbuf)[0] = SWAP (ctx->A); ((uint32 *) resbuf)[1] = SWAP (ctx->B); ((uint32 *) resbuf)[2] = SWAP (ctx->C); ((uint32 *) resbuf)[3] = SWAP (ctx->D); return resbuf; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ byte* MD5Sum::md5_finish_ctx(struct md5_ctx* ctx, byte* resbuf) { /* Take yet unprocessed bytes into account. */ uint32 bytes = ctx->buflen; size_t pad; /* Now count remaining bytes. */ ctx->total[0] += bytes; if (ctx->total[0] < bytes) ++ctx->total[1]; pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; memcpy (&ctx->buffer[bytes], fillbuf, pad); /* Put the 64-bit file length in *bits* at the end of the buffer. */ *(uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); *(uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); /* Process last bytes. */ md5_process_block (ctx->buffer, bytes + pad + 8, ctx); return md5_read_ctx (ctx, resbuf); } /* Compute MD5 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ //[RA] int //[RA] md5_stream (stream, resblock) //[RA] FILE *stream; //[RA] void *resblock; //[RA] { //[RA] /* Important: BLOCKSIZE must be a multiple of 64. */ //[RA] #define BLOCKSIZE 4096 //[RA] md5_ctx ctx; // [RA] //[RA] char buffer[BLOCKSIZE + 72]; //[RA] size_t sum; //[RA] //[RA] /* Initialize the computation context. */ //[RA] md5_init_ctx (&ctx); //[RA] //[RA] /* Iterate over full file contents. */ //[RA] while (1) //[RA] { //[RA] /* We read the file in blocks of BLOCKSIZE bytes. One call of the //[RA] computation function processes the whole buffer so that with the //[RA] next round of the loop another block can be read. */ //[RA] size_t n; //[RA] sum = 0; //[RA] //[RA] /* Read block. Take care for partial reads. */ //[RA] do //[RA] { //[RA] n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); //[RA] //[RA] sum += n; //[RA] } //[RA] while (sum < BLOCKSIZE && n != 0); //[RA] if (n == 0 && ferror (stream)) //[RA] return 1; //[RA] //[RA] /* If end of file is reached, end the loop. */ //[RA] if (n == 0) //[RA] break; //[RA] //[RA] /* Process buffer with BLOCKSIZE bytes. Note that //[RA] BLOCKSIZE % 64 == 0 //[RA] */ //[RA] md5_process_block (buffer, BLOCKSIZE, &ctx); //[RA] } //[RA] //[RA] /* Add the last bytes if necessary. */ //[RA] if (sum > 0) //[RA] md5_process_bytes (buffer, sum, &ctx); //[RA] //[RA] /* Construct result in desired memory. */ //[RA] md5_finish_ctx (&ctx, resblock); //[RA] return 0; //[RA] } //[RA] /* Compute MD5 message digest for LEN bytes beginning at BUFFER. The //[RA] result is always in little endian byte order, so that a byte-wise //[RA] output yields to the wanted ASCII representation of the message //[RA] digest. */ //[RA] void * //[RA] md5_buffer (buffer, len, resblock) //[RA] const char *buffer; //[RA] size_t len; //[RA] void *resblock; //[RA] { //[RA] md5_ctx ctx; // RA //[RA] //[RA] /* Initialize the computation context. */ //[RA] md5_init_ctx (&ctx); //[RA] //[RA] /* Process whole buffer but last len % 64 bytes. */ //[RA] md5_process_bytes (buffer, len, &ctx); //[RA] //[RA] /* Put result in desired memory area. */ //[RA] return md5_finish_ctx (&ctx, resblock); //[RA] } void MD5Sum::md5_process_bytes(const void* buffer, size_t len, struct md5_ctx* ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 128 - left_over > len ? len : 128 - left_over; memcpy (&ctx->buffer[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 64) { md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); ctx->buflen &= 63; /* The regions in the following copy operation cannot overlap. */ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], ctx->buflen); } buffer = (const char *) buffer + add; len -= add; } /* Process available complete blocks. */ if (len >= 64) { #if !_STRING_ARCH_unaligned /* To check alignment gcc has an appropriate operator. Other compilers don't. */ # if __GNUC__ >= 2 # define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (uint32) != 0) # else # define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (uint32) != 0) # endif if (UNALIGNED_P (buffer)) while (len > 64) { md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); buffer = (const char *) buffer + 64; len -= 64; } else #endif { md5_process_block (buffer, len & ~63, ctx); buffer = (const char *) buffer + (len & ~63); len &= 63; } } /* Move remaining bytes in internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy (&ctx->buffer[left_over], buffer, len); left_over += len; if (left_over >= 64) { md5_process_block (ctx->buffer, 64, ctx); left_over -= 64; memcpy (ctx->buffer, &ctx->buffer[64], left_over); } ctx->buflen = left_over; } } /* These are the four functions used in the four steps of the MD5 algorithm and defined in the RFC 1321. The first function is a little bit optimized (as found in Colin Plumbs public domain implementation). */ /* #define FF(b, c, d) ((b & c) | (~b & d)) */ #define FF(b, c, d) (d ^ (b & (c ^ d))) #define FG(b, c, d) FF (d, b, c) #define FH(b, c, d) (b ^ c ^ d) #define FI(b, c, d) (c ^ (b | ~d)) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 64 == 0. */ void MD5Sum::md5_process_block(const void* buffer, size_t len, md5_ctx* ctx) { uint32 correct_words[16]; const uint32 *words = (uint32*)(buffer); size_t nwords = len / sizeof (uint32); const uint32 *endp = words + nwords; uint32 A = ctx->A; uint32 B = ctx->B; uint32 C = ctx->C; uint32 D = ctx->D; /* First increment the byte count. RFC 1321 specifies the possible length of the file up to 2^64 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] += len; if (ctx->total[0] < len) ++ctx->total[1]; /* Process all bytes in the buffer with 64 bytes in each round of the loop. */ while (words < endp) { uint32 *cwp = correct_words; uint32 A_save = A; uint32 B_save = B; uint32 C_save = C; uint32 D_save = D; /* First round: using the given function, the context and a constant the next context is computed. Because the algorithms processing unit is a 32-bit word and it is determined to work on words in little endian byte order we perhaps have to change the byte order before the computation. To reduce the work for the next steps we store the swapped words in the array CORRECT_WORDS. */ #define OP(a, b, c, d, s, T) \ do \ { \ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ ++words; \ CYCLIC (a, s); \ a += b; \ } \ while (0) /* It is unfortunate that C does not provide an operator for cyclic rotation. Hope the C compiler is smart enough. */ #define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) /* Before we start, one word to the strange constants. They are defined in RFC 1321 as T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 */ /* Round 1. */ OP (A, B, C, D, 7, 0xd76aa478); OP (D, A, B, C, 12, 0xe8c7b756); OP (C, D, A, B, 17, 0x242070db); OP (B, C, D, A, 22, 0xc1bdceee); OP (A, B, C, D, 7, 0xf57c0faf); OP (D, A, B, C, 12, 0x4787c62a); OP (C, D, A, B, 17, 0xa8304613); OP (B, C, D, A, 22, 0xfd469501); OP (A, B, C, D, 7, 0x698098d8); OP (D, A, B, C, 12, 0x8b44f7af); OP (C, D, A, B, 17, 0xffff5bb1); OP (B, C, D, A, 22, 0x895cd7be); OP (A, B, C, D, 7, 0x6b901122); OP (D, A, B, C, 12, 0xfd987193); OP (C, D, A, B, 17, 0xa679438e); OP (B, C, D, A, 22, 0x49b40821); /* For the second to fourth round we have the possibly swapped words in CORRECT_WORDS. Redefine the macro to take an additional first argument specifying the function to use. */ #undef OP #define OP(f, a, b, c, d, k, s, T) \ do \ { \ a += f (b, c, d) + correct_words[k] + T; \ CYCLIC (a, s); \ a += b; \ } \ while (0) /* Round 2. */ OP (FG, A, B, C, D, 1, 5, 0xf61e2562); OP (FG, D, A, B, C, 6, 9, 0xc040b340); OP (FG, C, D, A, B, 11, 14, 0x265e5a51); OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); OP (FG, A, B, C, D, 5, 5, 0xd62f105d); OP (FG, D, A, B, C, 10, 9, 0x02441453); OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); OP (FG, D, A, B, C, 14, 9, 0xc33707d6); OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); OP (FG, B, C, D, A, 8, 20, 0x455a14ed); OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); OP (FG, C, D, A, B, 7, 14, 0x676f02d9); OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); /* Round 3. */ OP (FH, A, B, C, D, 5, 4, 0xfffa3942); OP (FH, D, A, B, C, 8, 11, 0x8771f681); OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); OP (FH, B, C, D, A, 14, 23, 0xfde5380c); OP (FH, A, B, C, D, 1, 4, 0xa4beea44); OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); OP (FH, B, C, D, A, 6, 23, 0x04881d05); OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); /* Round 4. */ OP (FI, A, B, C, D, 0, 6, 0xf4292244); OP (FI, D, A, B, C, 7, 10, 0x432aff97); OP (FI, C, D, A, B, 14, 15, 0xab9423a7); OP (FI, B, C, D, A, 5, 21, 0xfc93a039); OP (FI, A, B, C, D, 12, 6, 0x655b59c3); OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); OP (FI, C, D, A, B, 10, 15, 0xffeff47d); OP (FI, B, C, D, A, 1, 21, 0x85845dd1); OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); OP (FI, C, D, A, B, 6, 15, 0xa3014314); OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); OP (FI, A, B, C, D, 4, 6, 0xf7537e82); OP (FI, D, A, B, C, 11, 10, 0xbd3af235); OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); OP (FI, B, C, D, A, 9, 21, 0xeb86d391); /* Add the starting values of the context. */ A += A_save; B += B_save; C += C_save; D += D_save; } /* Put checksum in context given as argument. */ ctx->A = A; ctx->B = B; ctx->C = C; ctx->D = D; } jigdo-0.7.3/src/util/glibc-md5.hh0000644000175000017500000001371207733671665016307 0ustar richardrichard/* Declaration of functions and data types used for MD5 sum computing library functions. Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _MD5_H #define _MD5_H 1 #include #if defined HAVE_LIMITS_H || _LIBC # include #endif /* The following contortions are an attempt to use the C preprocessor to determine an unsigned integral type that is 32 bits wide. An alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but doing that would require that the configure script compile and *run* the resulting executable. Locally running cross-compiled executables is usually not possible. */ #ifdef _LIBC # include typedef uint32_t md5_uint32; typedef uintptr_t md5_uintptr; #else # if defined __STDC__ && __STDC__ # define UINT_MAX_32_BITS 4294967295U # else # define UINT_MAX_32_BITS 0xFFFFFFFF # endif /* If UINT_MAX isn't defined, assume it's a 32-bit type. This should be valid for all systems GNU cares about because that doesn't include 16-bit systems, and only modern systems (that certainly have ) have 64+-bit integral types. */ # ifndef UINT_MAX # define UINT_MAX UINT_MAX_32_BITS # endif # if UINT_MAX == UINT_MAX_32_BITS typedef unsigned int md5_uint32; # else # if USHRT_MAX == UINT_MAX_32_BITS typedef unsigned short md5_uint32; # else # if ULONG_MAX == UINT_MAX_32_BITS typedef unsigned long md5_uint32; # else /* The following line is intended to evoke an error. Using #error is not portable enough. */ "Cannot determine unsigned 32-bit data type." # endif # endif # endif /* We have to make a guess about the integer type equivalent in size to pointers which should always be correct. */ typedef unsigned long int md5_uintptr; #endif #undef __P #if defined (__STDC__) && __STDC__ # define __P(x) x #else # define __P(x) () #endif /* Structure to save state of computation between the single steps. */ /* [RA] moved this to md5sum.hh */ /* [RA] struct md5_ctx */ /* [RA] { */ /* [RA] uint32 A; */ /* [RA] uint32 B; */ /* [RA] uint32 C; */ /* [RA] uint32 D; */ /* [RA] */ /* [RA] uint32 total[2]; */ /* [RA] uint32 buflen; */ /* [RA] char buffer[128]; */ /* [RA] }; */ /* * The following three functions are build up the low level used in * the functions `md5_stream' and `md5_buffer'. */ /* Initialize structure containing state of computation. (RFC 1321, 3.3: Step 3) */ extern void md5_init_ctx __P ((struct md5_ctx *ctx)); /* [RA] extern void __md5_init_ctx __P ((struct md5_ctx *ctx)); */ /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is necessary that LEN is a multiple of 64!!! */ extern void md5_process_block __P ((const void *buffer, size_t len, struct md5_ctx *ctx)); /* [RA] extern void __md5_process_block __P ((const void *buffer, size_t len, */ /* [RA] struct md5_ctx *ctx)); */ /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is NOT required that LEN is a multiple of 64. */ extern void md5_process_bytes __P ((const void *buffer, size_t len, struct md5_ctx *ctx)); /* [RA] extern void __md5_process_bytes __P ((const void *buffer, size_t len, */ /* [RA] struct md5_ctx *ctx)); */ /* Process the remaining bytes in the buffer and put result from CTX in first 16 bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); /* [RA] extern void *__md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); */ /* Put result from CTX in first 16 bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); /* [RA] extern void *__md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); */ /* Compute MD5 message digest for bytes read from STREAM. The resulting message digest number will be written into the 16 bytes beginning at RESBLOCK. */ /* [RA] extern int __md5_stream __P ((FILE *stream, void *resblock)); */ /* Compute MD5 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ /* [RA] extern void *__md5_buffer __P ((const char *buffer, size_t len, */ /* [RA] void *resblock)); */ #endif /* md5.h */ jigdo-0.7.3/src/util/gunzip-test.cc0000644000175000017500000002046410121135314016770 0ustar richardrichard/* $Id: gunzip-test.cc,v 1.5 2004/09/12 21:08:28 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. In-memory, push-oriented decompression of .gz files #test-deps util/gunzip.o #test-ldflags $(LIBS) */ #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ int returnCode = 0; namespace { const int FILEBUFSIZE = 4096; const int BUFSIZE = 4096; struct ToStdout : Gunzip::IO { byte buf[BUFSIZE]; virtual ~ToStdout() { } virtual void gunzip_deleted() { } virtual void gunzip_data(Gunzip* self, byte* decompressed, unsigned size) { Assert(self != 0); // NOP, doxygen insists on the param name cout.write(reinterpret_cast(decompressed), size); } virtual void gunzip_needOut(Gunzip* self) { self->setOut(buf, BUFSIZE); } virtual void gunzip_succeeded() { } virtual void gunzip_failed(string* message) { cerr << *message << endl; } }; const char* const hexDigits = "0123456789abcdef"; void escapedChar(string* o, byte c) { switch (c) { case 0: *o += "\\0"; break; case '\n': *o += "\\n"; break; case '\t': *o += "\\t"; break; case '"': case '\\': *o += '\\'; *o += c; break; default: if (c >= ' ' && c <= '~') { *o += c; } else { *o += "\\x"; *o += hexDigits[unsigned(c) >> 4]; *o += hexDigits[unsigned(c) & 0xfU]; } } } inline string escapedString(const string& s) { string result; for (unsigned i = 0; i < s.length(); ++i) escapedChar(&result, s[i]); return result; } inline string escapedString(const byte* s, unsigned ssize) { string result; for (unsigned i = 0; i < ssize; ++i) escapedChar(&result, s[i]); return result; } struct ToString : Gunzip::IO { unsigned bufsize; byte* buf; ToString(unsigned bufs) : bufsize(bufs) { buf = new byte[bufs + 1]; buf[bufs] = 0x7fU; } virtual ~ToString() { delete[] buf; } virtual void gunzip_deleted() { } virtual void gunzip_data(Gunzip* self, byte* decompressed, unsigned size) { Assert(self != 0); // NOP, doxygen insists on the param name if (buf[bufsize] != 0x7fU) o += "[BUFFER OVERFLOW]"; o.append(reinterpret_cast(decompressed), size); } virtual void gunzip_needOut(Gunzip* self) { self->setOut(buf, bufsize); } virtual void gunzip_succeeded() { } virtual void gunzip_failed(string* message) { o += '['; o += *message; o += ']'; } string o; }; void decompressFile(const char* name) { bifstream f(name); byte buf[FILEBUFSIZE]; ToStdout out; Gunzip decompressor(&out); while (f) { readBytes(f, buf, FILEBUFSIZE); unsigned n = f.gcount(); decompressor.inject(buf, n); } if (!f.eof()) { cerr << strerror(errno) << endl; } } // cbs/ubs: Block size for I/O of (un)compressed data void testCase2(const byte* c, unsigned csize, const string& unp, unsigned cbs, unsigned ubs) { ToString io(ubs); Gunzip decompressor(&io); unsigned cleft = csize; const byte* cptr = c; while (cleft > 0) { unsigned count = min(cleft, cbs); decompressor.inject(cptr, count); cptr += count; cleft -= count; } if (unp == io.o) { msg("OK: cbs=%1 ubs=%2 \"%3\"", cbs, ubs, escapedString(unp)); } else { msg("FAILED: cbs=%1 ubs=%2", cbs, ubs); msg(" expected \"%1\"", escapedString(unp)); msg(" but got \"%1\"", escapedString(io.o)); returnCode = 1; } } void testCase(const byte* c, unsigned csize, const byte* u, unsigned usize, int testNr) { string unp(reinterpret_cast(u), usize); msg("Test case %1: \"%2\"", testNr, escapedString(c, csize)); testCase2(c, csize, unp, 1, 1); testCase2(c, csize, unp, 3, 1); testCase2(c, csize, unp, 1, 3); testCase2(c, csize, unp, 10, 1); testCase2(c, csize, unp, 1, 10); testCase2(c, csize, unp, 10, 10); testCase2(c, csize, unp, 5, 1024); testCase2(c, csize, unp, 1024, 5); testCase2(c, csize, unp, 1024, 1024); } } // namespace int main(int argc, char* argv[]) { if (argc == 3 && strcmp("decompress", argv[1]) == 0) { decompressFile(argv[2]); exit(0); } /* Special case, nothing to do with testing gunzip: Convert stdin to C-style string. */ if (argc == 2 && strcmp("tohex", argv[1]) == 0) { string o; int len = 0; while (cin) { byte c; cin.read(reinterpret_cast(&c), 1); if (cin.gcount() == 0) continue; escapedChar(&o, c); ++len; } cout << len << ", \"" << o << '"' << endl; return 0; } else if (argc == 2) { Logger::scanOptions(argv[1], argv[0]); } msg("Usage: `gunziptest decompress FILENAME.gz' to decompress to stdout\n" " `gunziptest' to test some built-in files\n" " `gunziptest tohex' to convert stdin to escaped string"); // compressed size, compressed input, unc. size, uncompressed output struct Test { int csize; const char* c; int usize; const char* u; }; const Test test[] = { { // Rhubarb with embedded filename "rhubarb" 36, "\x1f\x8b\x08\x08\x8d\xb1\x89>\x02\x03rhubarb\0\x0b\xca(MJ,J\xe2\x02\0l]\x94\x11\x08\0\0\0", 8, "Rhubarb\n" }, { // Quick brown fox without filename 64, "\x1f\x8b\x08\0H\xf9\x8a>\x02\x03\x0b\xc9HU(,\xcdL\xceVH*\xca/\xcfSH\xcb\xafP\xc8*\xcd-(V\xc8/K-R(\x01J\xe7$VU*\xa4\xe4\xa7\xebq\x01\0j\xccP\xeb-\0\0\0", 45, "The quick brown fox jumps over the lazy dog.\n" }, { // Non-gzip, 0 gzip header bytes => transparent 11, "Gunzip test", 11, "Gunzip test" }, { // Non-gzip, 1 gzip header byte => transparent 11, "\x1funzip test", 11, "\x1funzip test" }, { // Non-gzip, 2 gzip header bytes => transparent 11, "\x1f\x8bnzip test", 11, "\x1f\x8bnzip test" }, { // Non-gzip, 3 gzip header bytes => error, flag byte invalid 11, "\x1f\x8b\x08zip test", 21, "[Decompression error]" }, { // Long name, null content 41, "\x1f\x8b\x08\x08\xde\xfd\x8a>\x02\x03""12345678901234567890\0\x03\0\0\0\0\0\0\0\0\0", 0, "" }, { // Otherwise correct .gz file, but with a wrong checksum at the end 47, "\x1f\x8b\x08\x08\xe6\x04\x8b>\x02\x03ssl\0\x0b.I\xcc\xccKQ\x08.\xcd""51T\xf0\xc9\xcc-p\xca\xac\xca\xce,\x01\0\xe7\"\x98'\x17\0\0\0", 63, "Staind Sum41 LimpBizkit[Decompression error: Checksum is wrong]" }, { // Two .gz files concatenated 48, "\x1f\x8b\x08\0s\x06\x8b>\x02\x03K\xcb\xcf\xe7\x02\0\xa8""e2~\x04\0\0\0\x1f\x8b\x08\0u\x06\x8b>\x02\x03KJ,\xe2\x02\0\xe9\xb3\xa2\x04\x04\0\0\0", 8, "foo\nbar\n" }, { // EXTRA field set 102, "\x1f\x8b\x08\x0c""F\x04\x8b>\x02\x03\x04\0\x11\"3Dyadayada\0\x01@\0\xbf\xff\x97\x93\x05\xf1\xed\xa5\xf2\x89\xc6_V\x05\x89""CG\xc2""9\xfe(\xee\x0e\x9f\xbf\x04\xb2$2\xf2\xf0\xd2\xe4\xd6\x8e<\xc8pD&r\x94\xe1l\x99\x80VT\n\xcc\xad\xfd\xc9Y\x95""9h\x17\x19""F\xdeQ\xcb]M?\x89n\xc0\xd4@\0\0\0", 64, "\x97\x93\x05\xf1\xed\xa5\xf2\x89\xc6_V\x05\x89""CG\xc2""9\xfe(\xee\x0e\x9f\xbf\x04\xb2$2\xf2\xf0\xd2\xe4\xd6\x8e<\xc8pD&r\x94\xe1l\x99\x80VT\n\xcc\xad\xfd\xc9Y\x95""9h\x17\x19""F\xdeQ\xcb]M?" }, { // EXTRA, name, comment all present 62, "\x1f\x8b\x08\x1c\x96\x0c\x8b>\x02\x03\x05\0\x11\"3DUyadayada\0comment\0s\xc9L-N-\nN,\xa9\xf2N\xcd\xcc\x0bK-J\x02\0h\xb6]\xb8\x12\0\0\0", 18, "DieserSatzKeinVerb" } }; int nrOfTests = sizeof(test) / sizeof(Test); for (int i = 0; i < nrOfTests; ++i) { testCase(reinterpret_cast(test[i].c), test[i].csize, reinterpret_cast(test[i].u), test[i].usize, i); } if (returnCode == 0) msg("OK - all tests succeeded!"); else msg("FAILED - at least one test had an incorrect result!"); return returnCode; } jigdo-0.7.3/src/util/gunzip.cc0000644000175000017500000002107310431672643016027 0ustar richardrichard/* $Id: gunzip.cc,v 1.6 2006/05/14 18:23:31 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. In-memory, push-oriented decompression of .gz files */ #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("gunzip") void Gunzip::error(const char* msg) { string err = _("Decompression error"); if (msg != 0) { err += ": "; err += msg; } state = ERROR; io->gunzip_failed(&err); } //______________________________________________________________________ Gunzip::Gunzip(IO* ioPtr) : io(ioPtr), state(INIT0), headerFlags(0), skip(0) { z.next_in = 0; z.avail_in = 0; z.next_out = 0; z.avail_out = 0; z.zalloc = (alloc_func)0; z.zfree = (free_func)0; z.opaque = 0; // Undocumented zlib feature - the following comment is from gzio.c: /* windowBits is passed < 0 to tell that there is no zlib header. Note that in this case inflate *requires* an extra "dummy" byte after the compressed stream in order to complete decompression and return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are present after the compressed stream. */ int ok = inflateInit2(&z, -MAX_WBITS); if (ok != Z_OK) error(z.msg); // Initialize checksum of uncompressed data crc = crc32(0L, Z_NULL, 0); } //______________________________________________________________________ Gunzip::~Gunzip() { inflateEnd(&z); // Ignore errors } //______________________________________________________________________ void Gunzip::inject(const byte* compressed, unsigned size) { if (state == ERROR) return; Assert(z.avail_in == 0); z.next_in = (byte*)compressed; z.avail_in = size; byte b; while (z.avail_in > 0) { if (skip > 0) { // Ignore "skip" bytes of input if (skip <= z.avail_in) { z.next_in += skip; z.avail_in -= skip; debug("Skipped %1", skip); skip = 0; if (z.avail_in == 0) break; } else { debug("Skipped~ %1", z.avail_in); z.next_in += z.avail_in; skip -= z.avail_in; z.avail_in = 0; break; } } Paranoid(z.avail_in > 0); switch (state) { case INIT0: // Start crc = crc32(0L, Z_NULL, 0); // Init checksum debug("INIT0: Need byte 31, got %1", unsigned(*z.next_in)); if (*z.next_in != '\x1f') { state = TRANSPARENT; break; } state = INIT1; nextInByte(); if (z.avail_in == 0) break; case INIT1: // Found first .gz ID byte \x1f, look for second \x8b debug("INIT1: Need byte 139, got %1", unsigned(*z.next_in)); if (static_cast(*z.next_in) != 0x8bU) { outputByte('\x1f'); state = TRANSPARENT; break; } state = HEADER_CM; nextInByte(); if (z.avail_in == 0) break; case HEADER_CM: // Found .gz ID bytes \x1f\x8b, compression method byte follows debug("HEADER_CM: Need byte 8, got %1", unsigned(*z.next_in)); if (*z.next_in != 8) { outputByte(0x1f); outputByte(0x8b); state = TRANSPARENT; break; } state = HEADER_FLG; nextInByte(); if (z.avail_in == 0) break; case HEADER_FLG: // Found .gz ID bytes \x1f\x8b\x08, now read flag byte headerFlags = nextInByte(); debug("HEADER_FLG: %1", unsigned(headerFlags)); if ((headerFlags & 0xe0) != 0) { error(0); // Reserved flags non-zero => error return; } state = HEADER_FEXTRA0; skip = 4 // skip MTIME field (Modification TIME) + 1 // skip XFL (eXtra FLags) + 1; // skip OS (Operating System) break; case HEADER_FEXTRA0: // If FEXTRA flag set, read XLEN if ((headerFlags & (1<<2)) == 0) { state = HEADER_FNAME; // FEXTRA not set break; } debug("FEXTRA0"); data = nextInByte(); // Lower 8 bits of XLEN (eXtra LENgth) state = HEADER_FEXTRA1; if (z.avail_in == 0) break; case HEADER_FEXTRA1: // If FEXTRA flag set, read XLEN data |= (nextInByte() << 8); // Upper 8 bits of XLEN (eXtra LENgth) debug("FEXTRA1: XLEN=%1", data); state = HEADER_FNAME; skip = data; // Skip contents of "extra field" break; case HEADER_FNAME: // If FNAME flag set, skip subsequent original filename if ((headerFlags & (1<<3)) == 0) { state = HEADER_FCOMMENT; // FNAME not set break; } // Skip null-terminated original filename while (z.avail_in > 0) { byte b = nextInByte(); debug("FNAME: Skipping name: %1", unsigned(b)); if (b == 0) { state = HEADER_FCOMMENT; break; } } if (z.avail_in == 0) break; case HEADER_FCOMMENT: // If FCOMMENT flag set, skip subsequent comment if ((headerFlags & (1<<4)) == 0) { state = ZLIB; // FCOMMENT not set if ((headerFlags & (1<<1)) != 0) skip = 2; // FHCRC set - skip header checksum break; } // Skip null-terminated comment while (z.avail_in > 0) { b = nextInByte(); debug("FCOMMENT: Skipping comment: %1", unsigned(b)); if (b == 0) { state = ZLIB; if ((headerFlags & (1<<1)) != 0) skip = 2; // FHCRC set - skip header checksum break; } } break; case ZLIB: // Pass compressed data to zlib while (z.avail_in > 0) { needOutByte(); Bytef* oldNextOut = z.next_out; int ok = inflate(&z, Z_NO_FLUSH); // Decompress! if (z.next_out > oldNextOut) { crc = crc32(crc, oldNextOut, z.next_out - oldNextOut); io->gunzip_data(this, oldNextOut, z.next_out - oldNextOut); } if (ok == Z_OK) { debug("ZLIB: Z_OK"); } else if (ok == Z_STREAM_END) { // Re-initialize decompressor if (inflateReset(&z) != Z_OK) { error(z.msg); return; } debug("ZLIB: Z_STREAM_END"); // End of zlib stream reached, now verify checksum state = TRAILER_CRC0; break; } else { // Z_NEED_DICT, Z_DATA_ERROR, Z_STREAM_ERROR, Z_BUF_ERROR debug("ZLIB: error %1", ok); error(z.msg); return; } } break; case TRAILER_CRC0: // Compare checksum byte debug("TRAILER_CRC0: crc=%1", crc); if ((b = nextInByte()) != (crc & 0xffU)) { debug("TRAILER_CRC0 failed: need %1, got %2", crc & 0xffU, unsigned(b)); error(_("Checksum is wrong")); return; } debug("TRAILER_CRC0 ok"); state = TRAILER_CRC1; if (z.avail_in == 0) break; case TRAILER_CRC1: // Compare checksum byte if (nextInByte() != ((crc >> 8) & 0xffU)) { debug("TRAILER_CRC1 failed"); error(_("Checksum is wrong")); return; } debug("TRAILER_CRC1 ok"); state = TRAILER_CRC2; if (z.avail_in == 0) break; case TRAILER_CRC2: // Compare checksum byte if (nextInByte() != ((crc >> 16) & 0xffU)) { debug("TRAILER_CRC2 failed"); error(_("Checksum is wrong")); return; } debug("TRAILER_CRC2 ok"); state = TRAILER_CRC3; if (z.avail_in == 0) break; case TRAILER_CRC3: // Compare checksum byte if (nextInByte() != ((crc >> 24) & 0xffU)) { debug("TRAILER_CRC3 failed"); error(_("Checksum is wrong")); return; } debug("TRAILER_CRC3 ok"); // Skip 4 bytes of length of uncompressed, then expect another .gz file skip = 4; state = INIT0; break; case TRANSPARENT: // Pass data through unmodified debug("TRANSPARENT"); while (z.avail_in > 0) { needOutByte(); unsigned s = min(z.avail_in, z.avail_out); Bytef* oldNextOut = z.next_out; memmove(z.next_out, z.next_in, s); z.avail_in -= s; z.avail_out -= s; z.next_in += s; z.next_out += s; io->gunzip_data(this, oldNextOut, s); } break; default: Paranoid(false); error("Bug"); debug("Unknown state %1", state); return; } // endswitch (state) } // endwhile (z.avail_in > 0) } jigdo-0.7.3/src/util/gunzip.hh0000644000175000017500000001503610226060300016022 0ustar richardrichard/* $Id: gunzip.hh,v 1.3 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file In-memory, push-oriented decompression of .gz files */ #ifndef GUNZIP_HH #define GUNZIP_HH #include #include #include #include #ifdef TRANSPARENT # undef TRANSPARENT #endif #ifdef ERROR # undef ERROR #endif //______________________________________________________________________ /** Allows .gz files to be decompressed in memory on the fly as they are being downloaded. In contrast to a gzstream-style object where calls would be made to the object to obtain decompressed data, with Gunzip calls have to be made to "push" new gzipped data to the object once available, and the Gunzip object then makes further calls, passing decompressed data to an object of your choice. Contains code to auto-detect .gz files: If the file starts with \\x1f\\x8b\\x08, the data is passed to zlib to decompress. Otherwise, it is assumed that the file is not compressed and it is passed to the output IO object unmodified. Can deal with >1 concatenated .gz files; in this case, simply outputs the concatenated uncompressed data. */ class Gunzip { public: //________________________________________ /** The Gunzip object makes calls to the virtual functions of a class you derive from this. Abstract class, define the gunzip_* methods in your derived class. */ class IO { public: virtual ~IO() { } /** Called by the Gunzip object when it is deleted or when a different IO object is registered with it. If the IO object considers itself owned by its Gunzip, it can delete itself. */ virtual void gunzip_deleted() = 0; /** Called from within Gunzip::inject() after each decompression step. @param self Gunzip object this IO object is registered with @param decompressed Pointer to "size" new bytes of uncompressed data @param size Number of bytes at decompressed */ virtual void gunzip_data(Gunzip* self, byte* decompressed, unsigned size) = 0; /** Called from within Gunzip::inject() if self->availOut()==0 and another output buffer is needed. You must call self->setOut() to supply it.*/ virtual void gunzip_needOut(Gunzip* self) = 0; /** Called when decompression has successfully finished. */ //virtual void gunzip_succeeded() = 0; /** Called when decompression fails. You can copy the error message away with mystring.swap(*message). After the error, further calls to the object are not allowed; delete the object. */ virtual void gunzip_failed(string* message) = 0; }; //________________________________________ /** Pointer to an IO object, can be changed during the Gunzip object's lifetime. */ class IOPtr { // cf. job.hh public: IOPtr(IO* io) : ptr(io) { } ~IOPtr() { ptr->gunzip_deleted(); } IO& operator*() const throw() { return *ptr; } IO* operator->() const throw() { return ptr; } operator bool() const throw() { return ptr != 0; } IO* get() const throw() { return ptr; } /** Calls the IO object's gunzip_deleted() method before overwriting the value, except if the old and new IO are identical */ void set(IO* io) { if (ptr == io) return; if (ptr != 0) ptr->gunzip_deleted(); ptr = io; } private: IO* ptr; }; IOPtr io; //________________________________________ Gunzip(IO* ioPtr); virtual ~Gunzip(); /** Called by you whenever new compressed data is available. The contents of "compressed" need not be preserved after the call returns. */ void inject(const byte* compressed, unsigned size); /** Return current output position, i.e. pointer to address where next uncompressed byte will be put. */ inline byte* nextOut() const; /** Return nr of bytes left in output buffer. */ inline unsigned availOut() const; /** Supply an output buffer to place decompressed data into. This *must* be called from gunzip_needOut(), but can also be called at other times, e.g. from gunzip_data(). */ inline void setOut(byte* newNextOut, unsigned newAvailOut); private: // Pass zlib error to IO object void error(const char* msg); // Ensure that at least one output byte is writable in z.next_out/avail_out inline void needOutByte(); // Advance z.next_in/z.avail_in by one byte & return it inline byte nextInByte(); // Output a single byte. Calls needOutByte() and io->data(this,...,1) inline void outputByte(byte b); /* zlib doesn't support in-memory decompression of .gz files, only of zlib streams, so we need to extract the zlib stream from the .gz file. Keep track of our progress in a state var. */ enum { INIT0, // Guessing whether or not .gz format: First two bytes \x1f \x8b INIT1, HEADER_CM, // .gz header: compression method byte HEADER_FLG, // .gz header: flag byte HEADER_FEXTRA0, // .gz header: optional extra fields HEADER_FEXTRA1, HEADER_FNAME, // .gz header: Original file name HEADER_FCOMMENT, // .gz header: Comment field ZLIB, // Found zlib stream, pass to zlib TRAILER_CRC0, // Checksum of uncompressed data, after zlib data TRAILER_CRC1, TRAILER_CRC2, TRAILER_CRC3, TRANSPARENT, // Final state: Not .gz format, pass to IO unmodified ERROR // Final state: zlib error or error in .gz header }; int state; byte headerFlags; z_stream z; unsigned skip; // Nr of bytes to ignore in input stream unsigned data; // to accumulate parameters from the stream uLong crc; // CRC32 checksum of uncompressed data }; //______________________________________________________________________ byte* Gunzip::nextOut() const { return z.next_out; } unsigned Gunzip::availOut() const { return z.avail_out; } void Gunzip::setOut(byte* newNextOut, unsigned newAvailOut) { z.next_out = newNextOut; z.avail_out = newAvailOut; } void Gunzip::needOutByte() { if (availOut() == 0) { io->gunzip_needOut(this); Paranoid(availOut() > 0); } } void Gunzip::outputByte(byte b) { needOutByte(); *z.next_out = b; --z.avail_out; ++z.next_out; io->gunzip_data(this, z.next_out - 1, 1); } byte Gunzip::nextInByte() { byte result = *z.next_in; ++z.next_in; --z.avail_in; return result; } #endif jigdo-0.7.3/src/util/ilist.hh0000644000175000017500000001251710226060300015633 0ustar richardrichard/* $Id: ilist.hh,v 1.10 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Intrusive list, ie every list member needs to derive publically from IListBase A speciality is that list members will remove themselves from their list automatically from their dtor. */ #ifndef ILIST_HH #define ILIST_HH #include #include #include //______________________________________________________________________ /** Derived classes can be list members. */ class IListBase { template friend class IList; public: IListBase() : iListBase_prev(0), iListBase_next(0) { //msg("IListBase %1", this); } ~IListBase() { //msg("~IListBase %1", this); iList_remove(); } /** May use this to unlink this object from its list, if any */ void iList_remove() { if (iListBase_prev == 0) return; Paranoid(iListBase_next != 0); iListBase_prev->iListBase_next = iListBase_next; iListBase_next->iListBase_prev = iListBase_prev; iListBase_prev = iListBase_next = 0; } private: IListBase* iListBase_prev; IListBase* iListBase_next; }; /** The list object. */ template class IList { public: typedef unsigned size_type; typedef T value_type; class iterator; class const_iterator; friend class iterator; friend class const_iterator; typedef T& reference; typedef const T& const_reference; IList() { e.iListBase_prev = e.iListBase_next = &e; } /** Releases all member objects from the list, does not delete them. */ ~IList() { IListBase* p = e.iListBase_next; while (p != &e) { IListBase* q = p->iListBase_next; p->iListBase_prev = p->iListBase_next = 0; p = q; } e.iListBase_prev = e.iListBase_next = 0; } bool empty() const { return e.iListBase_next == &e; } void push_back(T& x) { //msg("IList::push_back %1", &x); // Object must not already be a list member Assert(x.iListBase_prev == 0 && x.iListBase_next == 0); x.iListBase_prev = e.iListBase_prev; x.iListBase_next = &e; x.iListBase_prev->iListBase_next = &x; x.iListBase_next->iListBase_prev = &x; } void push_front(T& x) { // Object must not already be a list member Assert(x.iListBase_prev == 0 && x.iListBase_next == 0); x.iListBase_prev = &e; x.iListBase_next = e.iListBase_next; x.iListBase_prev->iListBase_next = &x; x.iListBase_next->iListBase_prev = &x; } T& front() const { return *static_cast(e.iListBase_next); } T& back() const { return *static_cast(e.iListBase_prev); } inline iterator begin() { return iterator(e.iListBase_next); } inline iterator end() { return iterator(&e); } inline const_iterator begin() const { return const_iterator(e.iListBase_next); } inline const_iterator end() const { return const_iterator(&e); } private: // For the iterator class, which cannot be a friend of IListBase static inline IListBase* next(const IListBase* ilb) { return ilb->iListBase_next; } static inline IListBase* prev(const IListBase* ilb) { return ilb->iListBase_prev; } IListBase e; }; /** iterator for an IList object */ template class IList::iterator { friend class const_iterator; public: iterator(IListBase* pp) : p(pp) { } iterator(const iterator& i) : p(i.p) { } iterator& operator=(const iterator& i) { p = i.p; return *this; } iterator& operator=(const const_iterator& i) { p = i.p; return *this; } T& operator*() { return *getT(); } const T& operator*() const { return *getT(); } T* operator->() { return getT(); } const T* operator->() const { return getT(); } iterator& operator++() { p = IList::next(p); return *this; } iterator& operator--() { p = IList::prev(p); return *this; } bool operator==(const iterator i) const { return p == i.p; } bool operator!=(const iterator i) const { return p != i.p; } private: //T* getT() { return reinterpret_cast(p); } // Will not work if IListBase is an inaccessible base of T: T* getT() { return static_cast(p); } IListBase* p; }; /** const_iterator for an IList object */ template class IList::const_iterator { friend class iterator; public: const_iterator(IListBase* pp) : p(pp) { } const_iterator(const IListBase* pp) : p(pp) { } explicit const_iterator(const iterator& i) : p(i.p) { } const_iterator& operator=(const iterator& i) { p = i.p; return *this; } const_iterator(const const_iterator& i) : p(i.p) { } const_iterator& operator=(const const_iterator& i) { p = i.p; return *this; } const T& operator*() const { return *getT(); } const T* operator->() const { return getT(); } const_iterator& operator++() { p = IList::next(p); return *this; } const_iterator& operator--() { p = IList::prev(p); return *this; } bool operator==(const const_iterator i) const { return p == i.p; } bool operator!=(const const_iterator i) const { return p != i.p; } private: //const T* getT() const { return reinterpret_cast(p); } // Will not work if IListBase is an inaccessible base of T: const T* getT() const { return static_cast(p); } const IListBase* p; }; #endif jigdo-0.7.3/src/util/log-test.cc0000644000175000017500000000253307735400630016247 0ustar richardrichard/* $Id: log-test.cc,v 1.3 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Logfile / debugging output #test-deps */ #include #include #include #include //______________________________________________________________________ Logger info("Log-test"); Logger debugg("Flame-fest"); namespace { ostringstream out; void put(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]) { out << unitName << ':'; if (unitNameLen < 15) out << " " + unitNameLen; out << Subst::subst(format, args, arg) << endl; } } int main() { // Don't do this; it interferes and makes the test fail: //if (argc == 2) Logger::scanOptions(argv[1], argv[0]); Logger::setOutputFunction(&put); Logger::setEnabled("Log-test"); string c = " (correct)"; info("The answer: %1%2", 42, c); debugg("yo"); Logger::setEnabled("Flame-fest"); debugg("boo"); Assert(out.str() == "Log-test: The answer: 42 (correct)\n" "Flame-fest: boo\n"); return 0; } jigdo-0.7.3/src/util/log.cc0000644000175000017500000000715707735400630015301 0ustar richardrichard/* $Id: log.cc,v 1.8 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Logfile / debugging output */ #include #include #include #include //______________________________________________________________________ /* Static head of linked list of Logger objects in different compilation units. Traversed e.g. to enable/disable specific units' messages with the --debug command line switch. */ Logger* Logger::list = 0; Logger msg("general"); Logger::Logger(const char* unitName, bool enabled) : unitNameVal(unitName), unitNameLen(strlen(unitName)), enabledVal(enabled), next(list) { list = this; } bool Logger::setEnabled(const char* unitName, bool enable) { Logger* l = list; if (unitName == 0) { // Change all loggers while (l != 0) { l->enabledVal = enable; l = l->next; } return true; } // Only change matching Loggers bool result = false; // Not found while (l != 0) { if (strcmp(unitName, l->unitNameVal) == 0) { l->enabledVal = enable; result = true; } l = l->next; } return result; } void Logger::defaultPut(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]) { cerr << unitName << ':'; if (unitNameLen < 15) cerr << " " + unitNameLen; cerr << Subst::subst(format, args, arg) << endl; } Logger::OutputFunction* Logger::output = &defaultPut; //______________________________________________________________________ /* The value of the --debug cmd line option is either missing (empty) or a comma-separated list of words (we also allow spaces for the fun of it). Each word can be preceded by a '~' for negation (i.e. disable debug messages rather than enable them). The word is the name of a compilation unit, or one of the special values "all" or "help". */ void Logger::scanOptions(const string& s, const char* binName) { unsigned i = 0; string word; bool enable; unsigned len = s.length(); while (i < len) { word.erase(); enable = true; while ((s[i] == '~' || s[i] == ' ') && i < len) { if (s[i] == '~') enable = !enable; ++i; } while (s[i] != ' ' && s[i] != ',' && i < len) { word += s[i]; ++i; } while ((s[i] == ',' || s[i] == ' ') && i < len) ++i; if (word == "all") { // Argument "all" - all units Logger::setEnabled(0, enable); } else if (word != "help") { // Other word - the name of a unit // Do not fail if unit not found - some units are only there with DEBUG if (!Logger::setEnabled(word.c_str(), enable)) { cerr << subst(_("%1: Unit `%2' not found while scanning --debug " "argument"), binName, word) << endl; throw Cleanup(3); } } else { // Argument "help" - print list of units Logger* l = Logger::enumerate(); cerr << _( "By default, debug output is disabled except for `assert'. Argument\n" "to --debug is a comma-separated list of unit names, or `all' for\n" "all units. Just `--debug' is equivalent to`--debug=all'. Output for\n" "the listed units is enabled, precede a name with `~' to disable it.\n" "Registered units:"); while (l != 0) { cerr << ' ' << l->name(); l = Logger::enumerate(l); } cerr << endl; throw Cleanup(3); } } } jigdo-0.7.3/src/util/log.hh0000644000175000017500000002134710261525207015304 0ustar richardrichard/* $Id: log.hh,v 1.10 2005/07/02 14:53:59 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Logfile / debugging output The (Debug)Logger class shouldn't be used directly, only via the macros. The following: DEBUG_UNIT("unitname") ensures that debug("format %1", arg) either prints the debug message (if DEBUG is 1), or does nothing at all (if DEBUG is 0). "Nothing at all" really means nothing, not even evaluating the argument expressions - at least if variable-argument preprocessor macros are supported. IMPORTANT: The expressions passed to debug() must not have any side-effects if the program is to behave identically with DEBUG=0 and DEBUG=1. The following: #undef debug namespace { Logger debug("unitname"); } Is like DEBUG_UNIT, except that the calls to debug() are always compiled in, regardless of the setting of DEBUG. Don't forget the #undef debug! Use LOCAL_DEBUG_UNIT to define "debug" in the current scope/namespace. With DEBUG_TO(FooBar::debug), you can use a non-local debug object (e.g. defined in a header somewhere; the macro will define a local "debug" reference to FooBar::debug. Making direct calls to FooBar::debug would fail if DEBUG=0. Again, there's also a LOCAL_DEBUG_TO, to be used e.g. inside a function. A "LOCAL_DEBUG_UNIT_DECL;" line should be used inside class definitions to introduce a static debug object (or non-static dummy if DEBUG=0). In the corresponding .cc file, you will need to add the following: #if DEBUG Logger MyClass::debug("myclass"); #endif */ #ifndef LOG_HH #define LOG_HH #include #include #include #include //______________________________________________________________________ #if DEBUG # define DEBUG_UNIT(_name) namespace { Logger debug(_name); } # define LOCAL_DEBUG_UNIT(_name) Logger debug(_name); # define LOCAL_DEBUG_UNIT_DECL static Logger debug; # define DEBUG_TO(_realobject) namespace { Logger& debug(_realobject); } # define LOCAL_DEBUG_TO(_realobject) Logger& debug(_realobject); #else # if HAVE_VARMACRO # define DEBUG_UNIT(_name) # define LOCAL_DEBUG_UNIT(_name) # define LOCAL_DEBUG_UNIT_DECL # define DEBUG_TO(_realobject) # define LOCAL_DEBUG_TO(_realobject) # if defined(__GNUC__) && __GNUC__ < 3 # define debug(_args...) do { } while (false) # else # define debug(...) do { } while (false) # endif # else # define DEBUG_UNIT(_name) namespace { IgnoreLogger debug; } # define LOCAL_DEBUG_UNIT(_name) IgnoreLogger debug; # define LOCAL_DEBUG_UNIT_DECL IgnoreLogger debug; # define DEBUG_TO(_realobject) namespace { IgnoreLogger debug; } # define LOCAL_DEBUG_TO(_realobject) IgnoreLogger debug; /* Var-arg macros not supported, we have to define a dummy class. Disadvantage: Arguments of debug() will be evaluated even in non-debug builds. */ struct IgnoreLogger : NoCopy { struct S { template S(const X&) { } }; bool enabled() const { return false; } operator bool() const { return false; } void operator()(const char*) const { } void operator()(const char*, S) const { } void operator()(const char*, S, S) const { } void operator()(const char*, S, S, S) const { } void operator()(const char*, S, S, S, S) const { } void operator()(const char*, S, S, S, S, S) const { } void operator()(const char*, S, S, S, S, S, S) const { } void operator()(const char*, S, S, S, S, S, S, S) const { } void operator()(const char*, S, S, S, S, S, S, S, S) const { } void operator()(const char*, S, S, S, S, S, S, S, S, S) const { } }; # endif #endif //______________________________________________________________________ /** Usually created by the DEBUG_UNIT macro, with an instance name of "debug" - an object which can be called to output debugging info. */ class Logger : NoCopy { public: # ifndef DOXYGEN_SKIP /* Logged strings are output via a OutputFunction* pointer. */ typedef void (OutputFunction)(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]); # endif /** Default output function prints to stderr */ static void defaultPut(const string& unitName, unsigned char unitNameLen, const char* format, int args, const Subst arg[]); /** Replace the output function */ static void setOutputFunction(OutputFunction* newOut) { output = newOut; } /** Register a compilation unit. All Loggers MUST *MUST* be static objects! unitName must remain valid during the lifetime of all Loggers, best make it a string constant. */ Logger(const char* unitName, bool enabled = false); bool enabled() const { return enabledVal; } operator bool() const { return enabledVal; } const char* name() const { return unitNameVal; } /** Enable/disable messages for specific units. By default, messages are disabled. @param unitName Name, or null for all units @param enable true to enable, false to disable @return true if successful (i.e. unit exists) */ static bool setEnabled(const char* unitName, bool enable = true); /** Walk through list of registered Logger objects. Call without arg to start, then call with returned value til 0 is returned: Logger* l = Logger::enumerate(); while (l != 0) { cerr << ' ' << l->name(); l = Logger::enumerate(l); } */ static inline Logger* enumerate(Logger* l = 0) { return (l ? l->next : list); } void operator()(const char* format) const { if (enabled()) put(format, 0, 0); } void operator()(const char* format, Subst a) const { if (enabled()) put(format, 1, &a); } void operator()(const char* format, Subst a, Subst b) const { if (!enabled()) return; Subst arg[] = { a, b }; put(format, 2, arg); } void operator()(const char* format, Subst a, Subst b, Subst c) const { if (!enabled()) return; Subst arg[] = { a, b, c }; put(format, 3, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d) const { if (!enabled()) return; Subst arg[] = { a, b, c, d }; put(format, 4, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e) const { if (!enabled()) return; Subst arg[] = { a, b, c, d, e }; put(format, 5, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f) const { if (!enabled()) return; Subst arg[] = { a, b, c, d, e, f }; put(format, 6, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g) const { if (!enabled()) return; Subst arg[] = { a, b, c, d, e, f, g }; put(format, 7, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g, Subst h) const { if (!enabled()) return; Subst arg[] = { a, b, c, d, e, f, g, h }; put(format, 8, arg); } void operator()(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g, Subst h, Subst i) const { if (!enabled()) return; Subst arg[] = { a, b, c, d, e, f, g, h, i }; put(format, 9, arg); } /** Scan value of the --debug cmd line option. It is either missing (empty) or a comma-separated list of words (we also allow spaces). Each word can be preceded by a '~' for negation (i.e. disable debug messages rather than enable them). The word is the name of a compilation unit, or one of the special values "all" or "help". */ static void scanOptions(const string& s, const char* binName); private: static OutputFunction* output; void put(const char* format, int args, const Subst arg[]) const { output(unitNameVal, unitNameLen, format, args, arg); } static Logger* list; // Linked list of registered units static string buf; // Temporary buffer for output string, printed at endl const char* unitNameVal; unsigned char unitNameLen; bool enabledVal; // only print messages if true Logger* next; // Next in linked list, or null }; //______________________________________________________________________ /** The default debugging logger uses the unit name "general". If possible, you should define a static, per-compilation-unit logger with a more descriptive name and use that instead. */ extern Logger msg; //______________________________________________________________________ #endif jigdo-0.7.3/src/util/md5sum-test.cc0000644000175000017500000000771107735400630016703 0ustar richardrichard/* $Id: md5sum-test.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Quite secure 128-bit checksum #test-deps util/glibc-md5.o util/md5sum.o */ #include #include #include #include #include #include #include #include //______________________________________________________________________ namespace { int returnCode = 0; // MD5 test suite (see end of RFC1321) const byte t1[] = ""; const byte s1[] = "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e"; const byte t2[] = "a"; const byte s2[] = "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8\x31\xc3\x99\xe2\x69\x77\x26\x61"; const byte t3[] = "abc"; const byte s3[] = "\x90\x01\x50\x98\x3c\xd2\x4f\xb0\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"; const byte t4[] = "message digest"; const byte s4[] = "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"; const byte t5[] = "abcdefghijklmnopqrstuvwxyz"; const byte s5[] = "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"; const byte t6[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const byte s6[] = "\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"; const byte t7[] = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"; const byte s7[] = "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55\xac\x49\xda\x2e\x21\x07\xb6\x7a"; const byte sAll[] = "\x6f\xec\x75\xd4\xe7\xfc\xd7\xe9\x66\x46\xb4\xc7\xaf\x96\xbc\xe2"; const char* const hexDigits = "0123456789abcdef"; } //______________________________________________________________________ string toHex(const byte* sum) { string result; for (int i = 0; i < 16; ++i) { result += hexDigits[sum[i] >> 4]; result += hexDigits[sum[i] & 0xfU]; } return result; } void compare(const byte* suite, const byte* mine) { for (int i = 0; i < 16; ++i) { if (suite[i] != mine[i]) { msg("ERROR: Expected %1, but got %2", toHex(suite), toHex(mine)); returnCode = 1; return; } } msg("OK: %1", toHex(suite)); } void printBlockSums(size_t blockSize, const char* fileName) { bifstream file(fileName, ios::binary); byte buf[blockSize]; byte* bufEnd = buf + blockSize; while (file) { // read another block byte* cur = buf; while (cur < bufEnd && file) { readBytes(file, cur, bufEnd - cur); // Fill buffer cur += file.gcount(); } MD5Sum sum; sum.update(buf, cur - buf).finishForReuse(); cout << ' ' << sum << endl; } exit(0); } int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); if (argc == 3) { // 2 cmdline args, blocksize and filename. Print RsyncSums of all blocks printBlockSums(atoi(argv[1]), argv[2]); exit(0); } const byte* sum; MD5Sum x; MD5Sum all; sum = x.reset().update(t1, sizeof(t1) - 1).finish().digest(); compare(s1, sum); all.update(t1, sizeof(t1) - 1); sum = x.reset().update(t2, sizeof(t2) - 1).finish().digest(); compare(s2, sum); all.update(t2, sizeof(t2) - 1); sum = x.reset().update(t3, sizeof(t3) - 1).finish().digest(); compare(s3, sum); all.update(t3, sizeof(t3) - 1); sum = x.reset().update(t4, sizeof(t4) - 1).finish().digest(); compare(s4, sum); all.update(t4, sizeof(t4) - 1); sum = x.reset().update(t5, sizeof(t5) - 1).finish().digest(); compare(s5, sum); all.update(t5, sizeof(t5) - 1); sum = x.reset().update(t6, sizeof(t6) - 1).finish().digest(); compare(s6, sum); all.update(t6, sizeof(t6) - 1); sum = x.reset().update(t7, sizeof(t7) - 1).finish().digest(); compare(s7, sum); all.update(t7, sizeof(t7) - 1); sum = all.finish().digest(); compare(sAll, sum); return returnCode; } jigdo-0.7.3/src/util/md5sum.cc0000644000175000017500000000727610065372403015730 0ustar richardrichard/* $Id: md5sum.cc,v 1.4 2004/06/20 20:35:15 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ "Ported" to C++ by RA. Uses glibc code for the actual algorithm. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Quite secure 128-bit checksum */ #include #include #include #include #include #include //______________________________________________________________________ void MD5Sum::ProgressReporter::error(const string& message) { cerr << message << endl; } void MD5Sum::ProgressReporter::info(const string& message) { cerr << message << endl; } void MD5Sum::ProgressReporter::readingMD5(uint64, uint64) { } MD5Sum::ProgressReporter MD5Sum::noReport; //______________________________________________________________________ MD5Sum::MD5Sum(const MD5Sum& md) { if (md.p == 0) { p = 0; for (int i = 0; i < 16; ++i) sum[i] = md.sum[i]; } else { p = new md5_ctx(); *p = *md.p; } } //________________________________________ // NB must work with self-assign MD5Sum& MD5Sum::operator=(const MD5Sum& md) { # if DEBUG finished = md.finished; # endif if (md.p == 0) { delete p; p = 0; for (int i = 0; i < 16; ++i) sum[i] = md.sum[i]; } else { if (p == 0) p = new md5_ctx(); *p = *md.p; } return *this; } //______________________________________________________________________ string MD5::toString() const { Base64String m; m.write(sum, 16).flush(); return m.result(); } //______________________________________________________________________ bool MD5::operator_less2(const MD5& x) const { if (sum[1] < x.sum[1]) return true; if (sum[1] > x.sum[1]) return false; if (sum[2] < x.sum[2]) return true; if (sum[2] > x.sum[2]) return false; if (sum[3] < x.sum[3]) return true; if (sum[3] > x.sum[3]) return false; if (sum[4] < x.sum[4]) return true; if (sum[4] > x.sum[4]) return false; if (sum[5] < x.sum[5]) return true; if (sum[5] > x.sum[5]) return false; if (sum[6] < x.sum[6]) return true; if (sum[6] > x.sum[6]) return false; if (sum[7] < x.sum[7]) return true; if (sum[7] > x.sum[7]) return false; if (sum[8] < x.sum[8]) return true; if (sum[8] > x.sum[8]) return false; if (sum[9] < x.sum[9]) return true; if (sum[9] > x.sum[9]) return false; if (sum[10] < x.sum[10]) return true; if (sum[10] > x.sum[10]) return false; if (sum[11] < x.sum[11]) return true; if (sum[11] > x.sum[11]) return false; if (sum[12] < x.sum[12]) return true; if (sum[12] > x.sum[12]) return false; if (sum[13] < x.sum[13]) return true; if (sum[13] > x.sum[13]) return false; if (sum[14] < x.sum[14]) return true; if (sum[14] > x.sum[14]) return false; if (sum[15] < x.sum[15]) return true; return false; } //______________________________________________________________________ uint64 MD5Sum::updateFromStream(bistream& s, uint64 size, size_t bufSize, ProgressReporter& pr) { uint64 nextReport = REPORT_INTERVAL; // When next to call reporter uint64 toRead = size; uint64 bytesRead = 0; vector buffer; buffer.resize(bufSize); byte* buf = &buffer[0]; // Read from stream and update *this while (s && !s.eof() && toRead > 0) { size_t n = (toRead < bufSize ? toRead : bufSize); readBytes(s, buf, n); n = s.gcount(); update(buf, n); bytesRead += n; toRead -= n; if (bytesRead >= nextReport) { pr.readingMD5(bytesRead, size); nextReport += REPORT_INTERVAL; } } return bytesRead; } jigdo-0.7.3/src/util/md5sum.fh0000644000175000017500000000074107735400630015732 0ustar richardrichard/* $Id: md5sum.fh,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ "Ported" to C++ by RA. Actual MD5 code taken from glibc This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Quite secure 128-bit checksum */ class MD5; class MD5Sum; jigdo-0.7.3/src/util/md5sum.hh0000644000175000017500000002416710226060300015725 0ustar richardrichard/* $Id: md5sum.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ "Ported" to C++ by RA. Actual MD5 code taken from glibc This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file 128-bit checksum, secure enough for our purposes */ #ifndef MD5SUM_HH #define MD5SUM_HH #ifndef INLINE # ifdef NOINLINE # define INLINE # else # define INLINE inline # endif #endif #include #include #include #include #include #include #include //______________________________________________________________________ /** Container for an already computed MD5Sum. Objects of this class are smaller than MD5Sum objects by one pointer. As soon as the checksum calculation of an MD5Sum object has finish()ed, the pointer is no longer needed. If you need to store a large number of calculated MD5Sums, it may be beneficial to assign the MD5Sum to an MD5 to save space. */ class MD5 { public: MD5() { } inline MD5(const MD5Sum& md); /** 16 bytes of MD5 checksum */ byte sum[16]; /** Allows you to treat the object exactly like a pointer to a byte array */ operator byte*() { return sum; } operator const byte*() const { return sum; } /** Assign an MD5Sum */ inline MD5& operator=(const MD5Sum& md); inline bool operator<(const MD5& x) const; /** Clear contents to zero */ inline MD5& clear(); /** Convert to string */ string toString() const; template inline Iterator serialize(Iterator i) const; template inline ConstIterator unserialize(ConstIterator i); inline size_t serialSizeOf() const { return 16; } // Default copy ctor private: bool operator_less2(const MD5& x) const; static const byte zero[16]; }; inline bool operator==(const MD5& a, const MD5& b); inline bool operator!=(const MD5& a, const MD5& b) { return !(a == b); } /// Output MD5 as Base64 digest INLINE ostream& operator<<(ostream& s, const MD5& r); //______________________________________________________________________ /** A 128-bit, cryptographically strong message digest algorithm. Unless described otherwise, if a method returns an MD5Sum&, then this is a reference to the object itself, to allow chaining of calls. */ class MD5Sum { friend class MD5; public: class ProgressReporter; /** Initialise the checksum */ inline MD5Sum(); /** Initialise with another checksum instance */ MD5Sum(const MD5Sum& md); ~MD5Sum() { delete p; } /** Assign another checksum instance */ MD5Sum& operator=(const MD5Sum& md); /** Tests for equality. Note: Will only return true if both message digest operations have been finished and their MD5 sums are the same. */ inline bool operator==(const MD5Sum& md) const; inline bool operator!=(const MD5Sum& md) const; inline bool operator==(const MD5& md) const { return sum == md; } inline bool operator!=(const MD5& md) const { return sum != md; } /** Reset checksum object to the same state as immediately after its creation. You must call when reusing an MD5Sum object - call it just before the first update() for the new checksum. */ inline MD5Sum& reset(); /** Process bytes with the checksum algorithm. May lead to some bytes being temporarily buffered internally. */ inline MD5Sum& update(const byte* mem, size_t len); /// Add a single byte. NB, not implemented efficiently ATM inline MD5Sum& update(byte x) { update(&x, 1); return *this; } /** Process remaining bytes in internal buffer and create the final checksum. @return Pointer to the 16-byte checksum. */ inline MD5Sum& finish(); /** Exactly the same behaviour as finish(), but is more efficient if you are going to call reset() again in the near future to re-use the MD5Sum object. @return Pointer to the 16-byte checksum. */ inline MD5Sum& finishForReuse(); /** Deallocate buffers like finish(), but don't generate the final sum */ inline MD5Sum& abort(); /** Return 16 byte buffer with checksum. Warning: Returns junk if checksum not yet finish()ed or flush()ed. */ inline const byte* digest() const; /** Convert to string */ INLINE string toString() const; /** Read data from file and update() this checksum with it. @param s The stream to read from @param size Total number of bytes to read @param bufSize Size of temporary read buffer @param pr Reporter object @return Number of bytes read (==size if no error) */ uint64 updateFromStream(bistream& s, uint64 size, size_t bufSize = 128*1024, ProgressReporter& pr = noReport); /* Serializing an MD5Sum is only allowed after finish(). The serialization is compatible with that of MD5. */ template inline Iterator serialize(Iterator i) const; template inline ConstIterator unserialize(ConstIterator i); inline size_t serialSizeOf() const { return sum.serialSizeOf(); } private: struct md5_ctx { uint32 A, B, C, D; uint32 total[2]; uint32 buflen; char buffer[128] __attribute__ ((__aligned__ (__alignof__ (uint32)))); }; /// Default reporter: Only prints error messages to stderr static ProgressReporter noReport; // These functions are the original glibc API static void md5_init_ctx(md5_ctx* ctx); static void md5_process_bytes(const void* buffer, size_t len, struct md5_ctx* ctx); static byte* md5_finish_ctx(struct md5_ctx* ctx, byte* resbuf); static byte* md5_read_ctx(const md5_ctx *ctx, byte* resbuf); static void md5_process_block(const void* buffer, size_t len, md5_ctx* ctx); MD5 sum; struct md5_ctx* p; // null once MD creation is finished # if DEBUG /* After finish(ForReuse)(), must call reset() before the next update(). OTOH, must only call digest() after finish(). Enforce this rule by keeping track of the state. */ bool finished; # endif }; inline bool operator==(const MD5& a, const MD5Sum& b) { return b == a; } inline bool operator!=(const MD5& a, const MD5Sum& b) { return b != a; } /// Output MD5Sum as Base64 digest INLINE ostream& operator<<(ostream& s, const MD5Sum& r); //______________________________________________________________________ /** Class allowing JigdoCache to convey information back to the creator of a JigdoCache object. */ class MD5Sum::ProgressReporter { public: virtual ~ProgressReporter() { } /// General-purpose error reporting. virtual void error(const string& message); /// Like error(), but for purely informational messages. virtual void info(const string& message); /// Called when data is read during updateFromStream() virtual void readingMD5(uint64 offInStream, uint64 size); }; //______________________________________________________________________ bool MD5Sum::operator==(const MD5Sum& md) const { # if DEBUG Paranoid(this->finished && md.finished); # endif return sum == md.sum; } bool MD5Sum::operator!=(const MD5Sum& md) const { # if DEBUG Paranoid(this->finished && md.finished); # endif return sum != md.sum; } MD5Sum::MD5Sum() { p = new md5_ctx(); md5_init_ctx(p); # if DEBUG finished = false; # endif } MD5Sum& MD5Sum::reset() { if (p == 0) p = new md5_ctx(); md5_init_ctx(p); # if DEBUG finished = false; # endif return *this; } MD5Sum& MD5Sum::update(const byte* mem, size_t len) { Paranoid(p != 0); # if DEBUG Paranoid(!finished); // Don't forget to call reset() before update() # endif md5_process_bytes(mem, len, p); return *this; } MD5Sum& MD5Sum::finish() { Paranoid(p != 0 ); md5_finish_ctx(p, sum); delete p; p = 0; # if DEBUG finished = true; # endif return *this; } MD5Sum& MD5Sum::finishForReuse() { Paranoid(p != 0 ); md5_finish_ctx(p, sum); # if DEBUG finished = true; # endif return *this; } MD5Sum& MD5Sum::abort() { delete p; p = 0; # if DEBUG finished = false; # endif return *this; } const byte* MD5Sum::digest() const { # if DEBUG Paranoid(finished); // Call finish() first # endif return sum.sum; } template inline Iterator MD5Sum::serialize(Iterator i) const { # if DEBUG Paranoid(finished ); // Call finish() first # endif return sum.serialize(i); } template inline ConstIterator MD5Sum::unserialize(ConstIterator i) { # if DEBUG finished = true; # endif return sum.unserialize(i); } //____________________ MD5& MD5::operator=(const MD5Sum& md) { # if DEBUG Paranoid(md.finished); // Call finish() first # endif *this = md.sum; return *this; } bool MD5::operator<(const MD5& x) const { if (sum[0] < x.sum[0]) return true; if (sum[0] > x.sum[0]) return false; return operator_less2(x); } // inline bool operator<(const MD5& a, const MD5& b) { // return a.operator<(b); // } MD5::MD5(const MD5Sum& md) { *this = md.sum; } bool operator==(const MD5& a, const MD5& b) { // How portable is this? return memcmp(a.sum, b.sum, 16 * sizeof(byte)) == 0; # if 0 return a.sum[0] == b.sum[0] && a.sum[1] == b.sum[1] && a.sum[2] == b.sum[2] && a.sum[3] == b.sum[3] && a.sum[4] == b.sum[4] && a.sum[5] == b.sum[5] && a.sum[6] == b.sum[6] && a.sum[7] == b.sum[7] && a.sum[8] == b.sum[8] && a.sum[9] == b.sum[9] && a.sum[10] == b.sum[10] && a.sum[11] == b.sum[11] && a.sum[12] == b.sum[12] && a.sum[13] == b.sum[13] && a.sum[14] == b.sum[14] && a.sum[15] == b.sum[15]; # endif } MD5& MD5::clear() { byte* x = sum; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0; return *this; } template inline Iterator MD5::serialize(Iterator i) const { for (int j = 0; j < 16; ++j) { *i = sum[j]; ++i; } return i; } template inline ConstIterator MD5::unserialize(ConstIterator i) { for (int j = 0; j < 16; ++j) { sum[j] = *i; ++i; } return i; } #ifndef NOINLINE # include /* NOINLINE */ #endif #endif jigdo-0.7.3/src/util/md5sum.ih0000644000175000017500000000213310065372403015726 0ustar richardrichard/* $Id: md5sum.ih,v 1.3 2004/06/20 20:35:15 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Quite secure 128-bit checksum */ #ifndef MD5SUM_IH #define MD5SUM_IH #include //______________________________________________________________________ ostream& operator<<(ostream& s, const MD5& r) { Base64String m; m.write(r.sum, 16).flush(); s << m.result(); return s; } string MD5Sum::toString() const { const byte* d = digest(); if (d == 0) { return "[MD5_creation_not_finished]"; } else { Base64String m; m.write(d, 16).flush(); return m.result(); } } ostream& operator<<(ostream& s, const MD5Sum& r) { const byte* d = r.digest(); if (d == 0) { s << "[MD5_creation_not_finished]"; } else { Base64String m; m.write(d, 16).flush(); s << m.result(); } return s; } #endif jigdo-0.7.3/src/util/mimestream-test.cc0000644000175000017500000000661307735400630017634 0ustar richardrichard/* $Id: mimestream-test.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Convert binary data to/from ASCII using Base64 encoding #test-deps */ #include #include #include #include #include //______________________________________________________________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); const char* correct = "V29yYmxlLCB3b2ZmbGUsIN_f3w"; Base64String m; char str[] = "Worble, woffle, ßßß"; for (char* s = str; *s != 0; ++s) m << *s; m.flush(); Assert(m.result() == correct); m.result().erase(); for (char* s = str; *s != 0; ++s) m << (*s); m << flush; Assert(m.result() == correct); m.result().erase(); m.write(str, sizeof(str) - 1); m << flush << flush; Assert(m.result() == correct); m.result().erase(); m.write((unsigned char*)str, sizeof(str) - 1); m.flush(); Assert(m.result() == correct); m.result().erase(); m.write((signed char*)str, sizeof(str) - 1); m.flush(); Assert(m.result() == correct); m.result().erase(); m.write((void*)str, sizeof(str) - 1); m.flush(); Assert(m.result() == correct); m.result().erase(); m << str << flush; Assert(m.result() == correct); m.result().erase(); m << (unsigned char*)str; m.flush(); Assert(m.result() == correct); m.result().erase(); m << (signed char*)str; m.flush(); Assert(m.result() == correct); m.result().erase(); m << (void*)str; m.flush(); Assert(m.result() == correct); //____________________ correct = "H7pU8Q"; m.result().erase(); uint32 i = 0xf154ba1f; m.put(i).flush(); Assert(m.result() == correct); m.result().erase(); m << i; m.flush(); Assert(m.result() == correct); m.result().erase(); m << char(0x1f) << (unsigned char)(0xba) << 0xfff54 << '\xf1'; m.flush(); Assert(m.result() == correct); //____________________ /* Try many of the 16.7 million ways of a "3 byte" <=> "4 ASCII chars" mapping */ byte x[3*256]; for (unsigned k = 0; k < 256; ++k) x[k * 3 + 2] = byte(k); Base64String toAscii; Base64StringI toBin; unsigned j = 0; for (unsigned i = 0; i < 256; i += 57) { for (unsigned k = 0; k < 256; ++k) x[k * 3 + 0] = i; j = j & 0xffU; for (; j < 256; j += 43) { for (unsigned k = 0; k < 256; ++k) x[k * 3 + 1] = j; // Binary -> ASCII toAscii.write(x, 3*256).flush(); Assert(toAscii.result().length() == 4*256); // ASCII -> binary toBin.put(toAscii.result().data(), 4*256); vector& r = toBin.result(); Assert(r.size() == 3*256); for (unsigned k = 0; k < 256; ++k) { if (r[k*3] != x[k*3] || r[k*3+1] != x[k*3+1] || r[k*3+2] != x[k*3+2]) { cout << endl; cout << "x=["< #include #include #include #include #include //______________________________________________________________________ /** Convert binary data to Base64 and output. Note that this does *not* implement the RFC2045 requirement that lines of text be no longer than 76 characters each. Furthermore, by default the data is not terminated with any '='. Output is a class offering the following: void put(char c); // Output one ASCII character typedef implementation_defined ResultType; ResultType result(); // Is called by Base64Out::result() */ template class Base64Out { public: Base64Out() : bits(0) { } typename Output::ResultType result() { return out.result(); } /** Output operators */ Base64Out& operator<<(char x) { return put(x); } Base64Out& operator<<(signed char x) { return put(x); } Base64Out& operator<<(unsigned char x) { return put(x); } /** Output the low 8 bits of an integer */ Base64Out& operator<<(int x) { return put(x); } /** Output 32 bit integer in little-endian order */ Base64Out& operator<<(uint32 x) { return put(x); } /** Output null-terminated string */ inline Base64Out& operator<<(const char* x); inline Base64Out& operator<<(const signed char* x); Base64Out& operator<<(const unsigned char* x); inline Base64Out& operator<<(const void* x); /** Output 1 character */ inline Base64Out& put(unsigned char x); inline Base64Out& put(signed char x); /** Output the low 8 bits of an integer */ inline Base64Out& put(int x); inline Base64Out& put(char x); /** Output 32 bit integer in little-endian order */ Base64Out& put(uint32 x); /** Output n characters */ inline Base64Out& write(const char* x, unsigned n); inline Base64Out& write(const signed char* x, unsigned n); Base64Out& write(const unsigned char* x, unsigned n); inline Base64Out& write(const void* x, unsigned n); /** This is *not* a no-op. */ Base64Out& flush(); /** Output the appropriate number of '=' characters (0, 1 or 2) given how many bytes were fed into the Base64Out object. */ Base64Out& trailer(streamsize n); /** A bit of a hack for jigdo: If true, switch from Base64 output to hexadecimal output. Default is false. */ static bool hex; private: /* String for MIME base64 encoding. Not entirely standard because b64 strings are used as filenames by jigdo. Additionally, "+" or "/" looks weird in the .jigdo file. */ static const char* const code; static const char* const hexCode; int bits; uint32 data; Output out; }; //______________________________________________________________________ /** Output base64 data to a std::string. */ class Base64StringOut { public: void put(char c) { val += c; } typedef string& ResultType; string& result() { return val; } const string& result() const { return val; } private: string val; }; /** A string which you can output to with "str << 1234" or "str.write(buf, 4096)". */ typedef Base64Out Base64String; //______________________________________________________________________ // Support functions to allow things like "b64Stream << flush;" template inline Base64Out& flush(Base64Out& s) { return s.flush(); } template inline Base64Out& operator<<(Base64Out& s, Base64Out& (*m)(Base64Out&)) { return (*m)(s); } //______________________________________________________________________ template bool Base64Out::hex = false; template const char* const Base64Out::code = //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; template const char* const Base64Out::hexCode = "0123456789abcdef"; template Base64Out& Base64Out::operator<<(const char* x) { return (*this) << reinterpret_cast(x); } template Base64Out& Base64Out::operator<<(const signed char* x) { return (*this) << reinterpret_cast(x); } template Base64Out& Base64Out::operator<<(const void* x) { return (*this) << static_cast(x); } template Base64Out& Base64Out::put(unsigned char x) { if (hex) { out.put(hexCode[(x >> 4) & 15U]); out.put(hexCode[x & 15U]); return *this; } data = (data << 8) | x; bits += 2; // plus 8 new bits, less 6 which we output in next line out.put(code[(data >> bits) & 63U]); if (bits >= 6) { //8? bits -= 6; out.put(code[(data >> bits) & 63U]); } return *this; } template Base64Out& Base64Out::put(signed char x) { return put(static_cast(x)); } template Base64Out& Base64Out::put(char x) { return put(static_cast(x)); } template Base64Out& Base64Out::put(int x) { return put(static_cast(x)); } template Base64Out& Base64Out::write(const char* x, unsigned n) { return write(reinterpret_cast(x), n); } template Base64Out& Base64Out::write(const signed char* x, unsigned n) { return write(reinterpret_cast(x), n); } template Base64Out& Base64Out::write(const void* x, unsigned n) { return write(static_cast(x), n); } template Base64Out& Base64Out::put(uint32 x) { (*this) .put(static_cast(x & 0xff)) .put(static_cast((x >> 8) & 0xff)) .put(static_cast((x >> 16) & 0xff)) .put(static_cast((x >> 24) & 0xff)); return *this; } template Base64Out& Base64Out::flush() { if (bits > 0) { data <<= 6 - bits; out.put(code[data & 63U]); } bits = 0; return *this; } template Base64Out& Base64Out::trailer(streamsize n) { int rest = n % 3; if (rest == 1) out.put('='); if (rest >= 1) out.put('='); return *this; } // Output null-terminated string template Base64Out& Base64Out::operator<<(const unsigned char* x) { while (*x != '\0') (*this) << static_cast(*x++); return *this; } // Output n characters template Base64Out& Base64Out::write(const unsigned char* x, unsigned n) { for (unsigned i = 0; i < n; ++i) (*this) << static_cast(*x++); return *this; } //______________________________________________________________________ /** Convert a series of Base64 ASCII strings into binary data. Output is a class offering the following: - void put(byte b); // Output one byte of binary data - typedef implementation_defined ResultType; - ResultType result(); // Is called by Base64In::result() */ template class Base64In { public: Base64In() : bits(0), data(0) { } typename Output::ResultType result() { return out.result(); } /** Output operators, for handing in the ASCII Base64 string. */ Base64In& operator<<(char x) { return put(x); } /** Convert null-terminated string */ inline Base64In& operator<<(const char* x); /** Convert string */ inline Base64In& operator<<(const string& x); /** Output 1 character */ inline Base64In& put(char x); /** Convert given number of characters */ Base64In& put(const char* x, unsigned n); /** Return the object to its initial state */ void reset() { bits = 0; } private: static const byte table[]; int bits; uint32 data; Output out; }; //______________________________________________________________________ /** Helper class for mimestream-test.cc, to convert base64 characters to bytes. */ class Base64StringIn { public: void put(byte b) { val.push_back(b); } typedef vector& ResultType; vector& result() { return val; } private: vector val; }; typedef Base64In Base64StringI; //______________________________________________________________________ // Inverse mapping for both of these: //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; #define x 255 template const byte Base64In::table[] = { // ! " # $ % & ' ( ) * + , - . / x, x, x, x, x, x, x, x, x, x, x, 62, x, 62, x, 63, //0 1 2 3 4 5 6 7 8 9 : ; < = > ? 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, x, x, x, x, x, x, //@ A B C D E F G H I J K L M N O x, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, //P Q R S T U V W x Y Z [ \ ] ^ _ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, x, x, x, x, 63, //` a b c d e f g h i j k l m n o x, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, //p q r s t u v w x y z { | } ~ DEL 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, x, x, x, x, x }; #undef x template Base64In& Base64In::operator<<(const char* x) { unsigned len = strlen(x); return put(x, len); } template Base64In& Base64In::operator<<(const string& x) { return put(x.data(), x.length()); } template Base64In& Base64In::put(char x) { return put(&x, 1); } template Base64In& Base64In::put(const char* x, unsigned n) { --x; while (n > 0) { --n; ++x; unsigned code = static_cast(*x); if (code < 32 || code > 127) continue; // Just ignore invalid characters code = table[code - 32]; if (code > 63) continue; data = (data << 6) | code; bits += 6; if (bits >= 8) { bits -= 8; out.put(static_cast((data >> bits) & 255U)); } } return *this; } #endif jigdo-0.7.3/src/util/nocopy.hh0000644000175000017500000000155210120166076016025 0ustar richardrichard/* $Id: nocopy.hh,v 1.2 2004/09/09 23:50:22 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ /** @file A class which prevents derived classes from being copied. */ #ifndef NOCOPY_HH #define NOCOPY_HH /** To be used as a base class only - prevents that the derived class can be copied. It would be equivalent to add a private copy ctor and a private assignment operator to the derived class, but deriving from NoCopy saves typing and looks cleaner. */ class NoCopy { protected: NoCopy() { } ~NoCopy() { } private: NoCopy(const NoCopy&); const NoCopy& operator=(const NoCopy&); }; #endif jigdo-0.7.3/src/util/progress.cc0000644000175000017500000002264207717143066016367 0ustar richardrichard/* $Id: progress.cc,v 1.7 2003/08/15 11:38:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Statistics: How many % of download done, average speed, ETA A "bug": If you pause and then resume the download after a while, speed() will return values which are unusually high, higher than your bandwidth. I think this is because of the way jigdo pauses its downloads; just by not select()ing the connection's buffers. I suspect this means that quite a lot of data queues up in the OS's buffers, and this data is then delivered to us in one big chunk when we resume. */ #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("progress") /** Append to s something like "9999B", "9999kB", "9999MB", "99.9MB" */ void Progress::appendSize(string* s, uint64 size) { if (size < 10000) { append(*s, size).append(_("B")); return; } if (size < 1024 * 100) { append(*s, static_cast(size) / 1024.0).append(_("kB")); return; } if (size < 1024 * 10000) { append(*s, size / 1024).append(_("kB")); return; } if (size < 1048576 * 100) { append(*s, static_cast(size) / 1048576.0).append(_("MB")); return; } append(*s, size / 1048576).append(_("MB")); return; } //________________________________________ void Progress::appendSizes(string* s, uint64 size, uint64 total) { if (total <= size) { total = size; appendSize(s, size); } else { //append(s, 100.0 * size / total).append("%, "); appendSize(s, size); *s += _(" of "); appendSize(s, total); } } //________________________________________ void Progress::appendProgress(string* s) const { if (dataSizeVal == 0) { appendSize(s, currentSizeVal); } else { append(*s, 100.0 * currentSizeVal / dataSizeVal).append(_("%, ")); appendSize(s, currentSizeVal); *s += _(" of "); appendSize(s, dataSizeVal); } *s += " ("; append(*s, currentSizeVal); if (dataSizeVal > 0) { *s += _(" of "); append(*s, dataSizeVal); } *s += _(" bytes)"); } //________________________________________ void Progress::appendSpeed(string* s, int speed, int timeLeft) const { if (timeLeft >= 0) { const int BUF_LEN = 64; static char buf[BUF_LEN]; int hr = timeLeft / 3600; snprintf(buf, BUF_LEN, _("%02d:%02d:%02d remaining"), hr, timeLeft / 60 - hr * 60, timeLeft % 60); buf[BUF_LEN - 1] = '\0'; *s += buf; } if (speed >= 0) { if (!(*s).empty()) *s += _(", "); appendSize(s, speed); *s += _("/sec"); } } //______________________________________________________________________ void Progress::reset() { currSlot = 0; calcSlot = 0; currSlotLeft = SPEED_TICK_INTERVAL; prevTimeLeft = -TIME_LEFT_GROW_THRESHOLD; for (int i = 0; i < SPEED_SLOTS; ++i) { slotStart[i].tv_sec = slotStart[i].tv_usec = 0; slotSizeVal[i] = 0; } //startTime.tv_sec = 0; g_get_current_time(&slotStart[0]); slotSizeVal[0] = currentSize(); } //______________________________________________________________________ /* The way the speed of the connection is measured is quite complicated, because it attempts to do two things equally well: 1) Show the *average* throughput accurately even on bursty connections, 2) React quickly when data suddenly stops flowing. */ void Progress::tick(const GTimeVal& now, int millisecs) { // if (slotStart[calcSlot].tv_sec == 0) // slotStart[calcSlot] = now; currSlotLeft -= millisecs; if (currSlotLeft > 0) return; // New slot starts now /* Compare the throughput of the slot that was just finished (at [currSlot]) with the next older one. If speed between these two differs too much (i.e. sudden large burst of data / data stops flowing), set calcSlot=currSlot, i.e. from now on calculate speed from youngest slot and ignore older slots. */ int oldSlot = currSlot - 1; if (oldSlot < 0) oldSlot = SPEED_SLOTS - 1; if (slotStart[oldSlot].tv_sec != 0) { uint64 slotSize = currentSizeVal - slotSizeVal[currSlot]; uint64 oldSlotSize = slotSizeVal[currSlot] - slotSizeVal[oldSlot]; uint64 sizeChange = 100 * slotSize / (oldSlotSize + 1); //cerr << "sizeChange=" << sizeChange << " slotSize=" << slotSize // << " oldSlotSize=" << oldSlotSize << endl; if (sizeChange >= SPEED_MAX_GROW || sizeChange <= SPEED_MIN_SHRINK) calcSlot = currSlot; // Speed differs too much } // Increase currSlot, overwriting oldest slot //cerr << "currSlot=" << currSlot << " oldSlot=" << oldSlot // << " calcSlot=" << calcSlot << endl; currSlotLeft += SPEED_TICK_INTERVAL; if (++currSlot == SPEED_SLOTS) currSlot = 0; // Init new slot's entries slotSizeVal[currSlot] = currentSizeVal; slotStart[currSlot] = now; // If calcSlot was oldest slot, increase it so it's the oldest again if (currSlot == calcSlot) if (++calcSlot == SPEED_SLOTS) calcSlot = 0; } //______________________________________________________________________ int Progress::speed(const GTimeVal& now) const { //if (slotStart[calcSlot].tv_sec == 0) return -1; double elapsed = (now.tv_sec - slotStart[calcSlot].tv_sec) * 1000000.0; if (now.tv_usec >= slotStart[calcSlot].tv_usec) elapsed += now.tv_usec - slotStart[calcSlot].tv_usec; else elapsed -= slotStart[calcSlot].tv_usec - now.tv_usec; if (elapsed == 0.0) return -1; debug("speed: %1 bytes (%2 previously, %3 now) in %4 sec", currentSizeVal - slotSizeVal[calcSlot], slotSizeVal[calcSlot], currentSizeVal, elapsed / 1000000.0); int speed = static_cast( static_cast(currentSizeVal - slotSizeVal[calcSlot]) / elapsed * 1000000.0); return speed; } //________________________________________ /* In contrast to the numbers for speed measurement, do not "reset" the calculation slot e.g. if amount of data changes rapidly from one tick to the next. Instead, always try to use the oldest slot and only fall back to an earlier one if the download hasn't been running for long enough. */ int Progress::timeLeft(const GTimeVal& now) const { if (dataSizeVal <= currentSizeVal) return -1; int oldestSlot = currSlot + 1; if (oldestSlot == SPEED_SLOTS) oldestSlot = 0; while (slotStart[oldestSlot].tv_sec == 0) if (++oldestSlot == SPEED_SLOTS) oldestSlot = 0; //if (slotStart[oldestSlot].tv_sec == 0) oldestSlot = calcSlot; //if (slotStart[oldestSlot].tv_sec == 0) return -1; double elapsed = (now.tv_sec - slotStart[oldestSlot].tv_sec) * 1000000.0; if (elapsed == 0.0) return -1; if (now.tv_usec >= slotStart[oldestSlot].tv_usec) elapsed += now.tv_usec - slotStart[oldestSlot].tv_usec; else elapsed -= slotStart[oldestSlot].tv_usec - now.tv_usec; double speed = (currentSizeVal - slotSizeVal[oldestSlot]) / elapsed * 1000000.0; if (speed == 0.0) return -1; int remaining = static_cast( (dataSizeVal - currentSizeVal) / speed); /* Avoid sudden and frequent jumps in the reported time by not reporting a change if the newly calculated remaining time is a little higher than the previous one. */ if (remaining >= prevTimeLeft && remaining < prevTimeLeft + TIME_LEFT_GROW_THRESHOLD) return prevTimeLeft; prevTimeLeft = remaining; return remaining; } //______________________________________________________________________ ostream& Progress::put(ostream& s) const { static long startSec = 0; if (startSec == 0) { GTimeVal now; g_get_current_time(&now); startSec = now.tv_sec; } for (int i = 0; i < SPEED_SLOTS; ++i) { s << ' '; if (i == currSlot) s << "curr:"; if (i == calcSlot) s << "calc:"; if (slotStart[i].tv_sec == 0) s << '?'; else s << slotStart[i].tv_sec - startSec; s << '/' << slotSizeVal[i]; } return s; } //______________________________________________________________________ Progress* Progress::autoTickList = 0; int Progress::autoTickId = 0; void Progress::setAutoTick(bool enable) { if (autoTick() == enable) return; // No change in state if (enable) { // Have tick() called automatically for this object Paranoid(prev == 0 && next == 0); if (autoTickList != 0) { Paranoid(autoTickList->prev == 0); next = autoTickList; next->prev = this; } else { // First entry - register callback autoTickId = g_timeout_add(SPEED_TICK_INTERVAL, autoTickCallback, 0); debug("setAutoTick: registered"); } autoTickList = this; } else { // Stop calling tick() automatically for this object if (prev == 0) autoTickList = next; else prev->next = next; if (next != 0) next->prev = prev; prev = next = 0; if (autoTickList == 0) { // This was the last entry - unregister callback g_source_remove(autoTickId); autoTickId = 0; debug("setAutoTick: unregistered"); } } } gboolean Progress::autoTickCallback(gpointer) { debug("autoTickCallback"); GTimeVal now; g_get_current_time(&now); Progress* p = autoTickList; while (p != 0) { p->tick(now, SPEED_TICK_INTERVAL); p = p->next; } return TRUE; // "Call me again" } jigdo-0.7.3/src/util/progress.fh0000644000175000017500000000066307723507134016373 0ustar richardrichard/* $Id: progress.fh,v 1.2 2003/08/28 23:21:00 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Statistics: How many % of download done, average speed, ETA */ class Progress; jigdo-0.7.3/src/util/progress.hh0000644000175000017500000001413710120166076016365 0ustar richardrichard/* $Id: progress.hh,v 1.9 2004/09/09 23:50:22 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Statistics: How many % of download done, average speed, ETA */ #ifndef PROGRESS_HH #define PROGRESS_HH #include #include #include #include #include //______________________________________________________________________ /** Keep track of statistics: How many % of download done, average speed, ETA */ class Progress { public: inline Progress(); inline ~Progress(); /** Set amount of data fetched so far */ inline void setCurrentSize(uint64 x); /** Read amount of data fetched so far */ inline uint64 currentSize() const; /** Set total amount of data - default is 0 (unknown) */ inline void setDataSize(uint64 x); /** Read total amount of data - default is 0 (unknown) */ inline uint64 dataSize() const; /** Call this at regular intervals: At least every SPEED_TICK_INTERVAL milliseconds, but can also be more often, preferably in such a way that every nth call happens every SPEED_TICK_INTERVAL milliseconds. @param now Current time: {GTimeVal now;g_get_current_time(&now);} Use same "now" value for related calls to timeLeft(), tick() etc. @param millisecs Number of milliseconds elapsed since last call to this function (approximate) */ void tick(const GTimeVal& now, int millisecs); /** Register/unregister a callback function with glib which automatically calls tick() every SPEED_TICK_INTERVAL milliseconds. This only registers one function which traverses the list of all Progress objects with autoTick==true. ~Progress automatically unregisters the autotick callback if necessary. @param enable true to call tick() automatically, false to stop calling it. */ void setAutoTick(bool enable); /** Is autotick enabled for this object? */ inline bool autoTick(); /** Return estimated bytes/sec for (roughly) last few secs (-1 for unknown) */ int speed(const GTimeVal& now) const; /** Return estimated remaining time in seconds (-1 for unknown). Call *before* tick() or you'll throw away some accuracy. */ int timeLeft(const GTimeVal& now) const; /** Append to s something like "9999B", "9999kB", "9999MB", "99.9MB" */ static void appendSize(string* s, uint64 size); /** Append "50kB" if size not known, else "50kB of 10MB" */ static void appendSizes(string* s, uint64 size, uint64 total); /** Convenience method: Create long progress string like "50%, 50kB of 100kB (3333 of 6666 bytes)" */ void appendProgress(string* s) const; /** Convenience method: Create long string with speed and estimated time of arrival like "00:01:59 remaining, 100kB/sec" */ void appendSpeed(string* s, int speed, int timeLeft) const; /** Reset internal state of "time left" calculation. Does not touch currentSize or dataSize. Use this e.g. when continuing a download after it has been paused. Also records the current time to signify the start of the download, so don't wait too long between calling this and actually (re)starting the download. Important: Use setCurrentSize(0);reset(); and not the other way round, or the speed calculation will go belly up. */ void reset(); static const int SPEED_TICK_INTERVAL = 3000; ostream& put(ostream& s) const; private: // Bytes downloaded so far uint64 currentSizeVal; // Total bytes, or 0 for don't know uint64 dataSizeVal; /* Estimation of download speed for "x kBytes per sec" display: Basic idea is to calculate the average download rate for the last 30 secs (SPEED_TICK_INTERVAL/1000*SPEED_SLOTS) all the time. To do this, we need to take note at regular intervals (SPEED_TICK_INTERVAL) of how much we've downloaded so far, and store the value for later use in one of SPEED_SLOTS slots. The slots are a "ring buffer" - the oldest value is always overwritten by the newest. */ static const int SPEED_SLOTS = 10; // If speed grows by >=x% from newer slot to older, ignores older slots static const unsigned SPEED_MAX_GROW = 130; static const unsigned SPEED_MIN_SHRINK = 70; uint64 slotSizeVal[SPEED_SLOTS]; // Value of currentSizeVal at slot start GTimeVal slotStart[SPEED_SLOTS]; // Timestamp of start of slot int currSlot; // Rotates through 0..SPEED_SLOTS-1 int calcSlot; // Index of slot which is used for speed calculation int currSlotLeft; // Millisecs before a new slot is started /* Secs that timeLeft() must have *increased* by before the new value is reported. A *drop* of timeLeft() values is reported immediately. */ static const int TIME_LEFT_GROW_THRESHOLD = 5; mutable int prevTimeLeft; /* Prev/next in list of autotick objects, or either one null if at start/end of list, or both null if not in list, or both null if only object in list. Yeah, keep it nice and simple! */ Progress* prev; Progress* next; // Callback function which calls tick() on the objects static gboolean autoTickCallback(gpointer); // First object in list of autotick objects, or null if list empty static Progress* autoTickList; static int autoTickId; // ID of glib event source for autoTickCallback }; inline ostream& operator<<(ostream& s, const Progress& r); //______________________________________________________________________ Progress::Progress() : currentSizeVal(0), dataSizeVal(0), prev(0), next(0) { reset(); } Progress::~Progress() { setAutoTick(false); } void Progress::setCurrentSize(uint64 x) { currentSizeVal = x; } uint64 Progress::currentSize() const { return currentSizeVal; } void Progress::setDataSize(uint64 x) { dataSizeVal = x; } uint64 Progress::dataSize() const { return dataSizeVal; } ostream& operator<<(ostream& s, const Progress& r) { return r.put(s); } bool Progress::autoTick() { return prev != 0 || next != 0 || autoTickList == this; } #endif jigdo-0.7.3/src/util/random.cc0000644000175000017500000000735110262247425015774 0ustar richardrichard/* $Id: random.cc,v 1.1 2005/07/04 15:00:37 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Pseudo random number generation */ #include #include #include //______________________________________________________________________ void update(MD5Sum& md, uint32 x) { md.update(static_cast(x)); md.update(static_cast(x >> 8)); md.update(static_cast(x >> 16)); md.update(static_cast(x >> 24)); } //______________________________________________________________________ struct Rand { MD5Sum md; struct { uint32 nr; uint32 serial; MD5 r; } hashData; byte* rptr; // points to one of hashData.r's elements byte* rend; uint32 res; // Bit reservoir size_t bitsInRes; bool msg; Rand(uint32 nr, bool printMessages = false) { hashData.nr = nr; hashData.serial = 0; hashData.r.clear(); rptr = rend = &hashData.r.sum[0] + 16; res = 0; bitsInRes = 0; msg = printMessages; } // Create another 128 semi-random bits in md void thumbScrew(); // Return n semi-random bits, n <= 24 uint32 get(size_t n) { while (bitsInRes < n) { if (rptr == rend) thumbScrew(); res |= (*rptr++) << bitsInRes; bitsInRes += 8; } uint32 r = res & ((1 << n) - 1); res >>= n; bitsInRes -= n; return r; } // Return an integer in the range 0...n-1 uint32 rnd(size_t n) { return static_cast(static_cast(get(24)) * n / 0x1000000); } }; void Rand::thumbScrew() { md.reset(); update(md, hashData.nr); update(md, hashData.serial); md.update(&hashData.r.sum[0], 16 * sizeof(byte)); md.finishForReuse(); hashData.r = md; ++hashData.serial; rptr = &hashData.r.sum[0]; //cout << '<' << hashData.r << '>' << endl; } //______________________________________________________________________ int main(int argc, const char* argv[]) { if (argc <= 1) { cerr << "Syntax: " << argv[0] << " []\n" "nr-of-bytes can have a trailing `k' for kiloBytes\n" "If byteVal is present, not random bytes are written, but bytes with\n" "the specified value." << endl; return 1; } //____________________ // Parse args uint32 bytesArg = 0; const char* p = argv[1]; while (*p >= '0' && *p <= '9') { bytesArg = bytesArg * 10 + *p - '0'; ++p; } if (*p == 'k') { bytesArg *= 1024; ++p; } if (*p != '\0') { cerr << "Wrong format for argument 1: `" << argv[1] << '\'' << endl; return 1; } //cerr << bytesArg << endl; uint32 byteVal = 256; if (argc >= 3) { byteVal = 0; const char* p = argv[2]; while (*p >= '0' && *p <= '9') { byteVal = byteVal * 10 + *p - '0'; ++p; } } //____________________ // Init PRNG uint32 randomSeed = 0; { fstream randomFile(".random"); if (randomFile) randomFile >> randomSeed; } Rand rand(randomSeed); byte buf[1024]; if (byteVal == 256) { // Write random bytes while (bytesArg > 0) { uint32 n = (bytesArg > 1024 ? 1024 : bytesArg); for (uint32 i = 0; i < n; ++i) buf[i] = rand.get(8); cout.write((char*)(buf), n); bytesArg -= n; } } else { // Write bytes with value of byteVal memset(buf, byteVal, 1024); while (bytesArg > 0) { uint32 n = (bytesArg > 1024 ? 1024 : bytesArg); cout.write((char*)(buf), n); bytesArg -= n; } } fstream randomFile(".random", fstream::out | fstream::trunc); ++randomSeed; randomFile << randomSeed; } jigdo-0.7.3/src/util/rsyncsum-test.cc0000644000175000017500000001526010151062264017343 0ustar richardrichard/* $Id: rsyncsum-test.cc,v 1.3 2004/11/24 10:38:44 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. A 32 or 64 bit rolling checksum Command line argument: Name of file to use for test. Will not output anything if test is OK. #test-deps util/rsyncsum.o */ #include #include #include #include #include #include #include //______________________________________________________________________ #ifdef CREATE_CONSTANTS int main(int, char* argv[]) { uint32 data[256]; FILE* f = fopen(argv[1], "r"); fread(data, sizeof(uint32), 256, f); for (int row = 0; row < 64; ++row) { uint32* r = data + 4*row; printf(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\n", r[0], r[1], r[2], r[3]); } } //====================================================================== #else namespace { int errs; char estr[17] = " "; } inline uint32 get_checksum1(const byte *buf1,int len) { RsyncSum s(buf1, len); return s.get(); } inline void error(int i, bool assertion) { if (assertion) { if (estr[i] == ' ') estr[i] = '.'; } else { ++errs; estr[i] = '*'; } } void printBlockSums(size_t blockSize, const char* fileName) { bifstream file(fileName, ios::binary); byte buf[blockSize]; byte* bufEnd = buf + blockSize; while (file) { // read another block byte* cur = buf; while (cur < bufEnd && file) { readBytes(file, cur, bufEnd - cur); // Fill buffer cur += file.gcount(); } RsyncSum64 sum(buf, cur - buf); cout << ' ' << sum << " (lo=0x" << hex << sum.getLo() << ", hi=0x" << hex << sum.getHi() << ')' << endl; } exit(0); } int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); if (argc == 3) { // 2 cmdline args, blocksize and filename. Print RsyncSums of all blocks printBlockSums(atoi(argv[1]), argv[2]); } // else if (argc != 2) { // cerr << "Try " << argv[0] << " [blocksize] filename" << endl; // exit(1); // } #if 0 // 1 cmdline arg => play around a little with the file data const int CHUNK = 8192; FILE* f = fopen(argv[1], "r"); byte* mem = (byte*)malloc(CHUNK); size_t size = CHUNK; /* of *mem */ size_t read = 0; size_t totalread = 0; /* bytes in file */ while ((read = fread(mem + totalread, 1, CHUNK, f)) != 0 && !feof(f)) { totalread += read; size = totalread + CHUNK; /*printf("%d %d %d\n", read, totalread, size);*/ mem = (byte*)realloc(mem, size); } totalread += read; #else const char* str = ".Ú<<³VÚÌGCÉ&m2¬¡'·Ø>öêU+ÆÊÎôENèwÌkºy¾tü7>¸Ä@vÿÐ;))k¦?M¶9±¨Ì2dIÎ5HNÜý^º6´(i|Ìe&5_ëâ÷­iHë§àKW-\tÊ÷.²ÒäÄeø5(\tdqB{fQ¿¼h¢%ÌÃDF³ûÑD`(`ã^ÌèFñ£|§j~k¼Þë}-x@P.Ïòm3Äïexupç»è8¾w$óO{\tY)u ²L£H¼£îÍV½$¶õªÙdn^*³¤ü,ųÙ':y½ïÕ®k».ú~2ÙªPg,ʧХhQk8dè9¤`CÕÐà¶|¢nê5èÅñ°­nUÆQâÔ¹h¡yo}ãs[qhÒNJJç\"ÂF7Ãu$ßô@ûòByYT£G\\11f]}kuMH)ë»ï¸Ü7{ðvÓé[¾scX£E³Ágr_ÑɨÍóÚGÊT·µ§íND0LÐÔnàôoÅ/Ï ü&\"/Iá@&äy¦Yé¹ïu/¤l¯lAÄàKf¥¨*6g\tÏ©_Z+û^ý\\ãnàÃePFôMp¤<#ØÂYÁu=[ú;CðÍ£@(-*!@Çì½á1±¿H±k¿wöbÜ>$íÌa1ÚF½³¾¨°Z\tV`ŨKbI!¦Í}¦1}ÛzôÌãVvÁwÀ\tÜÑ2VèF~¬ÃFÈÖ-Ç©w%«)¸ iÁ³dIÒü½unºf ·(%¾ý_d\t$OÃÚtÂ/Á'§ªJ¬Mgý\"mv~\tégµR@¡LhÃ'ÄäÕÒ(ÛðpUÇ}úì,_^Çf\\{hç·K ?¢=ÕzlKáìîÆzT&üg)eí!íøxëóÓ)é78MA«wÇ&Õäº\"çá¶yýph¦z4À´FûíF_1 ï3·ÒÏ·?ÛQ2&ËbIC:LØst®v®\t(öiX£ëjxõr¼Rq».¸cü`² ¿J)úL¨ýjÀ@9Èhñ¡¡­wï:;n34çÚ1YQѾÞ4\t;¥·P=æC5Íâ$,Ø!4!/Zd<'åÚ@£¡ñÿK¬sÖÙ/¾$\t CNJÔ ®#æRU®´TË'å¢v1Ô¦lÑÚöÃ>t©à*ãÓÁ³¹\t­£Ï&ßp=ÿÇaÂúÚÅmîæ«<Ï÷N ä-S_ØZ6æáFæA,uÓkì0`W:Þß±¤]Gä<ð÷b`Zd·/Ôàq¤D@n¿[?yr§¨ÐíjUæPé´ïí>pAG¶Á^ÀÕÒhåÛÍ/¡¤sXÕ/ø9ïb>Fíßá9¿`«o®}BQ ¾â ̼P_ÁÑ覶%R3çl~L®ã¡*CñqÚ cÃü:aÒã&Ðk-Áú{!>ªÖ®?ëÚñX©ÔÇzʾ]³a<ÔP¾y¸7Ç>w¼°Â+>KIuX¥;,@\\OuºUøs£3óòIâäß+ ´=t_È\\¤þàe1<ï?ÆWH¦P¥ÛðPªµÝQR|H,[ð¯.^}öP¨Ux¨ÊAWÀMá7è7èê¡¡2|!K½\"Ä#¾\"ýf·Xøp9¹BÿLTDP_j,ò¸Õ®þõtt-Í­è u¾þ¿­©²8s-ðÝÝ |yçÄÉÚj|à©þý0t¿ÌµÔÀOÿm?ý¨BLÒdä/ëèÔýf>§Ù«Ö1"; const byte* mem = reinterpret_cast(str); size_t totalread = strlen(str); #endif //________________________________________ errs = 0; { RsyncSum rs; rs.addBack(mem, totalread); error(0, get_checksum1(mem, totalread) == rs.get()); if (totalread > 256) { RsyncSum roll(mem + 32, 64); RsyncSum noRoll(roll); rs.reset().addBack(mem, 128); for (int i = 0; i < 32; ++i) { RsyncSum x = rs; // add stuff to end x.addBack(mem + 128, i); error(1, get_checksum1(mem, 128 + i) == x.get()); // roll by removing one byte at front, adding one at end roll.removeFront(mem[32+i], 64).addBack(mem[32+64+i]); } error(2, get_checksum1(mem+64, 64) == roll.get()); // roll by 32 bytes in one go noRoll.removeFront(mem+32, 32, 64).addBack(mem+32+64, 32); error(3, roll == noRoll); } } //____________________ { RsyncSum64 rs, y; if (totalread > 256) { RsyncSum64 roll(mem + 32, 64); RsyncSum64 noRoll(roll); rs.reset().addBack(mem, 128); for (int i = 0; i < 32; ++i) { RsyncSum64 x = rs; // add stuff to end x.addBack(mem + 128, i); error(4, y.reset().addBack(mem, 128 + i) == x); // roll by removing one byte at front, adding one at end roll.removeFront(mem[32+i], 64).addBack(mem[32+64+i]); } error(2, y.reset().addBack(mem+64, 64) == roll); // roll by 32 bytes in one go noRoll.removeFront(mem+32, 32, 64).addBack(mem+32+64, 32); error(3, roll == noRoll); } } if (errs != 0) { printf("%s %s\n", estr, argv[1]); return 1; } //________________________________________ return 0; } #endif jigdo-0.7.3/src/util/rsyncsum.cc0000644000175000017500000001615207735400630016376 0ustar richardrichard/* $Id: rsyncsum.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. A 32 or 64 bit rolling checksum */ #include #include #include //______________________________________________________________________ // NB: The following assumes that uint32 is at least 32 bits, but may be 64 /* Note: This will not yield the same results as the original rsync algorithm because 1) it uses a non-zero CHAR_OFFSET, and 2) it treats single bytes as unsigned, not signed. */ RsyncSum& RsyncSum::addBack(const byte* mem, size_t len) { uint32 a = sum; uint32 b = sum >> 16; const byte* blockLimit = mem + (len / 16) * 16; const byte* limit = mem + len; // 1st byte not to process while (mem < blockLimit) { a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; a += *mem++ + CHAR_OFFSET; b += a; } while (mem < limit) { a += *mem++ + CHAR_OFFSET; b += a; } sum = ((a & 0xffff) + (b << 16)) & 0xffffffff; return *this; } //________________________________________ RsyncSum& RsyncSum::removeFront(const byte* mem, size_t len, size_t areaSize) { RsyncSum front(mem, len); uint32 a = sum; uint32 b = sum >> 16; /* The following is much cheaper than subtracting (areaSize-i)*(mem[i]+CHAR_OFFSET) from b for each i, 0<=i> 16) + (areaSize - len) * front.get(); sum = ((a & 0xffff) + (b << 16)) & 0xffffffff; return *this; } //______________________________________________________________________ RsyncSum64& RsyncSum64::addBack2(const byte* mem, size_t len) { uint32 a = sumLo; uint32 b = sumHi; const byte* blockLimit = mem + (len / 16) * 16; const byte* limit = mem + len; // 1st byte not to process while (mem < blockLimit) { a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; a += charTable[*mem++]; b += a; } while (mem < limit) { a += charTable[*mem++]; b += a; } sumLo = a & 0xffffffff; sumHi = b & 0xffffffff; return *this; } //________________________________________ RsyncSum64& RsyncSum64::removeFront(const byte* mem, size_t len, size_t areaSize) { RsyncSum64 front(mem, len); sumLo = (sumLo - front.getLo()) & 0xffffffff; sumHi = (sumHi - front.getHi() - (areaSize - len) * front.getLo()) &0xffffffff; return *this; } //________________________________________ /* These are purely random, no patterns or anything... (I hope) I do not claim copyright for the actual numbers below, you may use them for a re-implementation of the algorithm under a license of your choice. -- Richard Atterer. */ const uint32 RsyncSum64::charTable[256] = { 0x51d65c0f, 0x083cd94b, 0x77f73dd8, 0xa0187d36, 0x29803d07, 0x7ea8ac0e, 0xea4c16c9, 0xfc576443, 0x6213df29, 0x1c012392, 0xb38946ae, 0x2e20ca31, 0xe4dc532f, 0xcb281c47, 0x8508b6a5, 0xb93c210d, 0xef02b5f3, 0x66548c74, 0x9ae2deab, 0x3b59f472, 0x4e546447, 0x45232d1f, 0x0ac0a4b1, 0x6c4c264b, 0x5d24ce84, 0x0f2752cc, 0xa35c7ac7, 0x3e31af51, 0x79675a59, 0x581f0e81, 0x49053122, 0x7339c9d8, 0xf9833565, 0xa3dbe5b3, 0xcc06eeb9, 0x92d0671c, 0x3eb220a7, 0x64864eae, 0xca100872, 0xc50977a1, 0xd90378e1, 0x7a36cab9, 0x15c15f4b, 0x8b9ef749, 0xcc1432dc, 0x1ec578ed, 0x27e6e092, 0xbb06db8f, 0x67f661ac, 0x8dd1a3db, 0x2a0ca16b, 0xb229ab84, 0x127a3337, 0x347d846f, 0xe1ea4b50, 0x008dbb91, 0x414c1426, 0xd2be76f0, 0x08789a39, 0xb4d93e30, 0x61667760, 0x8871bee9, 0xab7da12d, 0xe3c58620, 0xe9fdfbbe, 0x64fb04f7, 0x8cc5bbf0, 0xf5272d30, 0x8f161b50, 0x11122b05, 0x7695e72e, 0xa1c5d169, 0x1bfd0e20, 0xef7e6169, 0xf652d08e, 0xa9d0f139, 0x2f70aa04, 0xae2c7d6d, 0xa3cb9241, 0x3ae7d364, 0x348788f8, 0xf483b8f1, 0x55a011da, 0x189719dc, 0xb0c5d723, 0x8b344e33, 0x300d46eb, 0xd44fe34f, 0x1a2016c1, 0x66ce4cd7, 0xa45ea5e3, 0x55cb708a, 0xbce430df, 0xb01ae6e0, 0x3551163b, 0x2c5b157a, 0x574c4209, 0x430fd0e4, 0x3387e4a5, 0xee1d7451, 0xa9635623, 0x873ab89b, 0xb96bc6aa, 0x59898937, 0xe646c6e7, 0xb79f8792, 0x3f3235d8, 0xef1b5acf, 0xd975b22b, 0x427acce6, 0xe47a2411, 0x75f8c1e8, 0xa63f799d, 0x53886ad8, 0x9b2d6d32, 0xea822016, 0xcdee2254, 0xd98bcd98, 0x2933a544, 0x961f379f, 0x49219792, 0xc61c360f, 0x77cc0c64, 0x7b872046, 0xb91c7c12, 0x7577154b, 0x196573be, 0xf788813f, 0x41e2e56a, 0xec3cd244, 0x8c7401f1, 0xc2e805fe, 0xe8872fbe, 0x9e2faf7d, 0x6766456b, 0x888e2197, 0x28535c6d, 0x2ce45f3f, 0x24261d2a, 0xd6faab8b, 0x7a7b42b8, 0x15f0f6fa, 0xfe1711df, 0x7e5685a6, 0x00930268, 0x74755331, 0x1998912c, 0x7b60498b, 0x501a5786, 0x92ace0f6, 0x1d9752fe, 0x5a731add, 0x5b3b44fc, 0x473673f9, 0xa42c0321, 0xd82f9f18, 0xb4b225da, 0xfc89ece2, 0x072e1130, 0x5772aae3, 0x29010857, 0x542c970c, 0x94f67fe5, 0x71209e9b, 0xdb97ea39, 0x2689b41b, 0xae815804, 0xfc5e2651, 0xd4521674, 0x48ed979a, 0x2f617da3, 0xc350353d, 0xc3accd94, 0xbd8d313a, 0xc61a8e77, 0xf34940a4, 0x8d2c6b0f, 0x0f0e7225, 0x39e183db, 0xd19ebba9, 0x6a0f37b9, 0xd18922f3, 0x106420c5, 0xaa5a640b, 0x7cf0d273, 0xcf3238a7, 0x3b33204f, 0x476be7bb, 0x09d23bca, 0xbe84b2f7, 0xb7a3bace, 0x2528cee1, 0x3dcaa1dd, 0x900ad31a, 0xf21dea6d, 0x9ce51463, 0xf1540bba, 0x0fab1bdd, 0x89cfb79a, 0x01a2a6e6, 0x6f85d67c, 0xd1669ec4, 0x355db722, 0x00ebd5c4, 0x926eb385, 0x69ead869, 0x0da2b122, 0x402779fe, 0xdaed92d0, 0x57e9aabb, 0x3df64854, 0xfcc774b5, 0x2e1740ed, 0xa615e024, 0xf7bac938, 0x377dfd1a, 0xd0559d66, 0x25499be8, 0x2d8f2006, 0xfaa9e486, 0x95e980e7, 0x82aeba67, 0x5a7f2561, 0xbc60dff6, 0x6c8739a2, 0x7ec59a8b, 0x9998f265, 0xdfe37e5e, 0xb47cee1e, 0x4dd8bc9e, 0x35c57e09, 0x07850b63, 0x06eadbcb, 0x6c1f2956, 0x01685c2c, 0xf5725eef, 0xf13b98b5, 0xaab739c2, 0x200b1da2, 0xa716b98b, 0xd9ee3058, 0x76acf20b, 0x2f259e04, 0xed11658b, 0x1532b331, 0x0ab43204, 0xf0beb023, 0xb1685483, 0x58cbdc4f, 0x079384d3, 0x049b141c, 0xc38184b9, 0xaf551d9a, 0x66222560, 0x059deeca, 0x535f99e2 }; jigdo-0.7.3/src/util/rsyncsum.hh0000644000175000017500000001633110226060300016370 0ustar richardrichard/* $Id: rsyncsum.hh,v 1.4 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file A 32 or 64 bit rolling checksum */ #ifndef RSYNCSUM_HH #define RSYNCSUM_HH #ifndef INLINE # ifdef NOINLINE # define INLINE # else # define INLINE inline # endif #endif #include #include #include //______________________________________________________________________ /** A 32 bit checksum with the special property that you do not need to recalculate the checksum when data is added to the front/end of the checksummed area, or removed from it. Currently only adding to the end and removing from the front is supported. Adding to the end is very slightly faster. Many thanks to Andrew Tridgell and Paul Mackerras for the algorithm - NB none of rsync's code is used. Unless described otherwise, if a method returns an RsyncSum&, then this is a reference to the object itself, to allow chaining of calls, e.g. obj.addBack(x).addBack(y) */ class RsyncSum { public: /** Initialises the checksum with zero */ RsyncSum() : sum(0) { }; /** Initialises with the checksum of a memory area */ RsyncSum(const byte* mem, size_t len) : sum(0) { addBack(mem, len); }; /** Compare two RsyncSum objects */ bool operator==(const RsyncSum& x) const { return get() == x.get(); } bool operator!=(const RsyncSum& x) const { return get() != x.get(); } bool operator< (const RsyncSum& x) const { return get() < x.get(); } bool operator> (const RsyncSum& x) const { return get() > x.get(); } bool operator<=(const RsyncSum& x) const { return get() <= x.get(); } bool operator>=(const RsyncSum& x) const { return get() >= x.get(); } /** Append memory area to end of area covered by the checksum */ RsyncSum& addBack(const byte* mem, size_t len); /** Append one byte at end of area covered by the checksum */ inline RsyncSum& addBack(byte x); /** Append the same byte n times at end of area covered by the checksum. (addBack() is not overloaded for this in order to avoid confusion with removeFront(byte, size_t), which has the same signature, but only removes one byte.) */ inline RsyncSum& addBackNtimes(byte x, size_t n); /** Remove memory area from start of area covered by the checksum @param mem Data to remove @param len Number of bytes to remove from area, so it will cover area-len bytes after call @param areaSize Size covered by area before call (necessary for calculcations) */ RsyncSum& removeFront(const byte* mem, size_t len, size_t areaSize); /** Remove one byte from start of area covered by checksum */ inline RsyncSum& removeFront(byte x, size_t areaSize); /** Read stored checksum */ uint32 get() const { return sum; } /** Reset to initial state */ RsyncSum& reset() { sum = 0; return *this; } /** Check whether sum is zero */ bool empty() const { return sum == 0; } // Using default dtor & copy ctor private: // Nothing magic about this constant; just a random number which, // according to the rsync sources, improves the quality of the // checksum. static const uint32 CHAR_OFFSET = 0xb593; uint32 sum; }; //________________________________________ /** Like RsyncSum, but the checksum is a 64 bit value. However, the checksum quality would only improve very little if exactly the same algorithm were used, so this uses an additional 256-word lookup table for more "randomness". */ class RsyncSum64 { public: RsyncSum64() : sumLo(0), sumHi(0) { }; inline RsyncSum64(const byte* mem, size_t len); inline bool operator==(const RsyncSum64& x) const; inline bool operator!=(const RsyncSum64& x) const; inline bool operator< (const RsyncSum64& x) const; inline bool operator> (const RsyncSum64& x) const; inline bool operator<=(const RsyncSum64& x) const; inline bool operator>=(const RsyncSum64& x) const; INLINE RsyncSum64& addBack(const byte* mem, size_t len); INLINE RsyncSum64& addBack(byte x); INLINE RsyncSum64& addBackNtimes(byte x, size_t n); RsyncSum64& removeFront(const byte* mem, size_t len, size_t areaSize); inline RsyncSum64& removeFront(byte x, size_t areaSize); /** Return lower 32 bits of checksum */ uint32 getLo() const { return sumLo; } /** Return higher 32 bits of checksum */ uint32 getHi() const { return sumHi; } RsyncSum64& reset() { sumLo = sumHi = 0; return *this; } bool empty() const { return sumLo == 0 && sumHi == 0; } template inline Iterator serialize(Iterator i) const; template inline ConstIterator unserialize(ConstIterator i); inline size_t serialSizeOf() const; private: RsyncSum64& addBack2(const byte* mem, size_t len); static const uint32 charTable[256]; uint32 sumLo, sumHi; }; INLINE ostream& operator<<(ostream& s, const RsyncSum& r); INLINE ostream& operator<<(ostream& s, const RsyncSum64& r); //______________________________________________________________________ RsyncSum& RsyncSum::addBack(byte x) { uint32 a = sum; uint32 b = sum >> 16; a += x + CHAR_OFFSET; b += a; sum = ((a & 0xffff) + (b << 16)) & 0xffffffff; return *this; } RsyncSum& RsyncSum::addBackNtimes(byte x, size_t n) { uint32 a = sum; uint32 b = sum >> 16; b += n * a + (n * (n + 1) / 2) * (x + CHAR_OFFSET); // Gauß a += n * (x + CHAR_OFFSET); sum = ((a & 0xffff) + (b << 16)) & 0xffffffff; return *this; } RsyncSum& RsyncSum::removeFront(byte x, size_t areaSize) { uint32 a = sum; uint32 b = sum >> 16; b -= areaSize * (x + CHAR_OFFSET); a -= x + CHAR_OFFSET; sum = ((a & 0xffff) + (b << 16)) & 0xffffffff; return *this; } //________________________________________ RsyncSum64::RsyncSum64(const byte* mem, size_t len) : sumLo(0), sumHi(0) { addBack(mem, len); }; bool RsyncSum64::operator==(const RsyncSum64& x) const { return sumHi == x.sumHi && sumLo == x.sumLo; } bool RsyncSum64::operator!=(const RsyncSum64& x) const { return sumHi != x.sumHi || sumLo != x.sumLo; } bool RsyncSum64::operator< (const RsyncSum64& x) const { return (sumHi < x.sumHi) || (sumHi == x.sumHi && sumLo < x.sumLo); } bool RsyncSum64::operator> (const RsyncSum64& x) const { return (sumHi > x.sumHi) || (sumHi == x.sumHi && sumLo > x.sumLo); } bool RsyncSum64::operator<=(const RsyncSum64& x) const { return !(*this > x); } bool RsyncSum64::operator>=(const RsyncSum64& x) const { return !(*this < x); } RsyncSum64& RsyncSum64::removeFront(byte x, size_t areaSize) { sumHi = (sumHi - areaSize * charTable[x]) & 0xffffffff; sumLo = (sumLo - charTable[x]) & 0xffffffff; return *this; } template inline Iterator RsyncSum64::serialize(Iterator i) const { i = serialize4(sumLo, i); i = serialize4(sumHi, i); return i; } template inline ConstIterator RsyncSum64::unserialize(ConstIterator i) { i = unserialize4(sumLo, i); i = unserialize4(sumHi, i); return i; } inline size_t RsyncSum64::serialSizeOf() const { return 8; } #ifndef NOINLINE # include /* NOINLINE */ #endif #endif jigdo-0.7.3/src/util/rsyncsum.ih0000644000175000017500000000307007735400630016404 0ustar richardrichard/* $Id: rsyncsum.ih,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. A 32 or 64 bit rolling checksum */ #ifndef RSYNCSUM_IH #define RSYNCSUM_IH #include //______________________________________________________________________ ostream& operator<<(ostream& s, const RsyncSum& r) { Base64String m; m << r.get() << flush; s << m.result(); return s; } ostream& operator<<(ostream& s, const RsyncSum64& r) { Base64String m; m << r.getLo() << r.getHi() << flush; s << m.result(); return s; } //________________________________________ RsyncSum64& RsyncSum64::addBack(const byte* mem, size_t len) { if (len == 0) return *this; else return addBack2(mem, len); } RsyncSum64& RsyncSum64::addBack(byte x) { sumLo = (sumLo + charTable[x]) & 0xffffffff; sumHi = (sumHi + sumLo) & 0xffffffff; return *this; } RsyncSum64& RsyncSum64::addBackNtimes(byte x, size_t n) { // gaussProd = n*(n+1)/2, but need to do the "/2" before the "*", // otherwise bit 31 of result is lost on 32-bit architectures. uint32 gaussProd; if (n % 2 == 0) gaussProd = (n / 2) * (n + 1); else gaussProd = ((n + 1) / 2) * n; sumHi = (sumHi + n * sumLo + gaussProd * charTable[x]) & 0xffffffff; sumLo = (sumLo + n * charTable[x]) & 0xffffffff; return *this; } #endif jigdo-0.7.3/src/util/smartptr.hh0000644000175000017500000001627710226060300016372 0ustar richardrichard/* $Id: smartptr.hh,v 1.7 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2000-2004 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2 or later. See the file COPYING for details. *//** @file Smart pointer, i.e.\ pointer-like object that maintains a count for how many times an object is referenced, and only deletes the object when the last smart pointer to it is destroyed. */ #ifndef SMARTPTR_HH #define SMARTPTR_HH #ifdef DEBUG # include # include #endif //______________________________________________________________________ struct SmartPtr_lockStatic; /** The version of SmartPtrBase below needs the following:
      class Base    : public SmartPtrBase { ... };
      class Derived : public Base { ... };
in order to allow a Derived object's address to be assigned to a SmartPtr. Its only fault is that its member must be public (or we lose GCC 2.95 compatibility; friend templates only work as of 3.4). The reference count is set to 0 by the ctor. */ struct SmartPtrBase { //friend template class SmartPtr; friend struct SmartPtr_lockStatic; SmartPtrBase() /*throw()*/ : smartPtr_refCount(0) { } int smartPtr_refCount; }; /* The version of SmartPtrBase below needs the following: class Base : virtual public SmartPtrBase { ... }; class Derived : virtual public SmartPtrBase, public Base { ... }; in order to allow a Derived object's address to be assigned to a SmartPtr. */ // template class SmartPtr; // // template // class SmartPtrBase { // friend class SmartPtr; // public: // SmartPtrBase() /*throw()*/ : smartPtr_refCount(0) { } // private: // int smartPtr_refCount; // }; //______________________________________________________________________ /** If static objects are accessed through smart pointers, ensure that there are no attempts to delete them, by defining a non-static SmartPtr_lockStatic(object), which MUST be DEFINED (not declared) AFTER the object being locked, in the SAME translation unit. Otherwise, order of construction is not defined. */ struct SmartPtr_lockStatic { SmartPtr_lockStatic(SmartPtrBase& obj) { ++obj.smartPtr_refCount; } ~SmartPtr_lockStatic() { } }; //______________________________________________________________________ /** Smart pointer, i.e. pointer-like object that maintains a count for how many times an object is referenced, and only deletes the object when the last smart pointer to it is destroyed. To make it possible to create smart pointers to objects of a class, the class must inherit from SmartPtrBase. NB: SmartPtrBase must only be inherited from *once*, so derive virtually if it appears multiple times:
    class MyClass : public virtual SmartPtrBase {
      ...
    };
There are no implicit conversions from/to the actual pointer. */ template class SmartPtr { public: typedef X element_type; SmartPtr() : ptr(0) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr()" << endl; # endif } ~SmartPtr() { # ifdef DEBUG_SMARTPTR cerr << this << " ~SmartPtr() " << ptr << "/" << (ptr == 0 ? 0 : ptr->smartPtr_refCount) << endl; # endif decRef(); } // init from SmartPtr SmartPtr(const SmartPtr& x) : ptr(x.get()) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr(SP " << x.get() << ")" << endl; # endif incRef(); } // init from SmartPtr to other type; only works if implicit conv. possible template SmartPtr(const SmartPtr& y) : ptr(y.get()) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr(SP " << y.get() << ")" << endl; # endif incRef(); } // init from pointer explicit SmartPtr(X* x) : ptr(x) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr(" << x << ")" << endl; # endif incRef(); } /* This one is necessary, the compiler will *not* generate one from the template below. */ SmartPtr& operator=(const SmartPtr& x) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr " << ptr << " = SP " << x.get() << endl; # endif if (ptr != x.get()) { decRef(); ptr = x.get(); incRef(); } return *this; } template SmartPtr& operator=(const SmartPtr& y) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr " << ptr << " = SP " << y.get() << endl; # endif if (ptr != y.get()) { decRef(); ptr = y.get(); incRef(); } return *this; } template SmartPtr& operator=(Y* y) { # ifdef DEBUG_SMARTPTR cerr << this << " SmartPtr " << ptr << " = " << y << endl; # endif if (ptr != y) { decRef(); ptr = y; incRef(); } return *this; } X& operator*() const { return *ptr; } X* operator->() const { return ptr; } X* get() const { return ptr; } X* release() { // relinquish ownership, but never delete # ifdef DEBUG if (ptr != 0 && ptr->SmartPtrBase::smartPtr_refCount == 0) abort(); # endif if (ptr != 0) --(ptr->SmartPtrBase::smartPtr_refCount); X* tmp = ptr; ptr = 0; return tmp; } void swap(SmartPtr& x) { X* tmp = ptr; ptr = x.ptr; x.ptr = tmp; } void clear() { decRef(); ptr = 0; } bool isNull() const { return ptr == 0; } private: void incRef() { if (ptr != 0) ++(ptr->SmartPtrBase::smartPtr_refCount); } void decRef() { # ifdef DEBUG if (ptr != 0 && ptr->SmartPtrBase::smartPtr_refCount == 0) abort(); # endif if (ptr != 0 && --(ptr->SmartPtrBase::smartPtr_refCount) <= 0) delete ptr; } X* ptr; }; //____________________ template inline SmartPtr makeSmartPtr(X* x) { return SmartPtr(x); } // only delete if count is zero // 'deleteSmart(x);' is equivalent to '{ SmartPtr tmp(x); }' template // need template for 'delete ptr' to call the right dtor inline bool deleteSmart(X* ptr) { if (ptr != 0 && ptr->SmartPtrBase::smartPtr_refCount <= 0) { delete ptr; return true; } else { return false; } } template inline X* releaseSmart(X* ptr) { if (ptr != 0) --ptr->SmartPtrBase::smartPtr_refCount; return ptr; } //____________________ template inline void swap(SmartPtr& a, SmartPtr& b) { a.swap(b); } template inline bool operator<(const SmartPtr a, const SmartPtr b) { return a.get() < b.get(); } template inline bool operator>(const SmartPtr a, const SmartPtr b) { return a.get() > b.get(); } template inline bool operator<=(const SmartPtr a, const SmartPtr b) { return a.get() <= b.get(); } template inline bool operator>=(const SmartPtr a, const SmartPtr b) { return a.get() >= b.get(); }; // allow comparison with pointers template inline bool operator==(const SmartPtr a, const void* b) { return a.get() == b; } template inline bool operator==(const void* a, const SmartPtr b) { return a == b.get(); } template inline bool operator!=(const SmartPtr a, const void* b) { return a.get() != b; } template inline bool operator!=(const void* a, const SmartPtr b) { return a != b.get(); } #endif jigdo-0.7.3/src/util/status.hh0000644000175000017500000000467510226060300016040 0ustar richardrichard/* $Id: status.hh,v 1.4 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Return type for functions to show success or failure */ #ifndef STATUS_HH #define STATUS_HH /** Simple success/failure return type */ class Status { public: static const bool OK = false; static const bool FAILED = true; explicit Status(bool c) : code(c) { } Status(const Status& x) : code(x.code) { } Status& operator=(const Status& x) { code = x.code; return *this; } bool ok() const { return code == OK; } bool failed() const { return code == FAILED; } /* Default dtor */ /* Intentionally no operator bool() - should write ok() or failed() explicitly in if() conditions! */ bool code; private: // Prevent implicit conversions to bool explicit Status(int); explicit Status(unsigned); explicit Status(void*); }; static const Status OK = Status(Status::OK); static const Status FAILED = Status(Status::FAILED); inline bool operator==(const Status& a, const Status& b) { return a.code == b.code; } //______________________________________________________________________ /** Version of Status which can contain more than 2 values. Explicitly making this a separate class so users (hopefully) notice that there are other states beyond succeeded/failed. */ class XStatus { public: // static const int OK = 0; // static const int FAILED = -1; XStatus(const Status& x) : code(x.ok() ? 0 : -1) { } explicit XStatus(int c) : code(c) { } XStatus(const XStatus& x) : code(x.code) { } XStatus& operator=(const XStatus& x) { code = x.code; return *this; } XStatus& operator=(const Status& x) { code = (x.ok() ? 0 : -1); return *this; } /** Not calling these ok()/failed() to remind you that this is a XStatus */ bool xok() const { return code == 0; } bool xfailed() const { return code == -1; } //bool other() const { return code != 0 && code != -1; } bool returned(int x) const { return code == x; } /* Default dtor */ /* Intentionally no operator bool() - should write xok/xfailed/returned() explicitly in if() conditions! */ int code; }; inline bool operator==(const XStatus& a, const XStatus& b) { return a.code == b.code; } #endif jigdo-0.7.3/src/util/string-utf-test.cc0000644000175000017500000000503407731716572017602 0ustar richardrichard/* $Id: string-utf-test.cc,v 1.1 2003/09/16 23:32:10 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Helper functions for dealing with UTF-8 strings #test-deps */ #include #include #include #include #include #include #include //______________________________________________________________________ namespace { int returnCode = 0; void test(const char* correct, const string& generated) { if (generated == correct) { msg("OK: \"%1\"", generated); return; } msg("FAILED:"); msg(" expected \"%1\"", correct); msg(" but got \"%1\"", generated); returnCode = 1; } } //____________________ int main(int argc, char* argv[]) { if (argc == 2) Logger::scanOptions(argv[1], argv[0]); /* const char* charset; g_get_charset(&charset); cout << "glib thinks that your locale uses the following character " "encoding: " << charset << "\nI'm setting CHARSET=ISO-8859-15 just for " "this test\n"; */ // For this test, make glib assume 8-bit locale //putenv("CHARSET=ISO-8859-15"); string s("string"); test("1 >42 foo 43% 666 1024 X © string string ", subst("1 >%1 foo %2%% %3 %4 %5 %6 %7 %8 %9", 42, 43U, 666L, 1024UL, 'X', "©", s, &s)); // Arg not valid UTF-8, so it is cut off at first invalid char test("2 &Wah <>&W Woo!", subst("2 &Wah %1 Woo!", "<>&Wäääh")); // As above, but escape <>& test("3 &Wah <>&W Woo!", subst("3 &Wah %E1 Woo!", "<>&Wäääh")); // Let's try this again with a valid UTF-8 string test("4 &Wah Wä<©>&h Woo!", subst("4 &Wah %E1 Woo!", "Wä<©>&h")); // When using F, the thing is not checked, producing invalid UTF-8 test("5 Wah Wäääh & test("7 Wah Wä<ä>äh& < Woo!", subst("7 Wah %FELL1 < Woo!", "Wä<ä>äh&")); // escape, but assume that arg is UTF-8 test("8 Wah Wo<o>oh& < Woo!", subst("8 Wah %FE1 < Woo!", "Wooh&")); return returnCode; } jigdo-0.7.3/src/util/string-utf.cc0000644000175000017500000001710310120704646016606 0ustar richardrichard/* $Id: string-utf.cc,v 1.6 2004/09/11 23:26:30 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Helper functions for dealing with UTF-8 strings */ #include #include #include #include #include #include //______________________________________________________________________ namespace { const int BUF_LEN = 40; // Enough room for 128-bit integers. :-) char buf[BUF_LEN]; const char* const PAD = " "; const char* const PAD_END = PAD + 40; } string& append(string& s, double x) { snprintf(buf, BUF_LEN, "%.1f", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, int x) { snprintf(buf, BUF_LEN, "%d", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned x) { snprintf(buf, BUF_LEN, "%u", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%u", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, long x) { snprintf(buf, BUF_LEN, "%ld", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long x) { snprintf(buf, BUF_LEN, "%lu", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%lu", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } #if HAVE_UNSIGNED_LONG_LONG string& append(string& s, unsigned long long x) { snprintf(buf, BUF_LEN, "%llu", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long long x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%llu", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } #endif //______________________________________________________________________ namespace { // flag bits static const int F = 1 << 0; // fast substitution static const int L = 1 << 1; // locale-format string arg, not UTF-8 static const int E = 1 << 2; // escape: turn < into < etc //____________________ // Convert input to UTF-8. Returned string must be freed. inline gchar* localeToUTF8(const char* input) { gsize written; GError* error = NULL; int len = strlen(input); gchar* s = g_locale_to_utf8(input, len, NULL, &written, &error); if (error == NULL) return s; if (s != NULL) g_free(s); /* Maybe this is just me, but for me, glib always thinks that my charset is "ANSI_X3.4-1968" - ?! Fall back to ISO-8859-15 if glib failed above. Users can override this by setting the CHARSET variable. */ g_clear_error(&error); s = g_convert(input, len, "UTF-8", "ISO-8859-1", NULL, &written, &error); if (error == NULL || s != NULL) return s; g_clear_error(&error); return g_strdup("[UTF8ConvFail]"); } //____________________ /* Append s to result according to flags */ void strSubst(string& result, const char* s, int flags) { const char* e; switch (flags) { case F: Paranoid(false); // Already handled in doSubst() below break; case L: case F|L: // Convert locale-format to UTF s = localeToUTF8(s); result += s; g_free((gpointer)s); break; case L|E: case F|L|E: // Convert locale-format to UTF and turn < into < s = localeToUTF8(s); e = s; while (*s != '\0') { if (*s == '&') result += "&"; else if (*s == '<') result += "<"; else if (*s == '>') result += ">"; else result += *s; ++s; } g_free((gpointer)e); break; case F|E: while (*s != '\0') { if (*s == '&') result += "&"; else if (*s == '<') result += "<"; else if (*s == '>') result += ">"; else result += *s; ++s; } break; case 0: // Verify UTF-8, only append up to first invalid character g_utf8_validate(s, -1, &e); result.append(s, e - s); break; case E: // Verify UTF-8 data, turn < into < g_utf8_validate(s, -1, &e); while (s < e) { if (*s == '&') result += "&"; else if (*s == '<') result += "<"; else if (*s == '>') result += ">"; else result += *s; ++s; } break; default: Paranoid(false); } } } // namespace //____________________ #ifndef DOXYGEN_SKIP /* Look at arg[n] and append it to result according to flags */ inline void Subst::doSubst(string& result, const Subst arg[], int n, int flags) { switch (arg[n].type) { case INT: snprintf(buf, BUF_LEN, "%d", arg[n].val.intVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case UNSIGNED: snprintf(buf, BUF_LEN, "%u", arg[n].val.unsignedVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case LONG: snprintf(buf, BUF_LEN, "%ld", arg[n].val.longVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case ULONG: snprintf(buf, BUF_LEN, "%lu", arg[n].val.ulongVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; # if HAVE_UNSIGNED_LONG_LONG case ULONGLONG: snprintf(buf, BUF_LEN, "%llu", arg[n].val.ulonglongVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; # endif case DOUBLE: snprintf(buf, BUF_LEN, "%f", arg[n].val.doubleVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case CHAR: result += arg[n].val.charVal; break; case CHAR_P: if (flags == F) result += arg[n].val.charPtr; else strSubst(result, arg[n].val.charPtr, flags); break; case STRING_P: if (flags == F) result += *arg[n].val.stringPtr; else strSubst(result, arg[n].val.stringPtr->c_str(), flags); break; case POINTER: snprintf(buf, BUF_LEN, "%p", arg[n].val.pointerVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; } } #endif //____________________ string Subst::subst(const char* format, int args, const Subst arg[]) { string result; gunichar x; const char* i = format; unsigned max = '1' + args; while (true) { // Search through string until either '%' or '\0' found const char* j = i; while (true) { x = g_utf8_get_char(j); if (x == 0 || x == '%') break; j = g_utf8_next_char(j); } // x == '%' or x == 0, normal string between [i;j) result.append(i, j - i); if (x == 0) return result; // '%' escape detected int flags = 0; while (true) { j = g_utf8_next_char(j); x = g_utf8_get_char(j); // Handle special flags between % and digit if (x == 0) return result; else if (x == '%') { result += '%'; break; } else if (x == 'F') flags |= F; else if (x == 'L') flags |= L; else if (x == 'E') flags |= E; // Ignore other characters, loop until digit found else if (x >= '1' && x <= '9') { if (x < max) doSubst(result, arg, x - '1', flags); // Arg subst break; } } // Now j points to digit in "%1" or second '%' in "%%" i = ++j; // i and j point after digit/% } } jigdo-0.7.3/src/util/string-utf.hh0000644000175000017500000000357010226060300016610 0ustar richardrichard/* $Id: string-utf.hh,v 1.2 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2003 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file subst("Format %1, %2", arg1, arg2) creates strings with the arguments filled in, and does so in a safer way than sprintf() and friends. This is the same functionality as in string.hh, except that UTF-8 strings are handled by subst(): - Output string is always valid UTF-8 - Format string is assumed to be in valid UTF-8 - "%F1" arg is assumed to be in valid UTF-8 (is copied over unvalidated) - "%1" arg is untrusted UTF-8, will be validated while substituting it - "%L1" arg is assumed to be in the OS locale, is converted into UTF-8 Additionally: - "%E1" arg: The characters <>& are escaped with their entities < > & when substituting the value. Useful if the string will be rendered by Pango. The escaping is limited to this single substitution, the format string or other substituted values can still contain tags. NB: Newlines, tabs, delete (0x7f) etc. are not removed. Modifiers can be combined, except that F is ignored when you use L. Modifiers only have an effect for string substitution, not for int/long/char etc. If untrusted UTF-8 turns out to be invalid, only the valid prefix (if any) of the invalid arg is substituted. Substituting single chars only makes sense if the char is an ASCII character. In a nutshell:
F = fast UTF-8 string substitution
L = locale-format string
E = escape <>& */ #ifndef STRING_UTF_HH #define STRING_UTF_HH #include /* Same interface, different implementation */ #endif jigdo-0.7.3/src/util/string.cc0000644000175000017500000001015410225730235016010 0ustar richardrichard/* $Id: string.cc,v 1.6 2005/04/09 10:38:21 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ #include #include #include #include //______________________________________________________________________ namespace { const int BUF_LEN = 40; // Enough room for 128-bit integers. :-) char buf[BUF_LEN]; const char* const PAD = " "; const char* const PAD_END = PAD + 40; } string& append(string& s, double x) { snprintf(buf, BUF_LEN, "%.1f", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, int x) { snprintf(buf, BUF_LEN, "%d", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned x) { snprintf(buf, BUF_LEN, "%u", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%u", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, long x) { snprintf(buf, BUF_LEN, "%ld", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long x) { snprintf(buf, BUF_LEN, "%lu", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%lu", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } #if HAVE_UNSIGNED_LONG_LONG string& append(string& s, unsigned long long x) { snprintf(buf, BUF_LEN, "%llu", x); buf[BUF_LEN - 1] = '\0'; return s += buf; } string& append(string& s, unsigned long long x, int width) { Assert(*PAD_END == '\0' && width < PAD_END - PAD); int written = snprintf(buf, BUF_LEN, "%llu", x); if (written < width) s += PAD_END - width + written; buf[BUF_LEN - 1] = '\0'; return s += buf; } #endif //______________________________________________________________________ string Subst::subst(const char* format, int args, const Subst arg[]) { // Create output string result; const char* i = format - 1; char max = '1' + args; while (*++i != '\0') { if (*i != '%') { result += *i; continue; } ++i; const char* iPos = i; while (*i == 'F' || *i == 'L' || *i == 'E') ++i; if (*i == '\0') break; if (*i == '%') { result += '%'; continue; } if (*i >= '1' && *i < max) { int n = *i - '1'; switch (arg[n].type) { case INT: snprintf(buf, BUF_LEN, "%d", arg[n].val.intVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case UNSIGNED: snprintf(buf, BUF_LEN, "%u", arg[n].val.unsignedVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case LONG: snprintf(buf, BUF_LEN, "%ld", arg[n].val.longVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case ULONG: snprintf(buf, BUF_LEN, "%lu", arg[n].val.ulongVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; # if HAVE_UNSIGNED_LONG_LONG case ULONGLONG: snprintf(buf, BUF_LEN, "%llu", arg[n].val.ulonglongVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; # endif case DOUBLE: snprintf(buf, BUF_LEN, "%f", arg[n].val.doubleVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; case CHAR: result += arg[n].val.charVal; break; case CHAR_P: result += arg[n].val.charPtr; break; case STRING_P: result += *arg[n].val.stringPtr; break; case POINTER: snprintf(buf, BUF_LEN, "%p", arg[n].val.pointerVal); buf[BUF_LEN - 1] = '\0'; result += buf; break; } continue; } i = iPos; result += '%'; result += *i; } return result; } jigdo-0.7.3/src/util/string.hh0000644000175000017500000001103410226060300016006 0ustar richardrichard/* $Id: string.hh,v 1.8 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file subst("Format %1, %2", arg1, arg2) creates strings with the arguments filled in, and does so in a safer way than sprintf() and friends. */ #ifndef STRING_HH #define STRING_HH #include #include //______________________________________________________________________ /** Convert a number x to characters and append to a string. */ string& append(string& s, double x); string& append(string& s, int x); string& append(string& s, unsigned x); string& append(string& s, long x); string& append(string& s, unsigned long x); #if HAVE_UNSIGNED_LONG_LONG string& append(string& s, unsigned long long x); #endif /** Convert a number x to characters and append to a string, padding at front with space characters. width must be <40*/ string& append(string& s, unsigned x, int width); string& append(string& s, unsigned long x, int width); #if HAVE_UNSIGNED_LONG_LONG string& append(string& s, unsigned long long x, int width); #endif //______________________________________________________________________ /** Class for passing arguments to Logger */ class Subst { public: Subst(int x) { type = INT; val.intVal = x; } Subst(unsigned x) { type = UNSIGNED; val.unsignedVal = x; } Subst(long x) { type = LONG; val.longVal = x; } Subst(unsigned long x) { type = ULONG; val.ulongVal = x; } # if HAVE_UNSIGNED_LONG_LONG Subst(unsigned long long x) { type = ULONGLONG; val.ulonglongVal = x; } # endif Subst(double x) { type = DOUBLE; val.doubleVal = x; } Subst(char x) { type = CHAR; val.charVal = x; } Subst(const char* x) { type = CHAR_P; val.charPtr = x; } Subst(const string& x) { type = STRING_P; val.stringPtr = &x; } Subst(const string* x) { type = STRING_P; val.stringPtr = x; } Subst(const void* x) { type = POINTER; val.pointerVal = x; } static string subst(const char* format, int args, const Subst arg[]); private: # ifndef DOXYGEN_SKIP # ifdef STRING_UTF_HH static inline void doSubst(string& result, const Subst arg[], int n, int flags); # endif # endif enum { INT, UNSIGNED, LONG, ULONG, ULONGLONG, DOUBLE, CHAR, CHAR_P, STRING_P, POINTER } type; union { int intVal; unsigned unsignedVal; long longVal; unsigned long ulongVal; # if HAVE_UNSIGNED_LONG_LONG unsigned long long ulonglongVal; # endif double doubleVal; char charVal; const char* charPtr; const string* stringPtr; const void* pointerVal; } val; }; //______________________________________________________________________ /* Example: cout << subst("file `%1' not found: %2", string("foo"), strerror(errno)); */ inline string subst(const char* format, Subst a) { return Subst::subst(format, 1, &a); } inline string subst(const char* format, Subst a, Subst b) { Subst arg[] = { a, b }; return Subst::subst(format, 2, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c) { Subst arg[] = { a, b, c }; return Subst::subst(format, 3, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d) { Subst arg[] = { a, b, c, d }; return Subst::subst(format, 4, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e) { Subst arg[] = { a, b, c, d, e }; return Subst::subst(format, 5, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f) { Subst arg[] = { a, b, c, d, e, f }; return Subst::subst(format, 6, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g) { Subst arg[] = { a, b, c, d, e, f, g }; return Subst::subst(format, 7, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g, Subst h) { Subst arg[] = { a, b, c, d, e, f, g, h }; return Subst::subst(format, 8, arg); } inline string subst(const char* format, Subst a, Subst b, Subst c, Subst d, Subst e, Subst f, Subst g, Subst h, Subst i) { Subst arg[] = { a, b, c, d, e, f, g, h, i }; return Subst::subst(format, 9, arg); } #endif jigdo-0.7.3/src/util/unistd-jigdo.h0000644000175000017500000000125710262246711016752 0ustar richardrichard/* $Id: unistd-jigdo.h,v 1.1 2005/07/04 14:55:05 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Avoid clash between ftruncate() between Win32 and gtk */ #ifndef UNISTD_JIGDO_HH /* Grr, on Windows, include/glib-2.0/glib/gwin32.h #defines ftruncate to g_win32_ftruncate, which causes trouble with unistd's ftruncate() declaration. */ #ifdef ftruncate # undef ftruncate #endif #include #endif jigdo-0.7.3/src/zstream-bz.cc0000644000175000017500000001377710224334171015637 0ustar richardrichard/* $Id: zstream-bz.cc,v 1.2 2005/04/04 21:58:17 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Zlib compression layer which integrates with C++ streams */ #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("zstream-bz") namespace { // Turn zlib error codes/messages into C++ exceptions void throwZerrorBz(int status) { Assert(status != BZ_OK); switch (status) { case BZ_OK: break; case BZ_MEM_ERROR: throw bad_alloc(); break; case -1: case -2: case -4: case -5: case -6: case -7: case -8: case -9: throw Zerror(status, ZibstreamBz::bzerrorstrings[-status]); default: string m = subst("libbz error %1", status); throw Zerror(status, m); } } } // namespace //______________________________________________________________________ const char* ZibstreamBz::bzerrorstrings[] = { "OK" ,"SEQUENCE_ERROR" ,"PARAM_ERROR" ,"MEM_ERROR" ,"DATA_ERROR" ,"DATA_ERROR_MAGIC" ,"IO_ERROR" ,"UNEXPECTED_EOF" ,"OUTBUFF_FULL" ,"CONFIG_ERROR" ,"" ,"" ,"" }; //______________________________________________________________________ /* libbz2 subdivides input data into chunks of 100000 to 900000 bytes, depending on compression level. For best compression results, BZIP input data chunks should be exactly as large as the block size. The bzip2 sources use n*100000-19 as the actual block size in one place, we'll use n*100000-50 just to be safe. NB: chunkLim() is the OUTPUT size limit for gzip, but the INPUT size limit for bzip2. */ void ZobstreamBz::open(bostream& s, int level, unsigned todoBufSz) { // Unlike zlib, libbz2 does not support level==0 if (level == 0) level = 1; compressLevel = level; z.next_in = 0; z.next_out = reinterpret_cast(zipBuf->data); z.avail_out = (zipBuf == 0 ? 0 : ZIPDATA_SIZE); z.total_in_lo32 = z.total_in_hi32 = 0; debug("BZ2_bzCompressInit"); int verbosity = 0; # if DEBUG if (debug.enabled()) verbosity = 2; # endif int status = BZ2_bzCompressInit(&z, level, verbosity, 0/*default workFactor*/); if (status == BZ_OK) memReleased = false; else throwZerrorBz(status); // Declare stream as open debug("opening"); Zobstream::open(s, 100000 * level - 50, todoBufSz); debug("opened, chunkLim=%1", chunkLim()); } //______________________________________________________________________ unsigned ZobstreamBz::partId() { return 0x41544144u; // "DATA" } void ZobstreamBz::deflateEnd() { int status = BZ2_bzCompressEnd(&z); memReleased = true; if (status != BZ_OK) throwZerrorBz(status); } void ZobstreamBz::deflateReset() { int status = BZ2_bzCompressEnd(&z); memReleased = true; if (status != BZ_OK) throwZerrorBz(status); z.next_in = 0; z.next_out = reinterpret_cast(zipBuf->data); z.avail_out = (zipBuf == 0 ? 0 : ZIPDATA_SIZE); z.total_in_lo32 = z.total_in_hi32 = 0; debug("BZ2_bzCompressInit deflateReset"); int verbosity = 0; # if DEBUG if (debug.enabled()) verbosity = 2; # endif status = BZ2_bzCompressInit(&z, compressLevel, verbosity, 0/*default workFactor*/); if (status == BZ_OK) memReleased = false; else throwZerrorBz(status); } //______________________________________________________________________ void ZobstreamBz::zip2(byte* start, unsigned len, bool finish) { debug("zip2 %1 bytes at %2", len, start); int flush = (finish ? BZ_FINISH : BZ_RUN); Assert(is_open()); if (z.total_in_lo32 <= chunkLim() && chunkLim() <= z.total_in_lo32 + len) flush = BZ_FINISH; // true <=> must call BZ2_bzCompress() at least once bool callLibBzOnce = (flush != BZ_RUN); z.next_in = reinterpret_cast(start); z.avail_in = len; while (z.avail_in != 0 || z.avail_out == 0 || callLibBzOnce) { callLibBzOnce = false; // If big enough, finish and write out this chunk int availInDifference = 0; if (z.total_in_lo32 <= chunkLim() && chunkLim() <= z.total_in_lo32 + z.avail_in) { // Adjust avail_in to hit the bzip2 block size exactly availInDifference = chunkLim() - z.total_in_lo32 - z.avail_in; // <0 flush = BZ_FINISH; } if (z.avail_out == 0) { // Get another output buffer object ZipData* zd; if (zipBufLast == 0 || zipBufLast->next == 0) { zd = new ZipData(); if (zipBuf == 0) zipBuf = zd; if (zipBufLast != 0) zipBufLast->next = zd; } else { zd = zipBufLast->next; } zipBufLast = zd; z.next_out = reinterpret_cast(zd->data); z.avail_out = ZIPDATA_SIZE; //cerr << "Zob: new ZipData @ " << &zd->data << endl; } debug("deflate ni=%1 ai=%2 no=%3 ao=%4", (void*)(z.next_in), z.avail_in, (void*)(z.next_out), z.avail_out); //memset(z.next_in, 0, z.avail_in); //memset(z.next_out, 0, z.avail_out); z.avail_in += availInDifference; debug("deflate ai=%1 availInDifference=%2 ti=%3", z.avail_in, availInDifference, z.total_in_lo32); int status = BZ2_bzCompress(&z, flush); // Call libbz2 z.avail_in -= availInDifference; debug("deflated ni=%1 ai=%2 no=%3 ao=%4 status=%5", (void*)(z.next_in), z.avail_in, (void*)(z.next_out), z.avail_out, status); if (status == BZ_STREAM_END) { unsigned ai = z.avail_in; char* ni = z.next_in; writeZipped(0x50495a42u); // BZIP flush = BZ_RUN; z.avail_in = ai; z.next_in = ni; } if (status == BZ_STREAM_END) continue; if (status != BZ_RUN_OK && status != BZ_FINISH_OK) throwZerrorBz(status); } } jigdo-0.7.3/src/zstream-bz.hh0000644000175000017500000001123010226060300015617 0ustar richardrichard/* $Id: zstream-bz.hh,v 1.3 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Bzip2 compression and decompression for zstream */ #ifndef ZSTREAM_BZ_HH #define ZSTREAM_BZ_HH #include #include #include #include //______________________________________________________________________ struct ZerrorBz : public Zerror { ZerrorBz(int s, const string& m) : Zerror(s, m) { } int status; }; //______________________________________________________________________ class ZobstreamBz : public Zobstream { public: inline ZobstreamBz(bostream& s, int level /*= 6*/, unsigned todoBufSz /*= 256U*/, MD5Sum* md /*= 0*/); ~ZobstreamBz() { Assert(memReleased); } /** @param s Output stream @param level 1 to 9 (0 is allowed but interpreted as 1) @param todoBufSz Size of mini buffer, which holds data sent to the stream with single put() calls or << statements */ void open(bostream& s, int level /*= 6*/, unsigned todoBufSz/* = 256U*/); protected: virtual unsigned partId(); virtual void deflateEnd(); virtual void deflateReset(); virtual unsigned totalOut() const { return z.total_out_lo32; } virtual unsigned totalIn() const { return z.total_in_lo32; } virtual unsigned availOut() const { return z.avail_out; } virtual unsigned availIn() const { return z.avail_in; } virtual byte* nextOut() const { return reinterpret_cast(z.next_out); } virtual byte* nextIn() const { return reinterpret_cast(z.next_in); } virtual void setTotalOut(unsigned n) { z.total_out_lo32 = n; z.total_out_hi32 = 0; } virtual void setTotalIn(unsigned n) { z.total_in_lo32 = n; z.total_in_hi32 = 0; } virtual void setAvailOut(unsigned n) { z.avail_out = n; } virtual void setAvailIn(unsigned n) { z.avail_in = n; } virtual void setNextOut(byte* n) { z.next_out = reinterpret_cast(n); } virtual void setNextIn(byte* n) { z.next_in = reinterpret_cast(n); } virtual void zip2(byte* start, unsigned len, bool finish = false); private: bz_stream z; int compressLevel; bool memReleased; }; //______________________________________________________________________ class ZibstreamBz : public Zibstream::Impl { public: class ZibstreamBzError : public Zerror { public: ZibstreamBzError(int s, const string& m) : Zerror(s, m) { } }; ZibstreamBz() : status(0), memReleased(true) { } ~ZibstreamBz() { Assert(memReleased); } virtual unsigned totalOut() const { return z.total_out_lo32; } virtual unsigned totalIn() const { return z.total_in_lo32; } virtual unsigned availOut() const { return z.avail_out; } virtual unsigned availIn() const { return z.avail_in; } virtual byte* nextOut() const { return reinterpret_cast(z.next_out); } virtual byte* nextIn() const { return reinterpret_cast(z.next_in); } virtual void setTotalOut(unsigned n) { z.total_out_lo32 = n; z.total_out_hi32 = 0; } virtual void setTotalIn(unsigned n) { z.total_in_lo32 = n; z.total_in_hi32 = 0; } virtual void setAvailIn(unsigned n) { z.avail_in = n; } virtual void setNextIn(byte* n) { z.next_in = reinterpret_cast(n); } virtual void init() { z.bzalloc = 0; z.bzfree = 0; z.opaque = 0; status = BZ2_bzDecompressInit(&z, 0/*silent*/, 0/*fast*/); if (ok()) memReleased = false; } virtual void end() { status = BZ2_bzDecompressEnd(&z); memReleased = true; } virtual void reset() { end(); if (status == BZ_OK) init(); } virtual void inflate(byte** nextOut, unsigned* availOut) { z.next_out = reinterpret_cast(*nextOut); z.avail_out = *availOut; status = BZ2_bzDecompress(&z); *nextOut = reinterpret_cast(z.next_out); *availOut = z.avail_out; } virtual bool streamEnd() const { return status == BZ_STREAM_END; } virtual bool ok() const { return status == BZ_OK; } static const char* bzerrorstrings[]; virtual void throwError() const { int s = status; if (s > 0) s = 0; throw ZibstreamBzError(status, bzerrorstrings[s*-1]); } private: int status; bz_stream z; bool memReleased; }; //====================================================================== ZobstreamBz::ZobstreamBz(bostream& s, int level, unsigned todoBufSz, MD5Sum* md) : Zobstream(md), memReleased(true) { z.bzalloc = 0; z.bzfree = 0; z.opaque = 0; open(s, level, todoBufSz); } #endif jigdo-0.7.3/src/zstream-gz.cc0000644000175000017500000001122710224334171015630 0ustar richardrichard/* $Id: zstream-gz.cc,v 1.3 2005/04/04 21:58:17 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Zlib compression layer which integrates with C++ streams */ #include #include #include #include #include #include #include #include #include #include #include //______________________________________________________________________ DEBUG_UNIT("zstream-gz") namespace { // Turn zlib error codes/messages into C++ exceptions void throwZerrorGz(int status, const char* zmsg) { string m; if (zmsg != 0) m += zmsg; Assert(status != Z_OK); switch (status) { case Z_OK: break; case Z_ERRNO: if (!m.empty() && errno != 0) m += " - "; if (errno != 0) m += strerror(errno); throw Zerror(Z_ERRNO, m); break; case Z_MEM_ERROR: throw bad_alloc(); break; // NB: fallthrough: case Z_STREAM_ERROR: if (m.empty()) m = "zlib Z_STREAM_ERROR"; case Z_DATA_ERROR: if (m.empty()) m = "zlib Z_DATA_ERROR"; case Z_BUF_ERROR: if (m.empty()) m = "zlib Z_BUF_ERROR"; case Z_VERSION_ERROR: if (m.empty()) m = "zlib Z_VERSION_ERROR"; default: throw Zerror(status, m); } } } //________________________________________ // void Zobstream::throwZerror(int status, const char* zmsg) { // ::throwZerror(status, zmsg); // } // void Zibstream::throwZerror(int status, const char* zmsg) { // ::throwZerror(status, zmsg); // } //______________________________________________________________________ void ZobstreamGz::open(bostream& s, unsigned chunkLimit, int level, int windowBits, int memLevel, unsigned todoBufSz) { z.next_in = 0; z.next_out = zipBuf->data; z.avail_out = (zipBuf == 0 ? 0 : ZIPDATA_SIZE); z.total_in = 0; debug("deflateInit2"); int status = deflateInit2(&z, level, Z_DEFLATED, windowBits, memLevel, Z_DEFAULT_STRATEGY); if (status == Z_OK) memReleased = false; else throwZerrorGz(status, z.msg); // Declare stream as open debug("opening"); Zobstream::open(s, chunkLimit, todoBufSz); debug("opened"); } //______________________________________________________________________ unsigned ZobstreamGz::partId() { return 0x41544144u; // "DATA" } void ZobstreamGz::deflateEnd() { int status = ::deflateEnd(&z); memReleased = true; if (status != Z_OK) throwZerrorGz(status, z.msg); } void ZobstreamGz::deflateReset() { int status = ::deflateReset(&z); if (status != Z_OK) throwZerrorGz(status, z.msg); } //______________________________________________________________________ void ZobstreamGz::zip2(byte* start, unsigned len, bool finish) { debug("zip2 %1 bytes at %2", len, start); int flush = (finish ? Z_FINISH : Z_NO_FLUSH); Assert(is_open()); // If big enough, finish and write out this chunk if (z.total_out > chunkLim()) flush = Z_FINISH; // true <=> must call deflate() at least once bool callZlibOnce = (flush != Z_NO_FLUSH); z.next_in = start; z.avail_in = len; while (z.avail_in != 0 || z.avail_out == 0 || callZlibOnce) { callZlibOnce = false; if (z.avail_out == 0) { // Get another output buffer object ZipData* zd; if (zipBufLast == 0 || zipBufLast->next == 0) { zd = new ZipData(); if (zipBuf == 0) zipBuf = zd; if (zipBufLast != 0) zipBufLast->next = zd; } else { zd = zipBufLast->next; } zipBufLast = zd; z.next_out = zd->data; z.avail_out = ZIPDATA_SIZE; //cerr << "Zob: new ZipData @ " << &zd->data << endl; } debug("deflate ni=%1 ai=%2 no=%3 ao=%4", z.next_in, z.avail_in, z.next_out, z.avail_out); //memset(z.next_in, 0, z.avail_in); //memset(z.next_out, 0, z.avail_out); int status = deflate(&z, flush); // Call zlib debug("deflated ni=%1 ai=%2 no=%3 ao=%4 status=%5", z.next_in, z.avail_in, z.next_out, z.avail_out, status); //cerr << "zip(" << (void*)start << ", " << len << ", " << flush // << ") returned " << status << endl; if (status == Z_STREAM_END // || (status == Z_OK && z.total_out > chunkLim) || (flush == Z_FULL_FLUSH && z.total_in != 0)) { writeZipped(0x41544144u); // DATA flush = Z_NO_FLUSH; } if (status == Z_STREAM_END) continue; if (status != Z_OK) throwZerrorGz(status, z.msg); } } jigdo-0.7.3/src/zstream-gz.hh0000644000175000017500000001153310226060300015632 0ustar richardrichard/* $Id: zstream-gz.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2004-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file zlib (gzip-style) compression and decompression for zstream */ #ifndef ZSTREAM_GZ_HH #define ZSTREAM_GZ_HH #include #include #include #include //______________________________________________________________________ struct ZerrorGz : public Zerror { ZerrorGz(int s, const string& m) : Zerror(s, m) { } int status; }; //______________________________________________________________________ class ZobstreamGz : public Zobstream { public: inline ZobstreamGz(bostream& s, unsigned chunkLimit, int level = Z_DEFAULT_COMPRESSION, int windowBits = 15, int memLevel = 8, unsigned todoBufSz = 256U, MD5Sum* md = 0); ~ZobstreamGz() { Assert(memReleased); } /** @param s Output stream @param chunkLimit Size limit for output data, will buffer this much @param level 0 to 9 @param windowBits zlib param @param memLevel zlib param @param todoBufSz Size of mini buffer, which holds data sent to the stream with single put() calls or << statements */ void open(bostream& s, unsigned chunkLimit, int level =Z_DEFAULT_COMPRESSION, int windowBits = 15, int memLevel = 8, unsigned todoBufSz = 256U); protected: virtual unsigned partId(); virtual void deflateEnd(); virtual void deflateReset(); virtual unsigned totalOut() const { return z.total_out; } virtual unsigned totalIn() const { return z.total_in; } virtual unsigned availOut() const { return z.avail_out; } virtual unsigned availIn() const { return z.avail_in; } virtual byte* nextOut() const { return z.next_out; } virtual byte* nextIn() const { return z.next_in; } virtual void setTotalOut(unsigned n) { z.total_out = n; } virtual void setTotalIn(unsigned n) { z.total_in = n; } virtual void setAvailOut(unsigned n) { z.avail_out = n; } virtual void setAvailIn(unsigned n) { z.avail_in = n; } virtual void setNextOut(byte* n) { z.next_out = n; } virtual void setNextIn(byte* n) { z.next_in = n; } virtual void zip2(byte* start, unsigned len, bool finish = false); private: // Throw a Zerror exception, or bad_alloc() for status==Z_MEM_ERROR // inline void throwZerror(int status, const char* zmsg); z_stream z; // To keep track in the dtor whether deflateEnd() has been called bool memReleased; }; //______________________________________________________________________ class ZibstreamGz : public Zibstream::Impl { public: class ZibstreamGzError : public Zerror { public: ZibstreamGzError(int s, const string& m) : Zerror(s, m) { } }; ZibstreamGz() : status(0), memReleased(true) { } ~ZibstreamGz() { Assert(memReleased); } virtual unsigned totalOut() const { return z.total_out; } virtual unsigned totalIn() const { return z.total_in; } virtual unsigned availOut() const { return z.avail_out; } virtual unsigned availIn() const { return z.avail_in; } virtual byte* nextOut() const { return z.next_out; } virtual byte* nextIn() const { return z.next_in; } virtual void setTotalOut(unsigned n) { z.total_out = n; } virtual void setTotalIn(unsigned n) { z.total_in = n; } virtual void setAvailIn(unsigned n) { z.avail_in = n; } virtual void setNextIn(byte* n) { z.next_in = n; } virtual void init() { //memset(&z, 0, sizeof(z)); z.zalloc = (alloc_func)0; z.zfree = (free_func)0; z.opaque = 0; status = inflateInit(&z); if (ok()) memReleased = false; } virtual void end() { status = inflateEnd(&z); memReleased = true; } virtual void reset() { status = inflateReset(&z); } virtual void inflate(byte** nextOut, unsigned* availOut) { z.next_out = *nextOut; z.avail_out = *availOut; status = ::inflate(&z, Z_NO_FLUSH); *nextOut = z.next_out; *availOut = z.avail_out; } virtual bool streamEnd() const { return status == Z_STREAM_END; } virtual bool ok() const { return status == Z_OK; } virtual void throwError() const { throw ZibstreamGzError(status, z.msg); } private: int status; z_stream z; // To keep track in the dtor whether deflateEnd() has been called bool memReleased; }; //====================================================================== ZobstreamGz::ZobstreamGz(bostream& s, unsigned chunkLimit, int level, int windowBits, int memLevel, unsigned todoBufSz, MD5Sum* md) : Zobstream(md), memReleased(true) { z.zalloc = (alloc_func)0; z.zfree = (free_func)0; z.opaque = 0; open(s, chunkLimit, level, windowBits, memLevel, todoBufSz); } #endif jigdo-0.7.3/src/zstream.cc0000644000175000017500000002155510224334171015217 0ustar richardrichard/* $Id: zstream.cc,v 1.11 2005/04/04 21:58:17 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. Zlib/bzlib2 compression layer which integrates with C++ streams */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // struct ZibstreamBz : Zibstream::Impl { }; //______________________________________________________________________ DEBUG_UNIT("zstream") //________________________________________ void Zobstream::close() { if (!is_open()) return; try { zip(todoBuf, todoCount, Z_FINISH); // Flush out remain. buffer contents deflateEnd(); } catch (Zerror) { zipBufLast = zipBuf; // Deallocate memory delete[] todoBuf; todoBuf = 0; todoBufSize = todoCount = 0; // Important; cf Zobstream() stream = 0; /* Only report errors *after* marking the stream as closed, to avoid another exception being thrown when the Zobstream object goes out of scope and ~Zobstream calls close() again. */ throw; } zipBufLast = zipBuf; // Deallocate memory delete[] todoBuf; todoBuf = 0; todoBufSize = todoCount = 0; // Important; cf Zobstream() stream = 0; } //______________________________________________________________________ // Write compressed, flushed data to output stream void Zobstream::writeZipped(unsigned partId) { debug("Writing %1 bytes compressed, was %2 uncompressed", totalOut(), totalIn()); // #Bytes Value Description // ---------------------------------------------------------------------- // 4 dataID "ID for the part: 'DATA' = the hex bytes 44 41 54 41" // 6 dataLen "Length of part, i.e. length of compressed data + 16" // 6 dataUnc "Number of bytes of *uncompressed* data of this part" // dataLen-16 "Compressed data" byte buf[16]; byte* p = buf; serialize4(partId, p); // DATA or BZIP uint64 l = totalOut() + 16; serialize6(l, p + 4); l = totalIn(); serialize6(l, p + 10); writeBytes(*stream, buf, 16); if (!stream->good()) throw Zerror(0, string(_("Could not write template data"))); if (md5sum != 0) md5sum->update(buf, 16); ZipData* zd = zipBuf; unsigned len; while (true) { Paranoid(zd != 0); len = (totalOut() < ZIPDATA_SIZE ? totalOut() : ZIPDATA_SIZE); writeBytes(*stream, zd->data, len); if (md5sum != 0) md5sum->update(zd->data, len); if (!stream->good()) throw Zerror(0, string(_("Could not write template data"))); zd = zd->next; if (len < ZIPDATA_SIZE || zd == 0) break; setTotalOut(totalOut() - ZIPDATA_SIZE); } zipBufLast = zipBuf; setNextOut(zipBuf->data); setAvailOut(ZIPDATA_SIZE); setTotalIn(0); setTotalOut(0); // NB: next_in, avail_in are left alone deflateReset(); // Might throw } //______________________________________________________________________ Zobstream& Zobstream::put(uint32 x) { if (todoCount > todoBufSize - 4) zip(todoBuf, todoCount); todoBuf[todoCount] = static_cast(x & 0xff); ++todoCount; todoBuf[todoCount] = static_cast((x >> 8) & 0xff); ++todoCount; todoBuf[todoCount] = static_cast((x >> 16) & 0xff); ++todoCount; todoBuf[todoCount] = static_cast((x >> 24) & 0xff); ++todoCount; return *this; } //====================================================================== void Zibstream::open(bistream& s) { Assert(!is_open()); Paranoid(buf == 0); buf = new byte[bufSize]; // z = new ZibstreamGz(); // TODO switch for each block // z->setNextIn(0); // z->setNextOut(0); // z->setAvailIn(0); // z->setAvailOut(0); // z->init(); // if (!z->ok()) z->throwError(); dataLen = dataUnc = 0; stream = &s; } void Zibstream::close() { if (!is_open()) return; if (z != 0) z->end(); // Deallocate memory delete[] buf; buf = 0; stream = 0; /* Only report errors *after* marking the stream as closed, to avoid another exception being thrown when the Zibstream object goes out of scope and ~Zibstream calls close() again. */ if (!z->ok()) z->throwError(); } //________________________________________ Zibstream& Zibstream::read(byte* dest, unsigned n) { gcountVal = 0; // in case n == 0 if (!good()) return *this; nextOut = dest; availOut = n; //cerr << "Zibstream::read: " << n << " to read, avail_in=" << z->availIn() // << endl; SerialIstreamIterator in(*stream); while (availOut > 0) { //____________________ /* If possible, uncompress into destination buffer. Handling this case first for speed */ if (z != 0 && z->availIn() != 0) { byte* oldNextOut = nextOut; z->inflate(&nextOut, &availOut); gcountVal = nextOut - dest; dataUnc -= nextOut - oldNextOut; debug("read: avail_out=%1 dataLen=%2 dataUnc=%3 - inflated %4", availOut, dataLen, dataUnc, nextOut - oldNextOut); Assert(dataUnc > 0 || z->streamEnd() || z->ok()); if (z->availOut() == 0) break; if (!z->ok() && !z->streamEnd()) z->throwError(); continue; } //____________________ // Need to read another DATA part? if (dataLen == 0) { Assert(dataUnc == 0); streamsize prevPos = stream->tellg(); unsigned id; unserialize4(id, in); if (!*stream || (id != DATA && id != BZIP)) { // Reached end of file or a non-DATA/BZIP part stream->seekg(prevPos, ios::beg); delete[] buf; buf = 0; // Causes fail() == true throw Zerror(0, string(_("Corrupted input data"))); } // Assert(dataUnc == 0); // const char* hdr = "DATA"; // const char* cur = hdr; // byte x; // while (*cur != '\0' && *stream) { // x = stream->get(); // Any errors handled below, after end of while() // //debug("read: cur=%1, infile=%2 @%3", int(*cur), x, // // implicit_cast(stream->tellg()) - 1); // if (*cur != x) { // Reached end of file or non-DATA part // stream->seekg(hdr - cur, ios::cur); // delete[] buf; // buf = 0; // Causes fail() == true // throw Zerror(0, string(_("Corrupted input data"))); // } // ++cur; // } unserialize6(dataLen, in); dataLen -= 16; unserialize6(dataUnc, in); # if 0 cerr << "Zibstream::read: avail_out=" << availOut << " dataLen=" << dataLen << " dataUnc=" << dataUnc << " - new DATA part" << endl; # endif if (dataUnc == 0 || !*stream) { delete[] buf; buf = 0; throw Zerror(0, string(_("Corrupted input data"))); } // Decide whether to (re)allocate inflater // One or both out of gz/bz will be null ZibstreamGz* gz = dynamic_cast(z); ZibstreamBz* bz = dynamic_cast(z); if ((id == DATA && gz == 0) || (id == BZIP && bz == 0)) { if (z != 0) { // Delete old, unneeded inflater z->end(); if (!z->ok()) z->throwError(); delete z; } // Allocate and init new one if (id == DATA) z = new ZibstreamGz(); else z = new ZibstreamBz(); z->setNextIn(0); z->setAvailIn(0); z->init(); if (!z->ok()) z->throwError(); } else { // Nothing changed - just recycle old inflater z->reset(); if (!z->ok()) z->throwError(); } } // endif (dataLen == 0) // Need to read another DATA part? //____________________ // Read data from file into buffer? unsigned toRead = (dataLen < bufSize ? dataLen : bufSize); byte* b = &buf[0]; z->setNextIn(b); z->setAvailIn(toRead); dataLen -= toRead; while (*stream && toRead > 0) { readBytes(*stream, b, toRead); unsigned n = stream->gcount(); b += n; toRead -= n; } # if 0 cerr << "Zibstream::read: avail_out=" << z->availOut() << " dataLen=" << dataLen << " dataUnc=" << dataUnc << " - read " << z->availIn() << " from file to buf" << endl; # endif if (!*stream) { delete[] buf; buf = 0; string err = subst(_("Error reading compressed data - %1"), strerror(errno)); throw Zerror(0, err); } //____________________ } // endwhile (availOut > 0) # if 0 cerr << "Zibstream::read: avail_out=" << z->availOut() << " dataLen=" << dataLen << " dataUnc=" << dataUnc << " - returns, gcount=" << gcountVal << " avail_in=" << z->availIn() << endl; # endif return *this; } jigdo-0.7.3/src/zstream.fh0000644000175000017500000000064207701377725015243 0ustar richardrichard/* $Id: zstream.fh,v 1.1.1.1 2003/07/04 22:29:41 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2002 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. */ struct Zerror; class Zobstream; class Zibstream; jigdo-0.7.3/src/zstream.hh0000644000175000017500000002514510261607620015233 0ustar richardrichard/* $Id: zstream.hh,v 1.10 2005/07/02 22:05:04 atterer Exp $ -*- C++ -*- __ _ |_) /| Copyright (C) 2001-2005 | richard@ | \/¯| Richard Atterer | atterer.net ¯ '` ¯ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2. See the file COPYING for details. *//** @file Zlib/bzlib2 compression layer which integrates with C++ streams. When deflating, chops up data into DATA chunks of approximately zippedBufSz (see ctor below and ../doc/TechDetails.txt). Maybe this should use streambuf, but 1) I don't really understand streambuf, and 2) the C++ library that comes with GCC 2.95 probably doesn't fully support it (not sure tho). */ #ifndef ZSTREAM_HH #define ZSTREAM_HH #include #include #include #include #include #include #include //______________________________________________________________________ /** Error messages returned by the zlib library. Note that bad_alloc() is thrown if zlib returns Z_MEM_ERROR. Base class for ZerrorGz. */ struct Zerror : Error { Zerror(int s, const string& m) : Error(m), status(s) { } int status; }; /** Output stream which compresses the data sent to it before writing it to its final destination. The b is for buffered - this class will buffer *all* generated output in memory until the bytes written to the underlying stream (i.e. the compressed data) exceed chunkLimit - once this happens, the zlib data is flushed and the resultant compressed chunk written out with a header (cf ../doc/TechDetails.txt for format). This is jigdo-specific. Additional features mainly useful for jigdo: If an MD5Sum object is passed to Zobstream(), any data written to the output stream is also passed to the object. */ class Zobstream { public: inline explicit Zobstream(MD5Sum* md = 0); /** Calls close(), which might throw a Zerror exception! Call close() before destroying the object to avoid this. */ virtual ~Zobstream() { close(); delete zipBuf; Assert(todoBuf == 0); } bool is_open() const { return stream != 0; } /** Forces any remaining data to be compressed and written out */ void close(); /** Get reference to underlying ostream */ bostream& getStream() { return *stream; } /** Output 1 character */ inline Zobstream& put(unsigned char x); inline Zobstream& put(signed char x); /** Output the low 8 bits of an integer */ inline Zobstream& put(int x); inline Zobstream& put(char x); /** Output 32 bit integer in little-endian order */ Zobstream& put(uint32 x); /* Output n characters */ // inline Zobstream& write(const char* x, size_t n); // inline Zobstream& write(const signed char* x, size_t n); // Zobstream& write(const unsigned char* x, size_t n); // inline Zobstream& write(const void* x, size_t n); inline Zobstream& write(const byte* x, unsigned n); protected: static const unsigned ZIPDATA_SIZE = 64*1024; // Child classes must call this when their open() is called inline void open(bostream& s, unsigned chunkLimit, unsigned todoBufSz); unsigned chunkLim() const { return chunkLimVal; } // Write data in zipBuf void writeZipped(unsigned partId); virtual void deflateEnd() = 0; // May throw Zerror virtual void deflateReset() = 0; // May throw Zerror virtual unsigned totalOut() const = 0; virtual unsigned totalIn() const = 0; virtual unsigned availOut() const = 0; virtual unsigned availIn() const = 0; virtual byte* nextOut() const = 0; virtual byte* nextIn() const = 0; virtual void setTotalOut(unsigned n) = 0; virtual void setTotalIn(unsigned n) = 0; virtual void setAvailOut(unsigned n) = 0; virtual void setAvailIn(unsigned n) = 0; virtual void setNextOut(byte* n) = 0; virtual void setNextIn(byte* n) = 0; virtual void zip2(byte* start, unsigned len, bool finish) = 0; /* Compressed data is stored in a linked list of ZipData objects. During the Zobstream object's lifetime, the list is only ever added to, never shortened. */ struct ZipData { ZipData() : next(0) { } ~ZipData() { delete next; } ZipData* next; byte data[ZIPDATA_SIZE]; }; ZipData* zipBuf; // Start of linked list ZipData* zipBufLast; // Last link private: static const unsigned MIN_TODOBUF_SIZE = 256; // // Throw a Zerror exception, or bad_alloc() for status==Z_MEM_ERROR // inline void throwZerror(int status, const char* zmsg); // Pipe contents of todoBuf through zlib into zipBuf // void zip(byte* start, unsigned len, int flush = Z_NO_FLUSH); inline void zip(byte* start, unsigned len, bool finish = false); //z_stream z; byte* todoBuf; // Allocated during open(), deallocated during close() unsigned todoBufSize; // Size of todoBuf unsigned todoCount; // Offset of 1st unused byte in todoBuf bostream* stream; unsigned chunkLimVal; MD5Sum* md5sum; }; //______________________________________________________________________ /** Input stream which decompresses data. Analogous to Zobstream, aware of jigdo file formats - expects a number of DATA or BZIP parts at current stream position. */ class Zibstream { public: /** Interface for gzip and bzip2 implementors. */ class Impl { public: virtual ~Impl() { } virtual unsigned totalOut() const = 0; virtual unsigned totalIn() const = 0; virtual unsigned availOut() const = 0; virtual unsigned availIn() const = 0; virtual byte* nextOut() const = 0; virtual byte* nextIn() const = 0; virtual void setTotalOut(unsigned n) = 0; virtual void setTotalIn(unsigned n) = 0; virtual void setAvailIn(unsigned n) = 0; virtual void setNextIn(byte* n) = 0; /** Initialize, i.e. inflateInit(). {next,avail}{in,out} must be set up before calling this. Does not throw, sets an internal status flag. */ virtual void init() = 0; /** Finalize, i.e. inflateEnd(). Does not throw. */ virtual void end() = 0; /** Re-init, i.e. inflateReset() */ virtual void reset() = 0; /** Sets an internal status flag. Updates nextOut and availOut. */ virtual void inflate(byte** nextOut, unsigned* availOut) = 0; /** Check status flag: At stream end? */ virtual bool streamEnd() const = 0; /** Check status flag: OK? */ virtual bool ok() const = 0; /** Depending on internal status flag, throw appropriate Zerror. Never returns. */ virtual void throwError() const = 0; /* throws Zerror */ }; //________________________________________ inline explicit Zibstream(unsigned bufSz = 64*1024); /** Calls close(), which might throw a Zerror exception! Call close() before destroying the object to avoid this. */ virtual ~Zibstream() { close(); delete buf; if (z != 0) z->end(); delete z; } inline Zibstream(bistream& s, unsigned bufSz = 64*1024); bool is_open() const { return stream != 0; } void close(); /** Get reference to underlying istream */ bistream& getStream() { return *stream; } /// Input 1 character // inline Zibstream& get(unsigned char& x); // inline Zibstream& get(signed char& x); /// Input 32 bit integer in little-endian order //Zibstream& get(uint32 x); /// Input n characters // inline Zibstream& read(const char* x, size_t n); // inline Zibstream& read(const signed char* x, size_t n); // Zibstream& read(const unsigned char* x, size_t n); // inline Zibstream& read(const void* x, size_t n); Zibstream& read(byte* x, unsigned n); typedef uint64 streamsize; /** Number of characters read by last read() */ inline streamsize gcount() const { streamsize n = gcountVal; gcountVal = 0; return n; } bool good() const { return is_open() && buf != 0; } bool eof() const { return dataUnc == 0; } bool fail() const { return !good(); } bool bad() const { return false; } operator void*() const { return fail() ? (void*)0 : (void*)(-1); } bool operator!() const { return fail(); } private: // Throw a Zerror exception, or bad_alloc() for status==Z_MEM_ERROR //inline void throwZerror(int status, const char* zmsg); static const unsigned DATA = 0x41544144u; static const unsigned BZIP = 0x50495a42u; void open(bistream& s); // z_stream z; Impl* z; bistream* stream; mutable streamsize gcountVal; unsigned bufSize; byte* buf; // Contains compressed data uint64 dataLen; // bytes remaining to be read from current DATA part uint64 dataUnc; // bytes remaining in uncompressed DATA part byte* nextOut; // Pointer into output buffer unsigned availOut; // Bytes remaining in output buffer }; //______________________________________________________________________ /* Initialising todoBufSize and todoCount in this way allows us to move Assert(is_open) out of the inline functions and into zip() for calls to put() and write() */ Zobstream::Zobstream(MD5Sum* md) : zipBuf(0), zipBufLast(0), todoBuf(0), todoBufSize(0), todoCount(0), stream(0), md5sum(md) { } //________________________________________ void Zobstream::open(bostream& s, unsigned chunkLimit, unsigned todoBufSz) { Assert(!is_open()); todoBufSize = (MIN_TODOBUF_SIZE > todoBufSz ? MIN_TODOBUF_SIZE :todoBufSz); chunkLimVal = chunkLimit; todoCount = 0; todoBuf = new byte[todoBufSize]; stream = &s; // Declare as open Paranoid(stream != 0); } void Zobstream::zip(byte* start, unsigned len, bool finish) { if (len != 0 || finish) zip2(start, len, finish); todoCount = 0; } //________________________________________ Zobstream& Zobstream::put(unsigned char x) { if (todoCount >= todoBufSize) zip(todoBuf, todoCount); todoBuf[todoCount] = static_cast(x); ++todoCount; return *this; } Zobstream& Zobstream::put(signed char x) { if (todoCount >= todoBufSize) zip(todoBuf, todoCount); todoBuf[todoCount] = static_cast(x); ++todoCount; return *this; } Zobstream& Zobstream::put(char x) { if (todoCount >= todoBufSize) zip(todoBuf, todoCount); todoBuf[todoCount] = static_cast(x); ++todoCount; return *this; } Zobstream& Zobstream::put(int x) { if (todoCount >= todoBufSize) zip(todoBuf, todoCount); todoBuf[todoCount] = static_cast(x); ++todoCount; return *this; } Zobstream& Zobstream::write(const byte* x, unsigned n) { Assert(is_open()); if (n > 0) { zip(todoBuf, todoCount); // Zip remaining data in todoBuf zip(const_cast(x), n); // Zip byte array } return *this; } //________________________________________ Zibstream::Zibstream(unsigned bufSz) : z(0), stream(0), bufSize(bufSz), buf(0) { } Zibstream::Zibstream(bistream& s, unsigned bufSz) : z(0), stream(0), bufSize(bufSz), buf(0) { // data* will be init'ed by open() open(s); } #endif