diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..4ab2739 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,3 @@ +autom4te.cache +config.h.in +configure diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/AUTHORS diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..cf3af40 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +COPYRIGHT \ No newline at end of file diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..2471ec0 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,35 @@ +Copyright (c) 1998-2003 University of Illinois Board of Trustees +Copyright (c) 1998-2003 Mark D. Roth +All rights reserved. + +Developed by: Campus Information Technologies and Educational Services, + University of Illinois at Urbana-Champaign + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + +* Neither the names of Campus Information Technologies and Educational + Services, University of Illinois at Urbana-Champaign, nor the names + of its contributors may be used to endorse or promote products derived + from this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE CONTRIBUTORS 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 WITH THE SOFTWARE. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..03bef68 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,335 @@ + NOTE: + All releases below marked (Chris Frey) are maintenance releases + done by Chris Frey, temporarily stepping in for Mark Roth. + These releases are git-based only and can be found at: + http://repo.or.cz/w/libtar.git + + Both git downloads and tarball downloads are possible at this site. + + +libtar 1.2.20 - 2013/10/09 (Chris Frey) +------------- + Added extern "C" protectors to listhash.h + Added autoconf checks for __thread compiler support + Fixed size_t overflow bug, as reported by Timo Warns + Fixed thread-safe bug in th_get_pathname() (Sergey Zhitomirsky) + + +libtar 1.2.19 - 2012/12/11 (Chris Frey) +------------- + Removed varargs.h and all dependencies, to avoid user compile errors + + Fixed some short int / int compiler warnings in va_arg() usage + + Fixed some gcc built-in compiler warnings + + Changed autoconf support code from AC_RUN_ to AC_COMPILE_ to fix + issues reported during cross-compiling. + + Applied most of Jan Cermak's const char* function argument patch. + + +libtar 1.2.18 - 2012/08/02 (Chris Frey) +------------- + Added more forgiving CRC checking logic when reading tar files + + Note: If your application uses the macro th_crc_ok(), then to gain full + advantage of the changes in this version, you will need to recompile + your application against the new headers. Otherwise, the library is + drop-in replaceable, as usual. + + +libtar 1.2.17 - 2012/07/24 (Chris Frey) +------------- + Applied Tim Band's checksum patch from mailing list (thanks!) + + +libtar 1.2.16 - 2012/05/17 (Chris Frey) +------------- + Fixed build system to allow for out-of-source tree builds + + +libtar 1.2.15 - 2012/05/10 (Chris Frey) +------------- +Chris Frey (1): + Fixed harmless buffer overflow which is caught by FORTIFY on some systems + + +libtar 1.2.14 - 2011/12/22 (Chris Frey) +------------- +Chris Frey (1): + Fixed truncation check, so 100 char names get GNU extension support when enabled + + +libtar 1.2.13 - 2011/06/13 (Chris Frey) +------------- +Chris Frey (10): + Fixed incorrect URL in readme + Added autoconf/ as macro dir + Added autogen.sh script to build a fresh configure + Renamed autoconf/aclocal.m4 to psg.m4 so aclocal isn't so confused + Removed m4 includes, and straightened out [] m4 quoting for modern autoconfs + Removed auto-generated files + Added datarootdir to Makefile.in's + Fixed header warnings + Applied Marcin Gibula's patch fixing tar_extract_glob() + Changed root Makefile.in to Makefile.am, which make autoreconf workable + +Glenn McGrath (1): + Use libtool to build dynamic library + +James Morrison (1): + Document stupidity of tartype_t in libtar.c. + +Magnus Holmgren (1): + Escape hyphens that should be minus signs in man pages. + +Per Lidén (2): + Fix memory leak in th_get_pathname + Reduce memory used by libtar when extracting files. + +------------------------------------------------------------------------------ + +libtar 1.2.11 - 3/2/03 +------------- + +- updated autoconf macros, compat code, and listhash code +- fixed tar_extract_regfile() to pass mode argument to open() + (caused EPERM on Solaris NFS clients) +- updated README + +------------------------------------------------------------------------------ + +libtar 1.2.10 - 12/15/02 +------------- + +- updated README +- minor Makefile fixes +- fixed TH_ISREG() macro to not return true for hard links + +------------------------------------------------------------------------------ + +libtar 1.2.9 - 11/19/02 +------------ + +- fixed th_read() to return 1 on EOF + (thanks to Yves Crespin for the bug report) +- minor portability fixes + (thanks to Yves Crespin for the bug report) +- fixed segfault on extracting filenames with 8-bit ASCII characters + (thanks to Per Liden for the patch) +- fixed TH_ISDIR() macro and th_get_mode() function to handle old + archives that don't set the typeflag field right for directories +- use 0777 instead of 0755 in mkdirhier() + (thanks to Yves Crespin for the bug report) + +------------------------------------------------------------------------------ + +libtar 1.2.8 - 9/13/02 +------------ + +- added "-I../listhash" to CPPFLAGS in libtar/Makefile.in + (thanks to Kris Warkentin for the bug report) +- added .PHONY target to Makefile.in + (thanks to Steven Engelhardt for the bug report) + +------------------------------------------------------------------------------ + +libtar 1.2.7 - 9/12/02 +------------ + +- fixed minor bugs in listhash code + (thanks to Jim Knoble for the bug reports) + +------------------------------------------------------------------------------ + +libtar 1.2.6 - 9/10/02 +------------ + +- updated COPYRIGHT file +- do not check magic field by default + (replaced TAR_IGNORE_MAGIC option with TAR_CHECK_MAGIC to enable check) +- fixed th_get_mode() not to modify S_IFMT bits if they were already set +- fixed TH_IS*() macros to check the S_IFMT mode bits in addition to typeflag + (this allows us to handle old tar archives that set mode bits but not + typeflag field for directories and other special files) +- updated to autoconf-2.53 +- restructured autoconf macros +- added "b" to gzoflags in gzopen_frontend() for win32 compatibility + (thanks to Kris Eric Warkentin for reporting this) +- if O_BINARY is defined (as on win32), set that bit in oflags in tar_open() + (thanks to Kris Eric Warkentin for reporting this) +- also use O_BINARY in when calling open() from tar_extract_regfile() + (based on patch from Graeme Peterson ) +- added COMPAT_FUNC_MAKEDEV macro to handle 3-arg version of makedev() + (based on patch from Graeme Peterson ) + +------------------------------------------------------------------------------ + +libtar 1.2.5 - 2/20/02 +------------ + +- updated to autoconf-2.52 +- improved Makefile portability +- fixed memory leak in hard-link detection code + (thanks to Michael Kamp for the bug report) +- fixed memory leak in symlink handling code + (thanks to Michael Kamp for the bug report) +- fixed memory leak in GNU long filename code + +------------------------------------------------------------------------------ + +libtar 1.2.4 - 7/24/01 +------------ + +- code cleanups to make gcc -Wall happy + (thanks to Jim Knoble for the patch) +- call utime() before chmod() in tar_set_file_perms() for cygwin + (thanks to Kris Eric Warkentin for reporting this) +- added "-g" flag to trigger GNU extensions in libtar binary +- fixed buffer termination bugs in POSIX filename prefix encoding + (thanks to Joerg Schilling for reporting this) +- fixed bug in th_crc_calc() for filenames with 8-bit ASCII characters + (thanks to Hamdouni El Bachir for reporting the bug + and Antoniu-George SAVU for the patch) +- fixed backwards conditional expression in th_read() + (thanks to Antoniu-George SAVU for the patch) +- added new tar_open() options to replace compile-time settings: + TAR_IGNORE_EOT, TAR_IGNORE_MAGIC, TAR_CHECK_VERSION, TAR_IGNORE_CRC + (based on feedback from Kris Eric Warkentin ) + +------------------------------------------------------------------------------ + +libtar 1.2.3 - 6/26/01 +------------ + +- misc portability fixes for OpenBSD +- fixed libtar.h to work with C++ programs +- fixed tar_extract_file() to properly check for pre-existing symlinks + (based on patch from Per Lid?n ) +- fixed hash creation in tar_init() +- replaced mkdirhier() with non-recursive version +- updated autoconf macros, compat code, and listhash code +- reformatted code for readability + +------------------------------------------------------------------------------ + +libtar 1.2.2 - 1/12/01 +------------ + +- fixed th_print_long_ls() to not truncate user and group names +- code cleanups to make -Wall happy + +------------------------------------------------------------------------------ + +libtar 1.2.1 - 1/8/01 +------------ + +- updated WSG_ENCAP autoconf macro +- fixed autoconf macros to behave properly when a config.cache file + is present +- fixed doc/Makefile.in to create links during compilation, not + installation +- fixed listhash manpage .so link lists + +------------------------------------------------------------------------------ + +libtar 1.2 - 1/4/01 +---------- + +- minor code cleanups + +------------------------------------------------------------------------------ + +libtar 1.1.b8 - 1/2/01 +------------- + +- updated WSG_ENCAP autoconf macro + +------------------------------------------------------------------------------ + +libtar 1.1.b7 - 12/13/00 +------------- + +- fixed autoconf snprintf() test to make sure it NUL-terminates + +------------------------------------------------------------------------------ + +libtar 1.1.b6 - 11/30/00 +------------- + +- added $(DESTDIR) to Makefiles +- Makefile changes to support WSG_PKG and WSG_ENCAP autoconf macros +- changed lib/output.c to use strftime() where available + +------------------------------------------------------------------------------ + +libtar 1.1.b5 - 10/29/00 +------------- + +- Makefile fix + +------------------------------------------------------------------------------ + +libtar 1.1.b4 - 10/29/00 +------------- + +- more directory reorganization +- minor Makefile cleanups +- minor portability fixes +- added function typecasting to avoid compiler warnings + +------------------------------------------------------------------------------ + +libtar 1.1.b3 - 10/26/00 +------------- + +- updated aclocal.m4 +- updated README +- updated manpages +- minor directory structure changes because of CVS setup + +------------------------------------------------------------------------------ + +libtar 1.1.b2 - 10/5/00 +------------- + +- added --without-zlib configure option +- minor portability fixes + +------------------------------------------------------------------------------ + +libtar 1.1.b1 - 8/21/00 +------------- + +- API changes: + - implemented tar_fdopen() + - implemented tar_fd() + - added TAR **t argument to tar_open() instead of returning dynamic memory + - if TAR_NOOVERWRITE is set in options and O_CREAT is set in oflags, + tar_open() automatically sets O_EXCL as well + +------------------------------------------------------------------------------ + +libtar 1.1.b0 - 7/10/00 +------------- + +- API changes: + - replaced internal table of tar file types with a tartype_t passed to + tar_open() by the caller + (allows file access methods to be defined dynamically) + - fixed tar_append_tree() to grok normal files as well as directories + - replaced mk_dirs_for_file() with mkdirhier() from epkg + - replaced strtok_r() with strsep() + - updated list/hash code to new interface + +- autoconf changes: + - added aclocal.m4 to clean up configure.in + - minor portability fixes related to lib/fnmatch.c + +- fixed a bug in tar_open() where the result of open() was being + checked for 0 instead of -1 to detect error + +- updated libtar driver program to handle both .tar.gz and ordinary .tar + via the -z option + diff --git a/ChangeLog-1.0.x b/ChangeLog-1.0.x new file mode 100644 index 0000000..23b06b3 --- /dev/null +++ b/ChangeLog-1.0.x @@ -0,0 +1,141 @@ +libtar 1.0.2 - 6/21/00 +------------ + +- tar_set_file_perms() now calls chown() only if the effective user ID is 0 + (workaround for IRIX and HP-UX, which allow file giveaways) + +- tar_set_file_perms() now calls chmod() or lchmod() after chown() + (this fixes a problem with extracting setuid files under Linux) + +- removed calls to fchown() and fchmod() from tar_extract_regfile() + +- fixed bugs in th_read() which didn't set errno properly + +- removed various unused variables + +---------------------------------------------------------------------- + +libtar 1.0.1 - 4/1/00 +------------ + +- removed libgen.h include from dirname and basename compat code + +- added lib/fnmatch.c compatability module from OpenBSD + +- fixed several objdirs bugs in libtar/Makefile.in + +- misc Makefile changes (added $CPPFLAGS support, added -o flag to compile + commands, use $CFLAGS on link line, etc) + +- removed "inline" keyword from all source files to prevent portability + problems + +- updated README + +---------------------------------------------------------------------- + +libtar 1.0 - 1/2/00 +---------- + +- various portability fixes + +- "make install" now runs mkencap and epkg if they're available + +- libmisc is now integrated into libtar + +---------------------------------------------------------------------- + +libtar 0.5.6 beta - 12/16/99 +----------------- + +- changed API to allow better error reporting via errno + +- added manpages to document libtar API + +- replaced symbolic_mode() call with strmode() compatibility code + +---------------------------------------------------------------------- + +libtar 0.5.5 beta - 11/16/99 +----------------- + +- fixed conditional expression in extract.c to check if we're overwriting + a pre-existing file + +- many improvements to libtar.c driver program (better error checking, + added -C and -v options, etc) + +- changed API to include list of canned file types, instead of passing + function pointers to tar_open() + +- fixed tar_set_file_perms() to not complain about chown() if not root + and not to call utime() on a symlink + +- added hash code for extracting hard links in other directory paths + +- fixed tar_extract_glob() to only print filenames if TAR_VERBOSE option + is set + +- replaced GNU basename(), dirname(), and strdup() compatibility code + with OpenBSD versions + +- configure performs super-anal checking of basename() and dirname() + +---------------------------------------------------------------------- + +libtar 0.5.4 beta - 11/13/99 +----------------- + +- portability fix: use ranlib instead of ar -s + +- misc fixes in append.c, extract.c, and wrapper.c to do error checking + +- fixed a bug in tar_append_file() in append.c which added some garbage + characters to encoded symlink names (wasn't NULL-terminating the result + of readlink()) + +- fixed a bug in symbolic_mode() in output.c concerning setuid and setgid + bit displaying + +- fixed tar_extract_all() in wrapper.c to only call print_long_ls() if + the TAR_VERBOSE option is set + +- added libtar_version constant string to handle.c for external configure + scripts to detect what version of libtar is installed + +---------------------------------------------------------------------- + +libtar 0.5.3 beta - 09/27/99 +----------------- + +- fixed mk_dirs_for_file() to avoid broken dirname() implementations + +- misc portability fixes + +- merged old "compat" and "libds" directories into new "misc" directory + and cleaned up Makefiles + +---------------------------------------------------------------------- + +libtar 0.5.2 beta - 09/10/99 +----------------- + +- use calloc() instead of malloc() in tar_open() to fix a bounds-checking + bug in tar_extract_all() + +- fix tar_extract_all() to properly honor the prefix argument + +---------------------------------------------------------------------- + +libtar 0.5.1 beta - 08/27/99 +----------------- + +- misc portability fixes + +---------------------------------------------------------------------- + +libtar 0.5 beta - 07/05/99 +--------------- + +- first public release + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2bfc77f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ +DISTCHECK_CONFIGURE_FLAGS = + +ACLOCAL_FLAGS = -I autoconf +ACLOCAL_AMFLAGS = -I autoconf + +#PACKAGE_NAME = @PACKAGE_NAME@ +#PACKAGE_VERSION = @PACKAGE_VERSION@ + +@ENCAP_DEFS@ + +#@SET_MAKE@ + +SUBDIRS = lib libtar doc + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/NEWS diff --git a/README b/README new file mode 100644 index 0000000..563c868 --- /dev/null +++ b/README @@ -0,0 +1,121 @@ +libtar - C library for manipulating tar files +====== + +libtar is a library for manipulating tar files from within C programs. +Here are some of its features: + + * Handles both POSIX tar file format and the GNU extensions. + * API provides functions for easy use, such as tar_extract_all(). + * Also provides functions for more granular use, such as + tar_append_regfile(). + + +Installation +------------ + +To build libtar, you should be able to simply run these commands: + + ./configure + make + make install + + +Encap Package Support +--------------------- + +To build this software as an Encap package, you can pass the +--enable-encap option to configure. This will be automatically +enabled if the epkg or mkencap programs are detected on the system, +but can be overridden by the --disable-encap option. + +When building an Encap package, the configure script will automatically +adjust the installation prefix to use an appropriate Encap package +directory. It does this using a heuristic algorithm which examines the +values of the ${ENCAP_SOURCE} and ${ENCAP_TARGET} environment variables +and the argument to configure's --prefix option. + +If mkencap was detected on the system, it will be automatically run during +"make install". By default, epkg will also be run, but this can be +inhibited with the --disable-epkg-install configure option. + +For information on the Encap package management system, see the WSG +Encap Archive: + + http://www.encap.org/ + + +zlib Support +------------ + +The configure script will attempt to find the zlib library on your system +for use with the libtar driver program. The zlib package is available from: + + http://www.gzip.org/zlib/ + +If zlib is installed on your system, but you do not wish to use it, +specify the --without-zlib option when you invoke configure. + + +More Information +---------------- + +For documentation of the libtar API, see the enclosed manpages. For more +information on the libtar package, see: + + http://www.feep.net/libtar/ + +Source code for the latest version of libtar will be available there, as +well as Encap binary distributions for many common platforms. + + +Supported Platforms +------------------- + +I develop and test libtar on the following platforms: + + AIX 4.3.3 and 5.1 + HP-UX 11.00 + IRIX 6.5 + RedHat Linux 7.2 + Solaris 8 and 9 + +It should also build on the following platforms, but I do not actively +support them: + + AIX 3.2.5 + AIX 4.2.1 + Cygwin + FreeBSD + HP-UX 10.20 + Linux/libc5 + OpenBSD + Solaris 2.5 + Solaris 2.6 + Solaris 7 + +If you successfully build libtar on another platform, please email me a +patch and/or configuration information. + + +Compatibility Code +------------------ + +libtar depends on some library calls which are not available or not +usable on some platforms. To accomodate these systems, I've included +a version of these calls in the compat subdirectory. + +I've slightly modified these functions for integration into this source +tree, but the functionality has not been modified from the original +source. Please note that while this code should work for you, I didn't +write it, so please don't send me bug reports on it. + + +Author +------ + +Feedback and bug reports are welcome. + +Mark D. Roth +Campus Information Technologies and Educational Services +University of Illinois at Urbana-Champaign + diff --git a/TODO b/TODO new file mode 100644 index 0000000..a3921c0 --- /dev/null +++ b/TODO @@ -0,0 +1,10 @@ +* library API: + - revamp API (make analogs for Unix fs access calls) + - add dircache list to tar handle + - support star format(s)? + - add support for POSIX.1-2001 pax format + - large file support? + - support reading from tape devices with different block sizes? +* libtar program: + - make the libtar program a much more fully-featured tar + - move output.c and wrapper.c from lib directory to libtar directory diff --git a/autoconf/ac_path_generic.m4 b/autoconf/ac_path_generic.m4 new file mode 100644 index 0000000..401a291 --- /dev/null +++ b/autoconf/ac_path_generic.m4 @@ -0,0 +1,136 @@ +dnl @synopsis AC_PATH_GENERIC(LIBRARY [, MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl +dnl Runs a LIBRARY-config script and defines LIBRARY_CFLAGS and LIBRARY_LIBS +dnl +dnl The script must support `--cflags' and `--libs' args. +dnl If MINIMUM-VERSION is specified, the script must also support the +dnl `--version' arg. +dnl If the `--with-library-[exec-]prefix' arguments to ./configure are given, +dnl it must also support `--prefix' and `--exec-prefix'. +dnl (In other words, it must be like gtk-config.) +dnl +dnl For example: +dnl +dnl AC_PATH_GENERIC(Foo, 1.0.0) +dnl +dnl would run `foo-config --version' and check that it is at least 1.0.0 +dnl +dnl If so, the following would then be defined: +dnl +dnl FOO_CFLAGS to `foo-config --cflags` +dnl FOO_LIBS to `foo-config --libs` +dnl +dnl At present there is no support for additional "MODULES" (see AM_PATH_GTK) +dnl (shamelessly stolen from gtk.m4 and then hacked around a fair amount) +dnl +dnl @author Angus Lees +dnl @version $Id: ac_path_generic.m4,v 1.1.4.1 2002/09/06 19:43:55 roth Exp $ + +AC_DEFUN([AC_PATH_GENERIC], +[dnl +dnl we're going to need uppercase, lowercase and user-friendly versions of the +dnl string `LIBRARY' +pushdef([UP], translit([$1], [a-z], [A-Z]))dnl +pushdef([DOWN], translit([$1], [A-Z], [a-z]))dnl + +dnl +dnl Get the cflags and libraries from the LIBRARY-config script +dnl +AC_ARG_WITH(DOWN-prefix,[ --with-]DOWN[-prefix=PFX Prefix where $1 is installed (optional)], + DOWN[]_config_prefix="$withval", DOWN[]_config_prefix="") +AC_ARG_WITH(DOWN-exec-prefix,[ --with-]DOWN[-exec-prefix=PFX Exec prefix where $1 is installed (optional)], + DOWN[]_config_exec_prefix="$withval", DOWN[]_config_exec_prefix="") + + if test x$DOWN[]_config_exec_prefix != x ; then + DOWN[]_config_args="$DOWN[]_config_args --exec-prefix=$DOWN[]_config_exec_prefix" + if test x${UP[]_CONFIG+set} != xset ; then + UP[]_CONFIG=$DOWN[]_config_exec_prefix/bin/DOWN-config + fi + fi + if test x$DOWN[]_config_prefix != x ; then + DOWN[]_config_args="$DOWN[]_config_args --prefix=$DOWN[]_config_prefix" + if test x${UP[]_CONFIG+set} != xset ; then + UP[]_CONFIG=$DOWN[]_config_prefix/bin/DOWN-config + fi + fi + + AC_PATH_PROG(UP[]_CONFIG, DOWN-config, no) + ifelse([$2], , + AC_MSG_CHECKING(for $1), + AC_MSG_CHECKING(for $1 - version >= $2) + ) + no_[]DOWN="" + if test "$UP[]_CONFIG" = "no" ; then + no_[]DOWN=yes + else + UP[]_CFLAGS="`$UP[]_CONFIG $DOWN[]_config_args --cflags`" + UP[]_LIBS="`$UP[]_CONFIG $DOWN[]_config_args --libs`" + ifelse([$2], , ,[ + DOWN[]_config_major_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + DOWN[]_config_minor_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + DOWN[]_config_micro_version=`$UP[]_CONFIG $DOWN[]_config_args \ + --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + DOWN[]_wanted_major_version="regexp($2, [\<\([0-9]*\)], [\1])" + DOWN[]_wanted_minor_version="regexp($2, [\<\([0-9]*\)\.\([0-9]*\)], [\2])" + DOWN[]_wanted_micro_version="regexp($2, [\<\([0-9]*\).\([0-9]*\).\([0-9]*\)], [\3])" + + # Compare wanted version to what config script returned. + # If I knew what library was being run, i'd probably also compile + # a test program at this point (which also extracted and tested + # the version in some library-specific way) + if test "$DOWN[]_config_major_version" -lt \ + "$DOWN[]_wanted_major_version" \ + -o \( "$DOWN[]_config_major_version" -eq \ + "$DOWN[]_wanted_major_version" \ + -a "$DOWN[]_config_minor_version" -lt \ + "$DOWN[]_wanted_minor_version" \) \ + -o \( "$DOWN[]_config_major_version" -eq \ + "$DOWN[]_wanted_major_version" \ + -a "$DOWN[]_config_minor_version" -eq \ + "$DOWN[]_wanted_minor_version" \ + -a "$DOWN[]_config_micro_version" -lt \ + "$DOWN[]_wanted_micro_version" \) ; then + # older version found + no_[]DOWN=yes + echo -n "*** An old version of $1 " + echo -n "($DOWN[]_config_major_version" + echo -n ".$DOWN[]_config_minor_version" + echo ".$DOWN[]_config_micro_version) was found." + echo -n "*** You need a version of $1 newer than " + echo -n "$DOWN[]_wanted_major_version" + echo -n ".$DOWN[]_wanted_minor_version" + echo ".$DOWN[]_wanted_micro_version." + echo "***" + echo "*** If you have already installed a sufficiently new version, this error" + echo "*** probably means that the wrong copy of the DOWN-config shell script is" + echo "*** being found. The easiest way to fix this is to remove the old version" + echo "*** of $1, but you can also set the UP[]_CONFIG environment to point to the" + echo "*** correct copy of DOWN-config. (In this case, you will have to" + echo "*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf" + echo "*** so that the correct libraries are found at run-time)" + fi + ]) + fi + if test "x$no_[]DOWN" = x ; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + if test "$UP[]_CONFIG" = "no" ; then + echo "*** The DOWN-config script installed by $1 could not be found" + echo "*** If $1 was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the UP[]_CONFIG environment variable to the" + echo "*** full path to DOWN-config." + fi + UP[]_CFLAGS="" + UP[]_LIBS="" + ifelse([$4], , :, [$4]) + fi + AC_SUBST(UP[]_CFLAGS) + AC_SUBST(UP[]_LIBS) + + popdef([UP]) + popdef([DOWN]) +]) diff --git a/autoconf/ax_tls.m4 b/autoconf/ax_tls.m4 new file mode 100644 index 0000000..228110b --- /dev/null +++ b/autoconf/ax_tls.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://autoconf-archive.cryp.to/ax_tls.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_TLS +# +# DESCRIPTION +# +# Provides a test for the compiler support of thread local storage (TLS) +# extensions. Defines TLS if it is found. Currently only knows about GCC +# and MSVC. I think SunPro uses the same as GCC, and Borland apparently +# supports either. +# +# LICENSE +# +# Copyright (c) 2008 Alan Woodland +# +# 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 3 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, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +AC_DEFUN([AX_TLS], [ + AC_MSG_CHECKING(for thread local storage (TLS) class) + AC_CACHE_VAL(ac_cv_tls, [ + ax_tls_keywords="__thread __declspec(thread) none" + for ax_tls_keyword in $ax_tls_keywords; do + case $ax_tls_keyword in + none) ac_cv_tls=none ; break ;; + *) + AC_TRY_COMPILE( + [#include + static void + foo(void) { + static ] $ax_tls_keyword [ int bar; + exit(1); + }], + [], + [ac_cv_tls=$ax_tls_keyword ; break], + ac_cv_tls=none + ) + esac + done +]) + + if test "$ac_cv_tls" != "none"; then + dnl AC_DEFINE([TLS], [], [If the compiler supports a TLS storage class define it to that here]) + AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here]) + fi + AC_MSG_RESULT($ac_cv_tls) +]) diff --git a/autoconf/encap.m4 b/autoconf/encap.m4 new file mode 100644 index 0000000..67f4998 --- /dev/null +++ b/autoconf/encap.m4 @@ -0,0 +1,133 @@ +# ENCAP_PKG([mkencap-options], [install target if enabled], +# [install target if disabled]) +# --------------------------------------------------------- +# Check for Encap tools. +AC_DEFUN([ENCAP_PKG], [ + MKENCAP_OPTS=$1; + + # allow user to disable Encap support + AC_ARG_ENABLE([encap], + [ --disable-encap Do not configure as an Encap package], + [], + [enable_encap=default]) + + if test "$enable_encap" != "no"; then + # look for epkg and mkencap + AC_PATH_PROG([EPKG], [epkg]) + AC_PATH_PROG([MKENCAP], [mkencap]) + + # enable by default if epkg or mkencap are found + if test "${EPKG:+set}" = "set" || test "${MKENCAP:+set}" = "set" && test "$enable_encap" = "default"; then + enable_encap=yes; + fi + fi + + if test "$enable_encap" = "yes"; then + # generate fallback values for ${ENCAP_SOURCE} and ${ENCAP_TARGET} + # from the environment or the default prefix + if test -z "${ENCAP_SOURCE}" && test -z "${ENCAP_TARGET}"; then + ENCAP_SOURCE="${ac_default_prefix}/encap"; + ENCAP_TARGET="${ac_default_prefix}"; + elif test -z "${ENCAP_TARGET}"; then + ENCAP_TARGET="`dirname ${ENCAP_SOURCE}`"; + elif test -z "${ENCAP_SOURCE}"; then + ENCAP_SOURCE="${ENCAP_TARGET}/encap"; + fi + + # if --prefix is specified: + # 1) if its next-to-last component is "encap", assume that it + # points to the package directory + # 2) otherwise, assume it points to the target directory + if test "${prefix}" != "NONE"; then + prefixdir="`dirname ${prefix}`"; + prefixbase="`basename ${prefix}`"; + if test "`basename ${prefixdir}`" = "encap"; then + ENCAP_SOURCE="${prefixdir}"; + ENCAP_TARGET="`dirname ${ENCAP_SOURCE}`"; + elif test "${prefixdir}" != "${ENCAP_SOURCE}"; then + ENCAP_SOURCE="${prefix}/encap"; + ENCAP_TARGET="${prefix}"; + fi + if ( test "`basename ${prefixdir}`" = "encap" || \ + test "${prefixdir}" = "${ENCAP_SOURCE}" ) && \ + test "${prefixbase}" != "${PACKAGE_NAME}-${PACKAGE_VERSION}"; then + ENCAP_PKGSPEC="${prefixbase}"; + fi + fi + + # display results + AC_MSG_CHECKING([for Encap source directory]) + AC_MSG_RESULT([${ENCAP_SOURCE}]) + AC_MSG_CHECKING([for Encap target directory]) + AC_MSG_RESULT([${ENCAP_TARGET}]) + AC_MSG_CHECKING([for Encap package directory]) + if test "${ENCAP_PKGSPEC:-unset}" = "unset"; then + ENCAP_PKGSPEC='${PACKAGE_NAME}-${PACKAGE_VERSION}'; + AC_MSG_RESULT([${ENCAP_SOURCE}/${PACKAGE_NAME}-${PACKAGE_VERSION}]) + else + AC_MSG_RESULT([${ENCAP_SOURCE}/${ENCAP_PKGSPEC}]) + fi + prefix='${ENCAP_SOURCE}/${ENCAP_PKGSPEC}'; + + # override default sysconfdir and localstatedir + if test "$sysconfdir" = '${prefix}/etc'; then + sysconfdir='${ENCAP_TARGET}/etc'; + fi + if test "$localstatedir" = '${prefix}/var'; then + localstatedir='/var/lib/${PACKAGE_NAME}'; + fi + + # check for --disable-epkg-install + AC_ARG_ENABLE([epkg-install], + [ --disable-epkg-install Do not run epkg during make install], + [], + [enable_epkg_install=yes]) + if test "$enable_epkg_install" = "no"; then + EPKG=":"; + fi + + # generate Makefile variables +dnl AC_SUBST([ENCAP_SOURCE]) +dnl AC_SUBST([ENCAP_TARGET]) +dnl AC_SUBST([ENCAP_PKGSPEC]) +dnl AC_SUBST([EPKG]) +dnl AC_SUBST([MKENCAP]) +dnl AC_SUBST([MKENCAP_OPTS]) +dnl +dnl m4_ifdef([EM_MAKEFILE_END], [ +dnl # generate rules for make install target +dnl EM_MAKEFILE_END([[ +dnl target modify : +dnl command \\\${MKENCAP} \\\`test -f \\\${srcdir}/COPYRIGHT && echo -I \\\${srcdir}/COPYRIGHT\\\` \\\${MKENCAP_OPTS} -s \\\${DESTDIR}\\\${ENCAP_SOURCE} -e \\\${ENCAP_PKGSPEC} +dnl command if test -z \\\\\"\\\${DESTDIR}\\\\\"; then \ +dnl \\\${EPKG} -s \\\${ENCAP_SOURCE} -t \\\${ENCAP_TARGET} \\\${ENCAP_PKGSPEC}; \ +dnl fi +dnl ]])]) + + ENCAP_DEFS="ENCAP_SOURCE = ${ENCAP_SOURCE}\\ +ENCAP_TARGET = ${ENCAP_TARGET}\\ +ENCAP_PKGSPEC = ${ENCAP_PKGSPEC}\\ +EPKG = ${EPKG:-:}\\ +MKENCAP = ${MKENCAP:-:}\\ +MKENCAP_OPTS = ${MKENCAP_OPTS}"; + AC_SUBST([ENCAP_DEFS]) + + dnl ### generate rules for make install target + ENCAP_INSTALL_RULES='if test -f ${top_srcdir}/COPYRIGHT; then \\\ + ${INSTALL_DATA} ${top_srcdir}/COPYRIGHT ${ENCAP_SOURCE}/${ENCAP_PKGSPEC}; \\\ + fi\ + ${MKENCAP} ${MKENCAP_OPTS} -s ${DESTDIR}${ENCAP_SOURCE} -e ${ENCAP_PKGSPEC};\ + if test -z \"${DESTDIR}\"; then \\\ + ${EPKG} -s ${ENCAP_SOURCE} -t ${ENCAP_TARGET} ${ENCAP_PKGSPEC}; \\\ + fi'; + AC_SUBST([ENCAP_INSTALL_RULES]) + + ENCAP_INSTALL_TARGET=$2 + else + ENCAP_INSTALL_TARGET=$3 + fi + + AC_SUBST([ENCAP_INSTALL_TARGET]) +]) + + diff --git a/autoconf/psg.m4 b/autoconf/psg.m4 new file mode 100644 index 0000000..a9b6280 --- /dev/null +++ b/autoconf/psg.m4 @@ -0,0 +1,195 @@ +# PSG_LIB_READLINE +# ---------------- +# Check for GNU readline library. +AC_DEFUN([PSG_LIB_READLINE], [ + AC_CHECK_HEADERS([readline/readline.h]) + AC_CHECK_HEADERS([readline/history.h]) + if test "$ac_cv_header_readline_readline_h" = "yes"; then + AC_SEARCH_LIBS([tputs], [termcap curses]) + AC_CHECK_LIB([readline], [rl_callback_handler_install]) + fi +]) + + +# PSG_LIB_TAR +# ----------- +# Check for usable version of libtar library. +AC_DEFUN([PSG_LIB_TAR], [ + psg_old_libs="$LIBS" + LIBS="$LIBS -ltar" + AC_CACHE_CHECK([for usable version of libtar], + [psg_cv_lib_tar_usable], + [AC_TRY_RUN([ +#include +#include +#include + +int main(int argc, char *argv[]) { + return (strcmp(libtar_version, "1.2") >= 0 ? 0 : 1); +} +], + [psg_cv_lib_tar_usable=yes], + [psg_cv_lib_tar_usable=no], + [psg_cv_lib_tar_usable=no] + )] + ) + if test "$psg_cv_lib_tar_usable" = "yes"; then + AC_DEFINE([HAVE_LIBTAR], 1, + [Define if your system has a current version of libtar]) + else + LIBS="$psg_old_libs" + fi +]) + + +# PSG_LIB_FGET +# ------------ +# Check for usable version of libfget library. +AC_DEFUN([PSG_LIB_FGET], [ + psg_old_libs="$LIBS" + AC_CHECK_LIB([socket], [socket]) + AC_CHECK_LIB([nsl], [gethostbyname]) + LIBS="$LIBS -lfget" + AC_CACHE_CHECK([for usable version of libfget], + [psg_cv_lib_fget_usable], + [AC_TRY_COMPILE([ + #include + ], [ + FTP *ftp; + char buf[10240]; + struct ftp_url fu; + + ftp_url_parse("ftp://host.com/dir/file.txt", &fu); + + ftp_connect(&ftp, fu.fu_hostname, buf, sizeof(buf), 0, 0, + -1, -1, NULL, NULL); + ], + [psg_cv_lib_fget_usable=yes], + [psg_cv_lib_fget_usable=no] + )] + ) + if test "$psg_cv_lib_fget_usable" = "yes"; then + AC_DEFINE([HAVE_LIBFGET], 1, + [Define if your system has a current version of libfget]) + else + LIBS="$psg_old_libs"; + fi +]) + + +# PSG_LIB_WRAP +# ------------ +# Check for TCP Wrapper library. +AC_DEFUN([PSG_LIB_WRAP], [ + AC_CHECK_HEADERS([tcpd.h]) + if test "$ac_cv_header_tcpd_h" = "yes"; then + psg_old_libs="$LIBS" + LIBS="$LIBS -lwrap" + AC_CACHE_CHECK([for libwrap library], + [psg_cv_lib_wrap_hosts_ctl], + AC_TRY_LINK([ + #include + #include + int allow_severity; + int deny_severity; + ], [ + hosts_ctl("test", STRING_UNKNOWN, "10.0.0.1", STRING_UNKNOWN); + ], + [psg_cv_lib_wrap_hosts_ctl=yes], + [psg_cv_lib_wrap_hosts_ctl=no] + ) + ) + if test "$psg_cv_lib_wrap_hosts_ctl" = "yes"; then + AC_DEFINE([HAVE_LIBWRAP], 1, [Define if you have libwrap]) + else + LIBS="$psg_old_libs" + fi + fi +]) + + +# PSG_REPLACE_TYPE(type_t, default, [includes]) +# --------------------------------------------- +# Check for arbitrary type in arbitrary header file(s). +AC_DEFUN([PSG_REPLACE_TYPE], + [AC_CHECK_TYPES([$1], + , + [AC_DEFINE($1, $2, + [Define to `$2' if not defined in system header files.] + )], + $3 + )] +) + + +# PSG_SHLIB(includes, code) +# ------------------------- +# Check how to build shared libraries containing the specified code +# (very rudimentary). +AC_DEFUN([PSG_SHLIB], [ + AC_MSG_CHECKING([how to build shared libraries]) + cflag_options="-fpic"; + ldflag_options="-G -shared"; + if test "$CC" != "gcc"; then + case "`uname`" in + HP-UX) + cflag_options="+Z $cflag_options"; + ldflag_options="-Wl,-b $ldflag_options"; + ;; + SunOS) + cflag_options="-Kpic $cflag_options"; + ;; + esac + fi + for SHLIB_CFLAGS in $cflag_options ""; do + for SHLIB_LDFLAGS in $ldflag_options ""; do + psg_old_cflags="$CFLAGS"; + CFLAGS="$CFLAGS $SHLIB_CFLAGS"; + psg_old_ldflags="$LDFLAGS"; + LDFLAGS="$LDFLAGS $SHLIB_LDFLAGS"; + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +$1 + +int +dummy(void) +{ + $2 + return 0; +} +]])], + [psg_cv_flags_shlib="CFLAGS=$SHLIB_CFLAGS LDFLAGS=$SHLIB_LDFLAGS"], + [psg_cv_flags_shlib=no] + ) + CFLAGS="$psg_old_cflags"; + LDFLAGS="$psg_old_ldflags"; + if test "$psg_cv_flags_shlib" != "no"; then + break; + fi + done + if test "$psg_cv_flags_shlib" != "no"; then + break; + fi + done + if test "$psg_cv_flags_shlib" = "no"; then + SHLIB_CFLAGS=""; + SHLIB_LDFLAGS=""; + fi + AC_SUBST([SHLIB_CFLAGS]) + AC_SUBST([SHLIB_LDFLAGS]) + AC_MSG_RESULT([$psg_cv_flags_shlib]) +]) + + +# PSG_MODULE(subdir, [args, ...]) +# ------------------------------- +# Process the module.ac file in subdir. If the module.ac file defines a +# macro called subdir[]_INIT, call it with the arguments passed to +# PSG_MODULE(). +AC_DEFUN([PSG_MODULE], [ + m4_define([subdir], [$1])dnl + m4_include([$1]/module.ac)dnl + m4_ifdef([$1][_INIT], [$1][_INIT($@)])dnl + m4_undefine([subdir])dnl +]) + + diff --git a/compat/ChangeLog b/compat/ChangeLog new file mode 100644 index 0000000..a5d904d --- /dev/null +++ b/compat/ChangeLog @@ -0,0 +1,31 @@ +2003-01-08 added COMPAT_FUNC_MAKEDEV macro + +2002-12-20 added COMPAT_VAR___PROGNAME macro + +2002-12-19 include necessary header files from gethostbyname_r.c + and getservbyname_r.c + + check for size_t in COMPAT_FUNC_GETHOSTBYNAME_R and + COMPAT_FUNC_GETSERVBYNAME_R macros + +2002-12-18 define _LINUX_SOURCE_COMPAT in compat.h if strsep() + is needed on AIX + + avoid prototype syntax problem with IBM C compiler in + fnmatch.c + +2002-11-24 added COMPAT_PROTO_MACRO macro + +2002-11-22 updated gethostbyname_r() and getservbyname_r() code and + macros to detect and use weird AIX implementation + + added warnings if thread-safe functions are not found + +2002-10-18 new macros and corresponding compat code: + * COMPAT_FUNC_GETHOSTBYNAME_R + * COMPAT_FUNC_GETSERVBYNAME_R + +2002-10-15 added COMPAT_FUNC_GETPWUID_R macro + +2002-07-17 added inet_aton.c + diff --git a/compat/README b/compat/README new file mode 100644 index 0000000..fd23ced --- /dev/null +++ b/compat/README @@ -0,0 +1,12 @@ +Compatibility Suite +------------------- + +This directory contains a compatibility suite that provides alternate +implementations of various library functions which are not available or +not usable on some platforms. + +The original copyright information for each function is included in +the source files. I've modified the files slightly for integration +into this suite, but the functionality has not been modified from +the original source. + diff --git a/compat/TODO b/compat/TODO new file mode 100644 index 0000000..1b44a33 --- /dev/null +++ b/compat/TODO @@ -0,0 +1,4 @@ +- fix COMPAT_FUNC_INET_ATON to check for existing function in -lresolv +- fix COMPAT_FUNC_MAKEDEV to avoid problems with IBM C compiler + (calling cpp macro with more args that necessary doesn't fail, but + calling it with too few macros does fail) diff --git a/compat/basename.c b/compat/basename.c new file mode 100644 index 0000000..2ac1e13 --- /dev/null +++ b/compat/basename.c @@ -0,0 +1,74 @@ +/* $OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $ */ + +/* + * Copyright (c) 1997 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $"; +#endif /* not lint */ + +#include +#include +#include + +char * +openbsd_basename(path) + const char *path; +{ + static char bname[MAXPATHLEN]; + register const char *endp, *startp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + (void)strcpy(bname, "."); + return(bname); + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* All slashes becomes "/" */ + if (endp == path && *endp == '/') { + (void)strcpy(bname, "/"); + return(bname); + } + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + if (endp - startp + 1 > sizeof(bname)) { + errno = ENAMETOOLONG; + return(NULL); + } + (void)strncpy(bname, startp, endp - startp + 1); + bname[endp - startp + 1] = '\0'; + return(bname); +} diff --git a/compat/compat.h b/compat/compat.h new file mode 100644 index 0000000..70ac2f4 --- /dev/null +++ b/compat/compat.h @@ -0,0 +1,256 @@ +/* prototypes for borrowed "compatibility" code */ + +#include + +#include +#include + +#include +#include + +#ifdef HAVE_LIBGEN_H +# include +#endif + + +#if defined(NEED_BASENAME) && !defined(HAVE_BASENAME) + +# ifdef basename +# undef basename /* fix glibc brokenness */ +# endif + +char *openbsd_basename(const char *); +# define basename openbsd_basename + +#endif /* NEED_BASENAME && ! HAVE_BASENAME */ + + +#if defined(NEED_DIRNAME) && !defined(HAVE_DIRNAME) + +char *openbsd_dirname(const char *); +# define dirname openbsd_dirname + +#endif /* NEED_DIRNAME && ! HAVE_DIRNAME */ + + +#ifdef NEED_FNMATCH +# ifndef HAVE_FNMATCH + +# define FNM_NOMATCH 1 /* Match failed. */ + +# define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +# define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +# define FNM_PERIOD 0x04 /* Period must be matched by period. */ + +# define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ +# define FNM_CASEFOLD 0x10 /* Case insensitive search. */ +# define FNM_IGNORECASE FNM_CASEFOLD +# define FNM_FILE_NAME FNM_PATHNAME + +int openbsd_fnmatch(const char *, const char *, int); +# define fnmatch openbsd_fnmatch + +# else /* HAVE_FNMATCH */ + +# ifdef HAVE_FNMATCH_H +# include +# endif + +# endif /* ! HAVE_FNMATCH */ +#endif /* NEED_FNMATCH */ + + +#ifdef NEED_GETHOSTBYNAME_R + +# include + +# if GETHOSTBYNAME_R_NUM_ARGS != 6 + +int compat_gethostbyname_r(const char *, struct hostent *, + char *, size_t, struct hostent **, int *); + +# define gethostbyname_r compat_gethostbyname_r + +# endif /* GETHOSTBYNAME_R_NUM_ARGS != 6 */ + +#endif /* NEED_GETHOSTBYNAME_R */ + + +#if defined(NEED_GETHOSTNAME) && !defined(HAVE_GETHOSTNAME) + +int gethostname(char *, size_t); + +#endif /* NEED_GETHOSTNAME && ! HAVE_GETHOSTNAME */ + + +#ifdef NEED_GETSERVBYNAME_R + +# include + +# if GETSERVBYNAME_R_NUM_ARGS != 6 + +int compat_getservbyname_r(const char *, const char *, struct servent *, + char *, size_t, struct servent **); + +# define getservbyname_r compat_getservbyname_r + +# endif /* GETSERVBYNAME_R_NUM_ARGS != 6 */ + +#endif /* NEED_GETSERVBYNAME_R */ + + + +#ifdef NEED_GLOB +# ifndef HAVE_GLOB + +typedef struct { + int gl_pathc; /* Count of total paths so far. */ + int gl_matchc; /* Count of paths matching pattern. */ + int gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ + /* Copy of errfunc parameter to glob. */ + int (*gl_errfunc)(const char *, int); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir)(void *); + struct dirent *(*gl_readdir)(void *); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *, struct stat *); + int (*gl_stat)(const char *, struct stat *); +} glob_t; + +/* Flags */ +# define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +# define GLOB_DOOFFS 0x0002 /* Use gl_offs. */ +# define GLOB_ERR 0x0004 /* Return on error. */ +# define GLOB_MARK 0x0008 /* Append / to matching directories. */ +# define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +# define GLOB_NOSORT 0x0020 /* Don't sort. */ + +# define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +# define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ +# define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +# define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +# define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +# define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +# define GLOB_NOESCAPE 0x1000 /* Disable backslash escaping. */ + +/* Error values returned by glob(3) */ +# define GLOB_NOSPACE (-1) /* Malloc call failed. */ +# define GLOB_ABORTED (-2) /* Unignored error. */ +# define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */ +# define GLOB_NOSYS (-4) /* Function not supported. */ +# define GLOB_ABEND GLOB_ABORTED + +int openbsd_glob(const char *, int, int (*)(const char *, int), glob_t *); +void openbsd_globfree(glob_t *); +# define glob openbsd_glob +# define globfree openbsd_globfree + +# else /* HAVE_GLOB */ + +# ifdef HAVE_GLOB_H +# include +# endif + +# endif /* ! HAVE_GLOB */ +#endif /* NEED_GLOB */ + + +#if defined(NEED_INET_ATON) && !defined(HAVE_INET_ATON) + +int inet_aton(const char *, struct in_addr *); + +#endif /* NEED_INET_ATON && ! HAVE_INET_ATON */ + + +#ifdef NEED_MAKEDEV + +# ifdef MAJOR_IN_MKDEV +# include +# else +# ifdef MAJOR_IN_SYSMACROS +# include +# endif +# endif + +/* +** On most systems makedev() has two args. +** Some weird systems, like QNX6, have makedev() functions that expect +** an extra first argument for "node", which can be 0 for a local +** machine. +*/ + +# ifdef MAKEDEV_THREE_ARGS +# define compat_makedev(maj, min) makedev(0, maj, min) +# else +# define compat_makedev makedev +# endif + +#endif /* NEED_MAKEDEV */ + + +#if defined(NEED_SNPRINTF) && !defined(HAVE_SNPRINTF) + +int mutt_snprintf(char *, size_t, const char *, ...); +int mutt_vsnprintf(char *, size_t, const char *, va_list); +#define snprintf mutt_snprintf +#define vsnprintf mutt_vsnprintf + +#endif /* NEED_SNPRINTF && ! HAVE_SNPRINTF */ + + +#if defined(NEED_STRLCAT) && !defined(HAVE_STRLCAT) + +size_t strlcat(char *, const char *, size_t); + +#endif /* NEED_STRLCAT && ! HAVE_STRLCAT */ + + +#if defined(NEED_STRLCPY) && !defined(HAVE_STRLCPY) + +size_t strlcpy(char *, const char *, size_t); + +#endif /* NEED_STRLCPY && ! HAVE_STRLCPY */ + + +#if defined(NEED_STRDUP) && !defined(HAVE_STRDUP) + +char *openbsd_strdup(const char *); +# define strdup openbsd_strdup + +#endif /* NEED_STRDUP && ! HAVE_STRDUP */ + + +#if defined(NEED_STRMODE) && !defined(HAVE_STRMODE) + +void strmode(register mode_t, register char *); + +#endif /* NEED_STRMODE && ! HAVE_STRMODE */ + + +#if defined(NEED_STRRSTR) && !defined(HAVE_STRRSTR) + +char *strrstr(char *, char *); + +#endif /* NEED_STRRSTR && ! HAVE_STRRSTR */ + + +#ifdef NEED_STRSEP + +# ifdef HAVE_STRSEP +# define _LINUX_SOURCE_COMPAT /* needed on AIX 4.3.3 */ +# else + +char *strsep(register char **, register const char *); + +# endif + +#endif /* NEED_STRSEP */ + + diff --git a/compat/dirname.c b/compat/dirname.c new file mode 100644 index 0000000..986db4a --- /dev/null +++ b/compat/dirname.c @@ -0,0 +1,77 @@ +/* $OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $ */ + +/* + * Copyright (c) 1997 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $"; +#endif /* not lint */ + +#include +#include +#include + +char * +openbsd_dirname(path) + const char *path; +{ + static char bname[MAXPATHLEN]; + register const char *endp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + (void)strcpy(bname, "."); + return(bname); + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + (void)strcpy(bname, *endp == '/' ? "/" : "."); + return(bname); + } else { + do { + endp--; + } while (endp > path && *endp == '/'); + } + + if (endp - path + 1 > sizeof(bname)) { + errno = ENAMETOOLONG; + return(NULL); + } + (void)strncpy(bname, path, endp - path + 1); + bname[endp - path + 1] = '\0'; + return(bname); +} diff --git a/compat/fnmatch.c b/compat/fnmatch.c new file mode 100644 index 0000000..fe75f0e --- /dev/null +++ b/compat/fnmatch.c @@ -0,0 +1,237 @@ +/* $OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; +#else +static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +#include + +#include + +#ifdef STDC_HEADERS +# include +#endif + +#ifdef HAVE_CTYPE_H +# include +#endif + +#include + + +#define EOS '\0' + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + +#ifdef NO_IBM_COMPILER_HORKAGE +static int rangematch (const char *, char, int, char **); +#else +static int rangematch (); +#endif + +int +fnmatch(pattern, string, flags) + const char *pattern, *string; + int flags; +{ + const char *stringstart; + char *newp; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) { + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + } else if (c == '/' && (flags & FNM_PATHNAME)) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && (flags & FNM_PATHNAME)) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + switch (rangematch(pattern, *string, flags, &newp)) { + case RANGE_ERROR: + /* not a good range, treat as normal text */ + goto normal; + case RANGE_MATCH: + pattern = newp; + break; + case RANGE_NOMATCH: + return (FNM_NOMATCH); + } + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + normal: + if (c != *string && !((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string)))) + return (FNM_NOMATCH); + ++string; + break; + } + /* NOTREACHED */ +} + +static int +rangematch(pattern, test, flags, newp) + const char *pattern; + char test; + int flags; + char **newp; +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ((negate = (*pattern == '!' || *pattern == '^'))) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = tolower((unsigned char)test); + + /* + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + */ + ok = 0; + c = *pattern++; + do { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (RANGE_ERROR); + if (c == '/' && (flags & FNM_PATHNAME)) + return (RANGE_NOMATCH); + if ((flags & FNM_CASEFOLD)) + c = tolower((unsigned char)c); + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (RANGE_ERROR); + if (flags & FNM_CASEFOLD) + c2 = tolower((unsigned char)c2); + if (c <= test && test <= c2) + ok = 1; + } else if (c == test) + ok = 1; + } while ((c = *pattern++) != ']'); + + *newp = (char *)pattern; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); +} diff --git a/compat/gethostbyname_r.c b/compat/gethostbyname_r.c new file mode 100644 index 0000000..5264b84 --- /dev/null +++ b/compat/gethostbyname_r.c @@ -0,0 +1,41 @@ +/* +** Copyright 2002 University of Illinois Board of Trustees +** Copyright 2002 Mark D. Roth +** All rights reserved. +** +** gethostbyname_r.c - gethostbyname_r() function for compatibility library +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include + + +int +compat_gethostbyname_r(const char *name, struct hostent *hp, + char *buf, size_t buflen, + struct hostent **hpp, int *herr) +{ +#if GETHOSTBYNAME_R_NUM_ARGS == 5 + *hpp = gethostbyname_r(name, hp, buf, buflen, herr); + + if (*hpp == NULL) + return -1; + return 0; +#elif GETHOSTBYNAME_R_NUM_ARGS == 3 + struct hostent_data hdata; + + if (gethostbyname_r(name, hp, &hdata) == -1) + return -1; + *hpp = hp; + return 0; +#endif /* GETHOSTBYNAME_R_NUM_ARGS == 5 */ +} + + diff --git a/compat/gethostname.c b/compat/gethostname.c new file mode 100644 index 0000000..1abaae1 --- /dev/null +++ b/compat/gethostname.c @@ -0,0 +1,36 @@ +/* gethostname.c: minimal substitute for missing gethostname() function + * created 2000-Mar-02 jmk + * requires SVR4 uname() and -lc + * + * by Jim Knoble + * Copyright ? 2000 Jim Knoble + * + * 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. + * + * This software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties of + * merchantability, fitness for a particular purpose and + * noninfringement. In no event shall the author(s) 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. + */ + +#include +#include + +int gethostname(char *name, size_t len) +{ + struct utsname u; + int status = uname(&u); + if (-1 != status) { + strncpy(name, u.nodename, len); + name[len - 1] = '\0'; + } + return(status); +} + diff --git a/compat/getservbyname_r.c b/compat/getservbyname_r.c new file mode 100644 index 0000000..e386bc9 --- /dev/null +++ b/compat/getservbyname_r.c @@ -0,0 +1,41 @@ +/* +** Copyright 2002 University of Illinois Board of Trustees +** Copyright 2002 Mark D. Roth +** All rights reserved. +** +** getservbyname_r.c - getservbyname_r() function for compatibility library +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include + + +int +compat_getservbyname_r(const char *name, const char *proto, + struct servent *sp, char *buf, size_t buflen, + struct servent **spp) +{ +#if GETSERVBYNAME_R_NUM_ARGS == 5 + *spp = getservbyname_r(name, proto, sp, buf, buflen); + + if (*spp == NULL) + return -1; + return 0; +#elif GETSERVBYNAME_R_NUM_ARGS == 4 + struct servent_data sdata; + + if (getservbyname_r(name, proto, sp, &sdata) == -1) + return -1; + *spp = sp; + return 0; +#endif /* GETSERVBYNAME_R_NUM_ARGS == 5 */ +} + + diff --git a/compat/glob.c b/compat/glob.c new file mode 100644 index 0000000..9ee235a --- /dev/null +++ b/compat/glob.c @@ -0,0 +1,873 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93"; +#else +static char rcsid[] = "$OpenBSD: glob.c,v 1.8 1998/08/14 21:39:30 deraadt Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * glob(3) -- a superset of the one defined in POSIX 1003.2. + * + * The [!...] convention to negate a range is supported (SysV, Posix, ksh). + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_QUOTE: + * Escaping convention: \ inhibits any special meaning the following + * character might have (except \ at end of string is retained). + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + * GLOB_NOMAGIC: + * Same as GLOB_NOCHECK, but it will only append pattern if it did + * not contain any magic characters. [Used in csh style globbing] + * GLOB_ALTDIRFUNC: + * Use alternately specified directory access functions. + * GLOB_TILDE: + * expand ~user/foo to the /home/dir/of/user/foo + * GLOB_BRACE: + * expand {1,2}{a,b} to 1a 1b 2a 2b + * gl_matchc: + * Number of matches in the current invocation of glob. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + + +#define DOLLAR '$' +#define DOT '.' +#define EOS '\0' +#define LBRACKET '[' +#define NOT '!' +#define QUESTION '?' +#define QUOTE '\\' +#define RANGE '-' +#define RBRACKET ']' +#define SEP '/' +#define STAR '*' +#define TILDE '~' +#define UNDERSCORE '_' +#define LBRACE '{' +#define RBRACE '}' +#define SLASH '/' +#define COMMA ',' + +#ifndef DEBUG + +#define M_QUOTE 0x8000 +#define M_PROTECT 0x4000 +#define M_MASK 0xffff +#define M_ASCII 0x00ff + +typedef u_short Char; + +#else + +#define M_QUOTE 0x80 +#define M_PROTECT 0x40 +#define M_MASK 0xff +#define M_ASCII 0x7f + +typedef char Char; + +#endif + + +#define CHAR(c) ((Char)((c)&M_ASCII)) +#define META(c) ((Char)((c)|M_QUOTE)) +#define M_ALL META('*') +#define M_END META(']') +#define M_NOT META('!') +#define M_ONE META('?') +#define M_RNG META('-') +#define M_SET META('[') +#define ismeta(c) (((c)&M_QUOTE) != 0) + + +static int compare (const void *, const void *); +static void g_Ctoc (const Char *, char *); +static int g_lstat (Char *, struct stat *, glob_t *); +static DIR *g_opendir (Char *, glob_t *); +static Char *g_strchr (Char *, int); +#ifdef notdef +static Char *g_strcat (Char *, const Char *); +#endif +static int g_stat (Char *, struct stat *, glob_t *); +static int glob0 (const Char *, glob_t *); +static int glob1 (Char *, glob_t *); +static int glob2 (Char *, Char *, Char *, glob_t *); +static int glob3 (Char *, Char *, Char *, Char *, glob_t *); +static int globextend (const Char *, glob_t *); +static const Char * globtilde (const Char *, Char *, size_t, glob_t *); +static int globexp1 (const Char *, glob_t *); +static int globexp2 (const Char *, const Char *, glob_t *, int *); +static int match (Char *, Char *, Char *); +#ifdef DEBUG +static void qprintf (const char *, Char *); +#endif + +int +openbsd_glob(pattern, flags, errfunc, pglob) + const char *pattern; + int flags, (*errfunc) __P((const char *, int)); + glob_t *pglob; +{ + const u_char *patnext; + int c; + Char *bufnext, *bufend, patbuf[MAXPATHLEN+1]; + + patnext = (u_char *) pattern; + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + if (!(flags & GLOB_DOOFFS)) + pglob->gl_offs = 0; + } + pglob->gl_flags = flags & ~GLOB_MAGCHAR; + pglob->gl_errfunc = errfunc; + pglob->gl_matchc = 0; + + bufnext = patbuf; + bufend = bufnext + MAXPATHLEN; + if (flags & GLOB_NOESCAPE) + while (bufnext < bufend && (c = *patnext++) != EOS) + *bufnext++ = c; + else { + /* Protect the quoted characters. */ + while (bufnext < bufend && (c = *patnext++) != EOS) + if (c == QUOTE) { + if ((c = *patnext++) == EOS) { + c = QUOTE; + --patnext; + } + *bufnext++ = c | M_PROTECT; + } + else + *bufnext++ = c; + } + *bufnext = EOS; + + if (flags & GLOB_BRACE) + return globexp1(patbuf, pglob); + else + return glob0(patbuf, pglob); +} + +/* + * Expand recursively a glob {} pattern. When there is no more expansion + * invoke the standard globbing routine to glob the rest of the magic + * characters + */ +static int globexp1(pattern, pglob) + const Char *pattern; + glob_t *pglob; +{ + const Char* ptr = pattern; + int rv; + + /* Protect a single {}, for find(1), like csh */ + if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) + return glob0(pattern, pglob); + + while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL) + if (!globexp2(ptr, pattern, pglob, &rv)) + return rv; + + return glob0(pattern, pglob); +} + + +/* + * Recursive brace globbing helper. Tries to expand a single brace. + * If it succeeds then it invokes globexp1 with the new pattern. + * If it fails then it tries to glob the rest of the pattern and returns. + */ +static int globexp2(ptr, pattern, pglob, rv) + const Char *ptr, *pattern; + glob_t *pglob; + int *rv; +{ + int i; + Char *lm, *ls; + const Char *pe, *pm, *pl; + Char patbuf[MAXPATHLEN + 1]; + + /* copy part up to the brace */ + for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) + continue; + ls = lm; + + /* Find the balanced brace */ + for (i = 0, pe = ++ptr; *pe; pe++) + if (*pe == LBRACKET) { + /* Ignore everything between [] */ + for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) + continue; + if (*pe == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pe = pm; + } + } + else if (*pe == LBRACE) + i++; + else if (*pe == RBRACE) { + if (i == 0) + break; + i--; + } + + /* Non matching braces; just glob the pattern */ + if (i != 0 || *pe == EOS) { + *rv = glob0(patbuf, pglob); + return 0; + } + + for (i = 0, pl = pm = ptr; pm <= pe; pm++) + switch (*pm) { + case LBRACKET: + /* Ignore everything between [] */ + for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) + continue; + if (*pm == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pm = pl; + } + break; + + case LBRACE: + i++; + break; + + case RBRACE: + if (i) { + i--; + break; + } + /* FALLTHROUGH */ + case COMMA: + if (i && *pm == COMMA) + break; + else { + /* Append the current string */ + for (lm = ls; (pl < pm); *lm++ = *pl++) + continue; + /* + * Append the rest of the pattern after the + * closing brace + */ + for (pl = pe + 1; (*lm++ = *pl++) != EOS;) + continue; + + /* Expand the current pattern */ +#ifdef DEBUG + qprintf("globexp2:", patbuf); +#endif + *rv = globexp1(patbuf, pglob); + + /* move after the comma, to the next string */ + pl = pm + 1; + } + break; + + default: + break; + } + *rv = 0; + return 0; +} + + + +/* + * expand tilde from the passwd file. + */ +static const Char * +globtilde(pattern, patbuf, patbuf_len, pglob) + const Char *pattern; + Char *patbuf; + size_t patbuf_len; + glob_t *pglob; +{ + struct passwd *pwd; + char *h; + const Char *p; + Char *b, *eb; + + if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) + return pattern; + + /* Copy up to the end of the string or / */ + eb = &patbuf[patbuf_len - 1]; + for (p = pattern + 1, h = (char *) patbuf; + h < (char *)eb && *p && *p != SLASH; *h++ = *p++) + continue; + + *h = EOS; + + if (((char *) patbuf)[0] == EOS) { + /* + * handle a plain ~ or ~/ by expanding $HOME + * first and then trying the password file + */ +#ifdef HAVE_ISSETUGID + if (issetugid() != 0 || (h = getenv("HOME")) == NULL) { +#endif + if ((pwd = getpwuid(getuid())) == NULL) + return pattern; + else + h = pwd->pw_dir; +#ifdef HAVE_ISSETUGID + } +#endif + } + else { + /* + * Expand a ~user + */ + if ((pwd = getpwnam((char*) patbuf)) == NULL) + return pattern; + else + h = pwd->pw_dir; + } + + /* Copy the home directory */ + for (b = patbuf; b < eb && *h; *b++ = *h++) + continue; + + /* Append the rest of the pattern */ + while (b < eb && (*b++ = *p++) != EOS) + continue; + *b = EOS; + + return patbuf; +} + + +/* + * The main glob() routine: compiles the pattern (optionally processing + * quotes), calls glob1() to do the real pattern matching, and finally + * sorts the list (unless unsorted operation is requested). Returns 0 + * if things went well, nonzero if errors occurred. It is not an error + * to find no matches. + */ +static int +glob0(pattern, pglob) + const Char *pattern; + glob_t *pglob; +{ + const Char *qpatnext; + int c, err, oldpathc; + Char *bufnext, patbuf[MAXPATHLEN+1]; + + qpatnext = globtilde(pattern, patbuf, sizeof(patbuf) / sizeof(Char), + pglob); + oldpathc = pglob->gl_pathc; + bufnext = patbuf; + + /* We don't need to check for buffer overflow any more. */ + while ((c = *qpatnext++) != EOS) { + switch (c) { + case LBRACKET: + c = *qpatnext; + if (c == NOT) + ++qpatnext; + if (*qpatnext == EOS || + g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) { + *bufnext++ = LBRACKET; + if (c == NOT) + --qpatnext; + break; + } + *bufnext++ = M_SET; + if (c == NOT) + *bufnext++ = M_NOT; + c = *qpatnext++; + do { + *bufnext++ = CHAR(c); + if (*qpatnext == RANGE && + (c = qpatnext[1]) != RBRACKET) { + *bufnext++ = M_RNG; + *bufnext++ = CHAR(c); + qpatnext += 2; + } + } while ((c = *qpatnext++) != RBRACKET); + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_END; + break; + case QUESTION: + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_ONE; + break; + case STAR: + pglob->gl_flags |= GLOB_MAGCHAR; + /* collapse adjacent stars to one, + * to avoid exponential behavior + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; + break; + default: + *bufnext++ = CHAR(c); + break; + } + } + *bufnext = EOS; +#ifdef DEBUG + qprintf("glob0:", patbuf); +#endif + + if ((err = glob1(patbuf, pglob)) != 0) + return(err); + + /* + * If there was no match we are going to append the pattern + * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified + * and the pattern did not contain any magic characters + * GLOB_NOMAGIC is there just for compatibility with csh. + */ + if (pglob->gl_pathc == oldpathc) { + if ((pglob->gl_flags & GLOB_NOCHECK) || + ((pglob->gl_flags & GLOB_NOMAGIC) && + !(pglob->gl_flags & GLOB_MAGCHAR))) + return(globextend(pattern, pglob)); + else + return(GLOB_NOMATCH); + } + if (!(pglob->gl_flags & GLOB_NOSORT)) + qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, + pglob->gl_pathc - oldpathc, sizeof(char *), compare); + return(0); +} + +static int +compare(p, q) + const void *p, *q; +{ + return(strcmp(*(char **)p, *(char **)q)); +} + +static int +glob1(pattern, pglob) + Char *pattern; + glob_t *pglob; +{ + Char pathbuf[MAXPATHLEN+1]; + + /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ + if (*pattern == EOS) + return(0); + return(glob2(pathbuf, pathbuf, pattern, pglob)); +} + +/* + * The functions glob2 and glob3 are mutually recursive; there is one level + * of recursion for each segment in the pattern that contains one or more + * meta characters. + */ +static int +glob2(pathbuf, pathend, pattern, pglob) + Char *pathbuf, *pathend, *pattern; + glob_t *pglob; +{ + struct stat sb; + Char *p, *q; + int anymeta; + + /* + * Loop over pattern segments until end of pattern or until + * segment with meta character found. + */ + for (anymeta = 0;;) { + if (*pattern == EOS) { /* End of pattern? */ + *pathend = EOS; + if (g_lstat(pathbuf, &sb, pglob)) + return(0); + + if (((pglob->gl_flags & GLOB_MARK) && + pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) + || (S_ISLNK(sb.st_mode) && + (g_stat(pathbuf, &sb, pglob) == 0) && + S_ISDIR(sb.st_mode)))) { + *pathend++ = SEP; + *pathend = EOS; + } + ++pglob->gl_matchc; + return(globextend(pathbuf, pglob)); + } + + /* Find end of next segment, copy tentatively to pathend. */ + q = pathend; + p = pattern; + while (*p != EOS && *p != SEP) { + if (ismeta(*p)) + anymeta = 1; + *q++ = *p++; + } + + if (!anymeta) { /* No expansion, do next segment. */ + pathend = q; + pattern = p; + while (*pattern == SEP) + *pathend++ = *pattern++; + } else /* Need expansion, recurse. */ + return(glob3(pathbuf, pathend, pattern, p, pglob)); + } + /* NOTREACHED */ +} + +static int +glob3(pathbuf, pathend, pattern, restpattern, pglob) + Char *pathbuf, *pathend, *pattern, *restpattern; + glob_t *pglob; +{ + register struct dirent *dp; + DIR *dirp; + int err; + char buf[MAXPATHLEN]; + + /* + * The readdirfunc declaration can't be prototyped, because it is + * assigned, below, to two functions which are prototyped in glob.h + * and dirent.h as taking pointers to differently typed opaque + * structures. + */ + struct dirent *(*readdirfunc)(); + + *pathend = EOS; + errno = 0; + + if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { + /* TODO: don't call for ENOENT or ENOTDIR? */ + if (pglob->gl_errfunc) { + g_Ctoc(pathbuf, buf); + if (pglob->gl_errfunc(buf, errno) || + pglob->gl_flags & GLOB_ERR) + return (GLOB_ABORTED); + } + return(0); + } + + err = 0; + + /* Search directory for matching names. */ + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + readdirfunc = pglob->gl_readdir; + else + readdirfunc = readdir; + while ((dp = (*readdirfunc)(dirp))) { + register u_char *sc; + register Char *dc; + + /* Initial DOT must be matched literally. */ + if (dp->d_name[0] == DOT && *pattern != DOT) + continue; + for (sc = (u_char *) dp->d_name, dc = pathend; + (*dc++ = *sc++) != EOS;) + continue; + if (!match(pathend, pattern, restpattern)) { + *pathend = EOS; + continue; + } + err = glob2(pathbuf, --dc, restpattern, pglob); + if (err) + break; + } + + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + (*pglob->gl_closedir)(dirp); + else + closedir(dirp); + return(err); +} + + +/* + * Extend the gl_pathv member of a glob_t structure to accomodate a new item, + * add the new item, and update gl_pathc. + * + * This assumes the BSD realloc, which only copies the block when its size + * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic + * behavior. + * + * Return 0 if new item added, error code if memory couldn't be allocated. + * + * Invariant of the glob_t structure: + * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and + * gl_pathv points to (gl_offs + gl_pathc + 1) items. + */ +static int +globextend(path, pglob) + const Char *path; + glob_t *pglob; +{ + register char **pathv; + register int i; + u_int newsize; + char *copy; + const Char *p; + + newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs); + pathv = pglob->gl_pathv ? + realloc((char *)pglob->gl_pathv, newsize) : + malloc(newsize); + if (pathv == NULL) { + if (pglob->gl_pathv) + free(pglob->gl_pathv); + return(GLOB_NOSPACE); + } + + if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + pathv += pglob->gl_offs; + for (i = pglob->gl_offs; --i >= 0; ) + *--pathv = NULL; + } + pglob->gl_pathv = pathv; + + for (p = path; *p++;) + continue; + if ((copy = malloc(p - path)) != NULL) { + g_Ctoc(path, copy); + pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; + } + pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + return(copy == NULL ? GLOB_NOSPACE : 0); +} + + +/* + * pattern matching function for filenames. Each occurrence of the * + * pattern causes a recursion level. + */ +static int +match(name, pat, patend) + register Char *name, *pat, *patend; +{ + int ok, negate_range; + Char c, k; + + while (pat < patend) { + c = *pat++; + switch (c & M_MASK) { + case M_ALL: + if (pat == patend) + return(1); + do + if (match(name, pat, patend)) + return(1); + while (*name++ != EOS); + return(0); + case M_ONE: + if (*name++ == EOS) + return(0); + break; + case M_SET: + ok = 0; + if ((k = *name++) == EOS) + return(0); + if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) + ++pat; + while (((c = *pat++) & M_MASK) != M_END) + if ((*pat & M_MASK) == M_RNG) { + if (c <= k && k <= pat[1]) + ok = 1; + pat += 2; + } else if (c == k) + ok = 1; + if (ok == negate_range) + return(0); + break; + default: + if (*name++ != c) + return(0); + break; + } + } + return(*name == EOS); +} + +/* Free allocated data belonging to a glob_t structure. */ +void +openbsd_globfree(pglob) + glob_t *pglob; +{ + register int i; + register char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) + if (*pp) + free(*pp); + free(pglob->gl_pathv); + } +} + +static DIR * +g_opendir(str, pglob) + register Char *str; + glob_t *pglob; +{ + char buf[MAXPATHLEN]; + + if (!*str) + strcpy(buf, "."); + else + g_Ctoc(str, buf); + + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_opendir)(buf)); + + return(opendir(buf)); +} + +static int +g_lstat(fn, sb, pglob) + register Char *fn; + struct stat *sb; + glob_t *pglob; +{ + char buf[MAXPATHLEN]; + + g_Ctoc(fn, buf); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_lstat)(buf, sb)); + return(lstat(buf, sb)); +} + +static int +g_stat(fn, sb, pglob) + register Char *fn; + struct stat *sb; + glob_t *pglob; +{ + char buf[MAXPATHLEN]; + + g_Ctoc(fn, buf); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_stat)(buf, sb)); + return(stat(buf, sb)); +} + +static Char * +g_strchr(str, ch) + Char *str; + int ch; +{ + do { + if (*str == ch) + return (str); + } while (*str++); + return (NULL); +} + +#ifdef notdef +static Char * +g_strcat(dst, src) + Char *dst; + const Char* src; +{ + Char *sdst = dst; + + while (*dst++) + continue; + --dst; + while((*dst++ = *src++) != EOS) + continue; + + return (sdst); +} +#endif + +static void +g_Ctoc(str, buf) + register const Char *str; + char *buf; +{ + register char *dc; + + for (dc = buf; (*dc++ = *str++) != EOS;) + continue; +} + +#ifdef DEBUG +static void +qprintf(str, s) + const char *str; + register Char *s; +{ + register Char *p; + + (void)printf("%s:\n", str); + for (p = s; *p; p++) + (void)printf("%c", CHAR(*p)); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", *p & M_PROTECT ? '"' : ' '); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", ismeta(*p) ? '_' : ' '); + (void)printf("\n"); +} +#endif diff --git a/compat/inet_aton.c b/compat/inet_aton.c new file mode 100644 index 0000000..a935d5a --- /dev/null +++ b/compat/inet_aton.c @@ -0,0 +1,27 @@ +/* +** Copyright 2002 University of Illinois Board of Trustees +** Copyright 2002 Mark D. Roth +** All rights reserved. +** +** inet_aton.c - inet_aton() function for compatibility library +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include +#include + + +int +inet_aton(const char *cp, struct in_addr *inp) +{ + inp->s_addr = inet_addr(cp); + if (inp->s_addr == -1) + return 0; + return 1; +} + + diff --git a/compat/module.ac b/compat/module.ac new file mode 100644 index 0000000..a76cf1c --- /dev/null +++ b/compat/module.ac @@ -0,0 +1,591 @@ +# COMPAT_VAR___PROGNAME +# --------------------- +# Check if libc defines the __progname variable. +AC_DEFUN([COMPAT_VAR___PROGNAME], [ + AC_CACHE_CHECK([if libc defines __progname], + [ac_cv_libc_defines___progname], + [AC_TRY_LINK([], + [ + extern char *__progname; + printf("%s", __progname); + ], + [ac_cv_libc_defines___progname=yes], + [ac_cv_libc_defines___progname=no] + )] + ) + if test "$ac_cv_libc_defines___progname" = "yes"; then + AC_DEFINE([HAVE___PROGNAME], 1, + [Define if libc defines the __progname variable]) + fi +]) + + +# COMPAT_FUNC_BASENAME +# -------------------- +# Check for working basename() function. +AC_DEFUN([COMPAT_FUNC_BASENAME], [ + AC_DEFINE([NEED_BASENAME], 1, + [Define if you want to use the basename function]) + AC_CHECK_HEADERS([libgen.h]) + AC_CACHE_CHECK([for working basename], + [compat_cv_func_basename_works], + [AC_TRY_RUN([ +#include +#ifdef HAVE_LIBGEN_H +# include +#endif + +typedef struct { + char *test; + char *result; +} test_t; + +const test_t tests[] = { + { "/usr/local/foo", "foo" }, + { "/usr/local/foo/", "foo" }, + { NULL, NULL } +}; + +int main() { + char test1[1024]; + int i; + + for (i = 0; tests[i].test; i++) { + strcpy(test1, tests[i].test); + if (strcmp(basename(test1), tests[i].result) || + strcmp(test1, tests[i].test)) + exit(1); + } + + exit(0); +} +], + [compat_cv_func_basename_works=yes], + [compat_cv_func_basename_works=no], + [compat_cv_func_basename_works=no] + )] + ) + if test "$compat_cv_func_basename_works" = "yes"; then + AC_DEFINE([HAVE_BASENAME], 1, + [Define if your system has a working basename]) + else + AC_LIBOBJ([basename]) + fi +]) + + +# COMPAT_FUNC_DIRNAME +# ------------------- +# Check for working dirname() function. +AC_DEFUN([COMPAT_FUNC_DIRNAME], [ + AC_DEFINE([NEED_DIRNAME], 1, + [Define if you want to use the dirname function]) + AC_CHECK_HEADERS([libgen.h]) + AC_CACHE_CHECK([for working dirname], + [compat_cv_func_dirname_works], + [AC_TRY_RUN([ +#include +#ifdef HAVE_LIBGEN_H +# include +#endif + +typedef struct { + char *test; + char *result; +} test_t; + +const test_t tests[] = { + { "foobar", "." }, + { "/usr/local/foo", "/usr/local" }, + { "/usr/local/foo/", "/usr/local" }, + { "/", "/" }, + { "", "." }, + { NULL, NULL } +}; + +int main() { + char test1[1024]; + int i; + + for (i = 0; tests[i].test; i++) { + strcpy(test1, tests[i].test); + if (strcmp(dirname(test1), tests[i].result) || + strcmp(test1, tests[i].test)) + exit(1); + } + + exit(0); +} +], + [compat_cv_func_dirname_works=yes], + [compat_cv_func_dirname_works=no], + [compat_cv_func_dirname_works=no] + )] + ) + if test "$compat_cv_func_dirname_works" = "yes"; then + AC_DEFINE([HAVE_DIRNAME], 1, + [Define if your system has a working dirname]) + else + AC_LIBOBJ([dirname]) + fi +]) + + +# COMPAT_FUNC_FNMATCH +# ------------------- +# Check for working fnmatch() function. +AC_DEFUN([COMPAT_FUNC_FNMATCH], [ + AC_DEFINE([NEED_FNMATCH], 1, [Define if you want to use the fnmatch function]) + AC_CHECK_HEADERS([fnmatch.h]) + if test "$ac_cv_header_fnmatch_h" = "yes"; then + AC_FUNC_FNMATCH + fi + if test "$ac_cv_func_fnmatch_works" != "yes"; then + AC_CHECK_HEADERS([ctype.h]) + AC_LIBOBJ([fnmatch]) + fi +]) + + +# COMPAT_FUNC_GLOB +# ---------------- +# Check for working glob() function. +AC_DEFUN([COMPAT_FUNC_GLOB], [ + AC_DEFINE([NEED_GLOB], 1, [Define if you want to use the glob function]) + AC_CHECK_HEADERS([glob.h]) + AC_CACHE_CHECK([for working glob], + [compat_cv_func_glob_works], + [AC_TRY_RUN([ +#include +#ifdef HAVE_GLOB_H +# include +#endif + +#ifndef GLOB_ABORTED +# define GLOB_ABORTED GLOB_ABEND +#endif + +int main() { + glob_t g; + int status; + + status = glob("conf*", 0, NULL, &g); + switch (status) { + case 0: + case GLOB_NOSPACE: + case GLOB_ABORTED: + case GLOB_NOMATCH: + exit(0); + break; + default: + exit(1); + break; + } +} +], + [compat_cv_func_glob_works=yes], + [compat_cv_func_glob_works=no], + [compat_cv_func_glob_works=no] + )] + ) + if test "$compat_cv_func_glob_works" = "yes"; then + AC_DEFINE([HAVE_GLOB], 1, [Define if your system has a working glob]) + else + AC_LIBOBJ([glob]) + AC_CHECK_FUNCS([issetugid]) + fi +]) + + +# COMPAT_FUNC_MAKEDEV +# ------------------- +# Check for number of arguments expected by makedev(). +AC_DEFUN([COMPAT_FUNC_MAKEDEV], [ + AC_REQUIRE([AC_HEADER_MAJOR]) + AC_DEFINE([NEED_MAKEDEV], 1, + [Define if you want to use the makedev function]) + AC_CACHE_CHECK([whether makedev expects three arguments], + [compat_cv_func_makedev_three_args], + [AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#include +#ifdef MAJOR_IN_MKDEV +# include +#else +# ifdef MAJOR_IN_SYSMACROS +# include +# endif +#endif +]], [[ +dev_t dev; +major_t maj = 5; +minor_t min = 7; + +dev = makedev(0, maj, min); +if (major(dev) != maj + || minor(dev) != min) + exit(1); +exit(0); +]])], + [compat_cv_func_makedev_three_args=yes], + [compat_cv_func_makedev_three_args=no] + )] + ) + if test "$compat_cv_func_makedev_three_args" = "yes"; then + AC_DEFINE([MAKEDEV_THREE_ARGS], 1, + [Define as 1 if makedev expects three arguments]) + fi +]) + + +# COMPAT_FUNC_SNPRINTF +# -------------------- +# Check for working snprintf() function. +AC_DEFUN([COMPAT_FUNC_SNPRINTF], [ + AC_DEFINE([NEED_SNPRINTF], 1, + [Define if you want to use the snprintf function]) + AC_CACHE_CHECK([for working snprintf], + [compat_cv_func_snprintf_works], + [AC_TRY_RUN([ +#include + +typedef struct { + int length; + char *test; + int retval; + char *result; +} test_t; + +const test_t tests[] = { + { 10, "12345678901234567890", 20, "123456789" }, +#if 0 + { 0, "12345678901234567890", 20, NULL }, + { -1, "12345678901234567890", -1, NULL }, +#endif + { 0, NULL, 0, NULL } +}; + +int main() { + char test1[1024]; + int i; + + for (i = 0; tests[i].test; i++) { + memset(test1, 'X', sizeof(test1)); + if ((snprintf(test1, tests[i].length, "%s", tests[i].test) + != tests[i].retval) || + (tests[i].result && strcmp(tests[i].result, test1))) + exit(1); + } + + exit(0); +} +], + [compat_cv_func_snprintf_works=yes], + [compat_cv_func_snprintf_works=no], + [compat_cv_func_snprintf_works=no] + )] + ) + if test "$compat_cv_func_snprintf_works" = "yes"; then + AC_DEFINE([HAVE_SNPRINTF], 1, + [Define if your system has a working snprintf]) + else + AC_LIBOBJ([snprintf]) + fi +]) + + +# COMPAT_PROTO_MACRO(FUNCTION, HEADER, MACRO-LIST, [BODY]) +# -------------------------------------------------------- +# Determine which C preprocessor macro is needed to expose prototype of +# FUNCTION in HEADER. First, we try with nothing special defined; then we +# try with each macro from MACRO-LIST. We stop as soon as it's found +# and adjust $CFLAGS appropriately. +AC_DEFUN([COMPAT_PROTO_MACRO], + [AC_CACHE_CHECK([what to define for $1 prototype], + [compat_cv_proto_]$1[_macro], + [AC_TRY_COMPILE( + [ + #include <$2> + ], + [ + void *funcptr; + $4 + funcptr = $1; + ], + [compat_cv_proto_]$1[_macro="none"], + [for macro in $3; do + AC_TRY_COMPILE( + [ + #define $macro + #include <$2> + ], + [ + void *funcptr; + $4 + funcptr = $1; + ], + [ + compat_cv_proto_]$1[_macro="$macro" + break + ], + [compat_cv_proto_]$1[_macro="not found"] + ) + done] + )] + )] + if test -n "$compat_cv_proto_$1_macro" -a "$compat_cv_proto_$1_macro" != "not found" -a "$compat_cv_proto_$1_macro" != "none"; then + CFLAGS="${CFLAGS} -D$compat_cv_proto_$1_macro"; + fi +) + + +# COMPAT_FUNC_STRTOK_R +# -------------------- +# Check for working strtok_r(). +AC_DEFUN([COMPAT_FUNC_STRTOK_R], [ + AC_DEFINE([NEED_STRTOK_R], 1, + [Define if you want to use the strtok_r function]) + AC_REPLACE_FUNCS([strtok_r]) + COMPAT_PROTO_MACRO([strtok_r], [string.h], [_REENTRANT _THREAD_SAFE]) +]) + + +# COMPAT_FUNC_GETPWUID_R +# ---------------------- +# Check for POSIX-compliant getpwuid_r(). +AC_DEFUN([COMPAT_FUNC_GETPWUID_R], [ + COMPAT_PROTO_MACRO([getpwuid_r], [pwd.h], + [_POSIX_PTHREAD_SEMANTICS _REENTRANT], + [ + struct passwd pwd, *pwdp; + char buf[10240]; + getpwuid_r(0, &pwd, buf, sizeof(buf), &pwdp); + ] + ) + if test "$compat_cv_proto_getpwuid_r_macro" != "not found"; then + AC_DEFINE([HAVE_GETPWUID_R], 1, + [Define if your system has a POSIX-compliant getpwuid_r]) + else + AC_MSG_WARN([cannot find usable getpwuid_r - resulting libraries will not be thread-safe]) + fi +]) + + +# COMPAT_FUNC_GETHOSTBYNAME_R +# --------------------------- +# Check for gethostbyname_r(). +AC_DEFUN([COMPAT_FUNC_GETHOSTBYNAME_R], [ + AC_REQUIRE([AC_TYPE_SIZE_T]) + AC_DEFINE([NEED_GETHOSTBYNAME_R], 1, + [Define if you want to use the gethostbyname_r function]) + AC_SEARCH_LIBS([gethostbyname_r], [nsl]) + if test "$ac_cv_search_gethostbyname_r" != "no"; then + COMPAT_PROTO_MACRO([gethostbyname_r], [netdb.h], [_REENTRANT]) + AC_CACHE_CHECK( + [for number of arguments to gethostbyname_r], + [compat_cv_gethostbyname_r_args], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct hostent hent; + char buf[10240]; + int herr; + + gethostbyname_r("localhost", &hent, buf, sizeof(buf), &herr); + ], + [compat_cv_gethostbyname_r_args=5], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct hostent hent, *hp; + char buf[10240]; + int herr; + + gethostbyname_r("localhost", &hent, buf, sizeof(buf), &hp, &herr); + ], + [compat_cv_gethostbyname_r_args=6], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct hostent hent; + struct hostent_data hdata; + + gethostbyname_r("localhost", &hent, &hdata); + ], + [compat_cv_gethostbyname_r_args=3], + [compat_cv_gethostbyname_r_args=no] + )] + )] + )] + ) + if test "$compat_cv_gethostbyname_r_args" != "no"; then + AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, + [Define if you have the gethostbyname_r function]) + AC_DEFINE_UNQUOTED([GETHOSTBYNAME_R_NUM_ARGS], + [$compat_cv_gethostbyname_r_args], + [Define to number of arguments for gethostbyname_r]) + if test "$compat_cv_gethostbyname_r_args" != "6"; then + AC_LIBOBJ([gethostbyname_r]) + fi + else + AC_MSG_WARN([unknown form of gethostbyname_r - resulting libraries will not be thread-safe]) + fi + else + AC_MSG_WARN([cannot find gethostbyname_r - resulting libraries will not be thread-safe]) + fi +]) + + +# COMPAT_FUNC_GETSERVBYNAME_R +# --------------------------- +# Check for getservbyname_r(). +AC_DEFUN([COMPAT_FUNC_GETSERVBYNAME_R], [ + AC_REQUIRE([AC_TYPE_SIZE_T]) + AC_DEFINE([NEED_GETSERVBYNAME_R], 1, + [Define if you want to use the getservbyname_r function]) + AC_SEARCH_LIBS([getservbyname_r], [socket nsl]) + if test "$ac_cv_search_getservbyname_r" != "no"; then + COMPAT_PROTO_MACRO([getservbyname_r], [netdb.h], [_REENTRANT]) + AC_CACHE_CHECK( + [for number of arguments to getservbyname_r], + [compat_cv_getservbyname_r_args], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct servent sent; + char buf[10240]; + + getservbyname_r("telnet", "tcp", &sent, buf, sizeof(buf)); + ], + [compat_cv_getservbyname_r_args=5], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct servent sent, *sp; + char buf[10240]; + + getservbyname_r("telnet", "tcp", &sent, buf, sizeof(buf), &sp); + ], + [compat_cv_getservbyname_r_args=6], + [AC_TRY_COMPILE( + [ + #include + ], + [ + struct servent sent; + struct servent_data sdata; + + getservbyname_r("telnet", "tcp", &sent, &sdata); + ], + [compat_cv_getservbyname_r_args=4], + [compat_cv_getservbyname_r_args=no] + )] + )] + )] + ) + if test "$compat_cv_getservbyname_r_args" != "no"; then + AC_DEFINE([HAVE_GETSERVBYNAME_R], 1, + [Define if you have the getservbyname_r function]) + AC_DEFINE_UNQUOTED([GETSERVBYNAME_R_NUM_ARGS], + [$compat_cv_getservbyname_r_args], + [Define to number of arguments for getservbyname_r]) + if test "$compat_cv_getservbyname_r_args" != "6"; then + AC_LIBOBJ([getservbyname_r]) + fi + else + AC_MSG_WARN([unknown form of getservbyname_r - resulting libraries will not be thread-safe]) + fi + else + AC_MSG_WARN([cannot find getservbyname_r - resulting libraries will not be thread-safe]) + fi +]) + + +# COMPAT_REPLACE_FUNC(function) +# ----------------------------- +# Replacement for AC_REPLACE_FUNCS. +AC_DEFUN([COMPAT_REPLACE_FUNC], [ + AC_DEFINE([NEED_]translit($1,[a-z],[A-Z]), 1, + [Define if you want to use the ]$1[ function]) + AC_CHECK_FUNC($1, + [AC_DEFINE([HAVE_]translit($1,[a-z],[A-Z]), 1, + [Define if you have the ]$1[ function])], + [AC_LIBOBJ(]$1[)] + ) +]) + + +# COMPAT_FUNC_GETHOSTNAME +# ----------------------- +# Check for gethostname(). +AC_DEFUN([COMPAT_FUNC_GETHOSTNAME], [ + COMPAT_REPLACE_FUNC([gethostname]) +]) + + +# COMPAT_FUNC_INET_ATON +# --------------------- +# Check for inet_aton(). +AC_DEFUN([COMPAT_FUNC_INET_ATON], [ + COMPAT_REPLACE_FUNC([inet_aton]) +]) + + +# COMPAT_FUNC_STRDUP +# ------------------ +# Check for strdup(). +AC_DEFUN([COMPAT_FUNC_STRDUP], [ + COMPAT_REPLACE_FUNC([strdup]) +]) + + +# COMPAT_FUNC_STRLCAT +# ------------------- +# Check for strlcat(). +AC_DEFUN([COMPAT_FUNC_STRLCAT], [ + COMPAT_REPLACE_FUNC([strlcat]) +]) + + +# COMPAT_FUNC_STRLCPY +# ------------------- +# Check for strlcpy(). +AC_DEFUN([COMPAT_FUNC_STRLCPY], [ + COMPAT_REPLACE_FUNC([strlcpy]) +]) + + +# COMPAT_FUNC_STRMODE +# ------------------- +# Check for strmode(). +AC_DEFUN([COMPAT_FUNC_STRMODE], [ + COMPAT_REPLACE_FUNC([strmode]) +]) + + +# COMPAT_FUNC_STRRSTR +# ------------------- +# Check for strrstr(). +AC_DEFUN([COMPAT_FUNC_STRRSTR], [ + COMPAT_REPLACE_FUNC([strrstr]) +]) + + +# COMPAT_FUNC_STRSEP +# ------------------ +# Check for strsep(). +AC_DEFUN([COMPAT_FUNC_STRSEP], [ + COMPAT_REPLACE_FUNC([strsep]) +]) + + diff --git a/compat/snprintf.c b/compat/snprintf.c new file mode 100644 index 0000000..8c228d4 --- /dev/null +++ b/compat/snprintf.c @@ -0,0 +1,761 @@ +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Chris Frey 2012/09/05 + * Removed varargs.h and all dependency on it. + * + **************************************************************/ + +#include + +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) + +#include +# include +#include + +#include +#define VA_LOCAL_DECL va_list ap +#define VA_START(f) va_start(ap, f) +#define VA_SHIFT(v,t) ; /* no-op for ANSI */ +#define VA_END va_end(ap) + +/*int snprintf (char *str, size_t count, const char *fmt, ...);*/ +/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/ + +static void dopr (char *buffer, size_t maxlen, const char *format, + va_list args); +static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static void fmtint (char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags); +static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, + long double fvalue, int min, int max, int flags); +static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 + +#define char_to_int(p) (p - '0') +#define MAX(p,q) ((p >= q) ? p : q) + +static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) +{ + char ch; + long value; + long double fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) + { + if ((ch == '\0') || (currlen >= maxlen)) + state = DP_S_DONE; + + switch(state) + { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) + { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) + { + min = 10*min + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } + else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') + { + state = DP_S_MAX; + ch = *format++; + } + else + state = DP_S_MOD; + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) + { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } + else + state = DP_S_MOD; + break; + case DP_S_MOD: + /* Currently, we don't support Long Long, bummer */ + switch (ch) + { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) + { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else + value = va_arg (args, int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else + value = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, long double); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, long double); + else + fvalue = va_arg (args, double); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, long double); + else + fvalue = va_arg (args, double); + break; + case 'c': + dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + if (max < 0) + max = maxlen; /* ie, no max */ + fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) + { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } + else if (cflags == DP_C_LONG) + { + long int *num; + num = va_arg (args, long int *); + *num = currlen; + } + else + { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else + buffer[maxlen - 1] = '\0'; +} + +static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + + if (value == 0) + { + value = ""; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while ((padlen > 0) && (cnt < max)) + { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) + { + dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while ((padlen < 0) && (cnt < max)) + { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + ++cnt; + } +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static void fmtint (char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) + { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + } + + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < 20)); + if (place == 20) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) + { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place)); +#endif + + /* Spaces */ + while (spadlen > 0) + { + dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) + { + while (zpadlen > 0) + { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } +} + +static long double abs_val (long double value) +{ + long double result = value; + + if (value < 0) + result = -value; + + return result; +} + +static long double pow10_ (int exp) +{ + long double result = 1; + + while (exp) + { + result *= 10; + exp--; + } + + return result; +} + +static long round_ (long double value) +{ + long intpart; + + intpart = value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} + +static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, + long double fvalue, int min, int max, int flags) +{ + int signvalue = 0; + long double ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + long intpart; + long fracpart; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) + signvalue = '-'; + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + + intpart = ufvalue; + + /* + * Sorry, we only support 9 digits past the decimal because of our + * conversion method + */ + if (max > 9) + max = 9; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + fracpart = round_ ((pow10_ (max)) * (ufvalue - intpart)); + + if (fracpart >= pow10_ (max)) + { + intpart++; + fracpart -= pow10_ (max); + } + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); +#endif + + /* Convert integer part */ + do { + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while(intpart && (iplace < 20)); + if (iplace == 20) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + do { + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while(fracpart && (fplace < 20)); + if (fplace == 20) fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) + { + if (signvalue) + { + dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) + { + dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) + { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + dopr_outch (buffer, currlen, maxlen, '.'); + + while (fplace > 0) + dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + + while (zpadlen > 0) + { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (padlen < 0) + { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } +} + +static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen < maxlen) + buffer[(*currlen)++] = c; +} +#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ + +#ifndef HAVE_VSNPRINTF +int mutt_vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + str[0] = 0; + dopr(str, count, fmt, args); + return(strlen(str)); +} +#endif /* !HAVE_VSNPRINTF */ + +#ifndef HAVE_SNPRINTF +int mutt_snprintf (char *str,size_t count,const char *fmt,...) +{ + VA_LOCAL_DECL; + + VA_START (fmt); + VA_SHIFT (str, char *); + VA_SHIFT (count, size_t ); + VA_SHIFT (fmt, char *); + (void) mutt_vsnprintf(str, count, fmt, ap); + VA_END; + return(strlen(str)); +} + +#ifdef TEST_SNPRINTF +#ifndef LONG_STRING +#define LONG_STRING 1024 +#endif +int main (void) +{ + char buf1[LONG_STRING]; + char buf2[LONG_STRING]; + char *fp_fmt[] = { + "%-1.5f", + "%1.5f", + "%123.9f", + "%10.5f", + "% 10.5f", + "%+22.9f", + "%+4.9f", + "%01.3f", + "%4f", + "%3.1f", + "%3.2f", + NULL + }; + double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, + 0.9996, 1.996, 4.136, 0}; + char *int_fmt[] = { + "%-1.5d", + "%1.5d", + "%123.9d", + "%5.5d", + "%10.5d", + "% 10.5d", + "%+22.33d", + "%01.3d", + "%4d", + NULL + }; + long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + int x, y; + int fail = 0; + int num = 0; + + printf ("Testing snprintf format codes against system sprintf...\n"); + + for (x = 0; fp_fmt[x] != NULL ; x++) + for (y = 0; fp_nums[y] != 0 ; y++) + { + mutt_snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); + sprintf (buf2, fp_fmt[x], fp_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + fp_fmt[x], buf1, buf2); + fail++; + } + num++; + } + + for (x = 0; int_fmt[x] != NULL ; x++) + for (y = 0; int_nums[y] != 0 ; y++) + { + mutt_snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); + sprintf (buf2, int_fmt[x], int_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + int_fmt[x], buf1, buf2); + fail++; + } + num++; + } + printf ("%d tests failed out of %d.\n", fail, num); +} +#endif /* SNPRINTF_TEST */ + +#endif /* !HAVE_SNPRINTF */ diff --git a/compat/strdup.c b/compat/strdup.c new file mode 100644 index 0000000..75e9af5 --- /dev/null +++ b/compat/strdup.c @@ -0,0 +1,62 @@ +/* $OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93"; +#else +static char *rcsid = "$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +char * +openbsd_strdup(str) + const char *str; +{ + size_t siz; + char *copy; + + siz = strlen(str) + 1; + if ((copy = malloc(siz)) == NULL) + return(NULL); + (void)memcpy(copy, str, siz); + return(copy); +} diff --git a/compat/strlcat.c b/compat/strlcat.c new file mode 100644 index 0000000..3912539 --- /dev/null +++ b/compat/strlcat.c @@ -0,0 +1,72 @@ +/* $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(initial dst) + strlen(src); if retval >= siz, + * truncation occurred. + */ +size_t strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/compat/strlcpy.c b/compat/strlcpy.c new file mode 100644 index 0000000..300a28b --- /dev/null +++ b/compat/strlcpy.c @@ -0,0 +1,68 @@ +/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/compat/strmode.c b/compat/strmode.c new file mode 100644 index 0000000..5e7f15e --- /dev/null +++ b/compat/strmode.c @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strmode.c,v 1.3 1997/06/13 13:57:20 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +void +strmode(mode, p) + register mode_t mode; + register char *p; +{ + /* print type */ + switch (mode & S_IFMT) { + case S_IFDIR: /* directory */ + *p++ = 'd'; + break; + case S_IFCHR: /* character special */ + *p++ = 'c'; + break; + case S_IFBLK: /* block special */ + *p++ = 'b'; + break; + case S_IFREG: /* regular */ + *p++ = '-'; + break; + case S_IFLNK: /* symbolic link */ + *p++ = 'l'; + break; + case S_IFSOCK: /* socket */ + *p++ = 's'; + break; +#ifdef S_IFIFO + case S_IFIFO: /* fifo */ + *p++ = 'p'; + break; +#endif +#ifdef S_IFWHT + case S_IFWHT: /* whiteout */ + *p++ = 'w'; + break; +#endif + default: /* unknown */ + *p++ = '?'; + break; + } + /* usr */ + if (mode & S_IRUSR) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWUSR) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXUSR | S_ISUID)) { + case 0: + *p++ = '-'; + break; + case S_IXUSR: + *p++ = 'x'; + break; + case S_ISUID: + *p++ = 'S'; + break; + case S_IXUSR | S_ISUID: + *p++ = 's'; + break; + } + /* group */ + if (mode & S_IRGRP) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWGRP) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXGRP | S_ISGID)) { + case 0: + *p++ = '-'; + break; + case S_IXGRP: + *p++ = 'x'; + break; + case S_ISGID: + *p++ = 'S'; + break; + case S_IXGRP | S_ISGID: + *p++ = 's'; + break; + } + /* other */ + if (mode & S_IROTH) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWOTH) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXOTH | S_ISVTX)) { + case 0: + *p++ = '-'; + break; + case S_IXOTH: + *p++ = 'x'; + break; + case S_ISVTX: + *p++ = 'T'; + break; + case S_IXOTH | S_ISVTX: + *p++ = 't'; + break; + } + *p++ = ' '; /* will be a '+' if ACL's implemented */ + *p = '\0'; +} diff --git a/compat/strrstr.c b/compat/strrstr.c new file mode 100644 index 0000000..097ef91 --- /dev/null +++ b/compat/strrstr.c @@ -0,0 +1,40 @@ +/* +** Copyright 1998-2002 University of Illinois Board of Trustees +** Copyright 1998-2002 Mark D. Roth +** All rights reserved. +** +** strrstr.c - strrstr() function for compatibility library +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include + +#include + + +/* +** find the last occurrance of find in string +*/ +char * +strrstr(char *string, char *find) +{ + size_t stringlen, findlen; + char *cp; + + findlen = strlen(find); + stringlen = strlen(string); + if (findlen > stringlen) + return NULL; + + for (cp = string + stringlen - findlen; cp >= string; cp--) + if (strncmp(cp, find, findlen) == 0) + return cp; + + return NULL; +} + + diff --git a/compat/strsep.c b/compat/strsep.c new file mode 100644 index 0000000..3132962 --- /dev/null +++ b/compat/strsep.c @@ -0,0 +1,87 @@ +/* $OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)strsep.c 8.1 (Berkeley) 6/4/93"; +#else +static char *rcsid = "$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +#ifndef HAVE_STRSEP +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(stringp, delim) + register char **stringp; + register const char *delim; +{ + register char *s; + register const char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} +#endif /* ! HAVE_STRSEP */ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..4623100 --- /dev/null +++ b/configure.ac @@ -0,0 +1,125 @@ +dnl ### Normal initialization. ###################################### +AC_INIT([libtar], [1.2.20]) +AC_PREREQ([2.61]) +AC_CONFIG_AUX_DIR([autoconf]) +AC_CONFIG_HEADERS([config.h]) +AC_COPYRIGHT([[ +Copyright (c) 1998-2003 University of Illinois Board of Trustees +Copyright (c) 1998-2003 Mark D. Roth +All rights reserved. +]]) +AC_CONFIG_SRCDIR([lib/libtar.h]) +AC_CONFIG_MACRO_DIR([autoconf]) +AM_INIT_AUTOMAKE([dist-bzip2]) +ENCAP_PKG([], [postinstall-encap]) + + +dnl ### Load subdirectory modules. ################################## +PSG_MODULE([compat]) +PSG_MODULE([listhash], [libtar]) + + +dnl ### Set some option defaults. ################################### +if test -z "$CFLAGS"; then + CFLAGS="-O" +fi +MKDIR="mkdir -p -m 755" +AC_SUBST([MKDIR]) + + +dnl ### Check for compiler et al. ################################### +AC_USE_SYSTEM_EXTENSIONS +AC_PROG_CC +AC_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET + + +dnl ### Compiler characteristics. ################################## +AC_C_CONST +AX_TLS +if test "$ac_cv_tls" = "none" ; then +AC_MSG_NOTICE([ +**************************************************************** +WARNING: Your compiler does not support TLS __thread keywords, +so your version of libtar will not be thread-safe. +**************************************************************** +]) +fi + + +dnl ### Checks for header files. ################################### +AC_HEADER_STDC +AC_CHECK_HEADERS([unistd.h]) +AC_HEADER_MAJOR +PSG_REPLACE_TYPE([major_t], [unsigned int], [ + #include + #ifdef MAJOR_IN_MKDEV + # include + #else + # ifdef MAJOR_IN_SYSMACROS + # include + # endif + #endif +]) +PSG_REPLACE_TYPE([minor_t], [unsigned int], [ + #include + #ifdef MAJOR_IN_MKDEV + # include + #else + # ifdef MAJOR_IN_SYSMACROS + # include + # endif + #endif +]) +PSG_REPLACE_TYPE([dev_t], [unsigned long], [ + #include + #ifdef MAJOR_IN_MKDEV + # include + #else + # ifdef MAJOR_IN_SYSMACROS + # include + # endif + #endif +]) +PSG_REPLACE_TYPE([socklen_t], [unsigned long], [ + #include + #include +]) +PSG_REPLACE_TYPE([uint64_t], [long long]) +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_UID_T +PSG_REPLACE_TYPE([nlink_t], [unsigned short]) + + +dnl ### Check for needed functions. ################################ +COMPAT_FUNC_BASENAME +COMPAT_FUNC_DIRNAME +COMPAT_FUNC_FNMATCH +AC_CHECK_FUNCS([lchown]) +COMPAT_FUNC_MAKEDEV +COMPAT_FUNC_SNPRINTF +COMPAT_FUNC_STRDUP +AC_FUNC_STRFTIME +COMPAT_FUNC_STRLCPY +COMPAT_FUNC_STRMODE +COMPAT_FUNC_STRSEP + + +dnl ### Check for libraries. ####################################### +AC_ARG_WITH([zlib], + [ --without-zlib Use external gzip binary instead of zlib], + [], + [with_zlib=yes]) +if test "$with_zlib" = "yes"; then + AC_CHECK_LIB([z], [gzread]) +fi + + +dnl ### Create output files. ####################################### +AC_CONFIG_FILES([Makefile lib/Makefile libtar/Makefile doc/Makefile]) +AC_OUTPUT + diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..0d82a48 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,153 @@ +# @configure_input@ + +### Path settings +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ +libdir = @libdir@ +includedir = @includedir@ +datarootdir = @datarootdir@ + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +@ENCAP_DEFS@ + +### Installation programs and flags +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s +INSTALL_DATA = @INSTALL_DATA@ +LN_S = @LN_S@ +MKDIR = @MKDIR@ +@SET_MAKE@ + + +### Makefile rules - no user-servicable parts below + +TAR_OPEN_SO = tar_fdopen \ + tar_fd \ + tar_close +TAR_APPEND_FILE_SO = tar_append_eof \ + tar_append_regfile +TAR_BLOCK_READ_SO = tar_block_write +TH_READ_SO = th_write +TH_SET_FROM_STAT_SO = th_finish \ + th_set_device \ + th_set_group \ + th_set_link \ + th_set_mode \ + th_set_path \ + th_set_type \ + th_set_user +TAR_EXTRACT_FILE_SO = tar_extract_blockdev \ + tar_extract_chardev \ + tar_extract_dir \ + tar_extract_fifo \ + tar_extract_hardlink \ + tar_extract_regfile \ + tar_extract_symlink \ + tar_skip_regfile \ + tar_set_file_perms +TH_GET_PATHNAME_SO = TH_ISBLK \ + TH_ISCHR \ + TH_ISDIR \ + TH_ISFIFO \ + TH_ISLNK \ + TH_ISLONGLINK \ + TH_ISLONGNAME \ + TH_ISREG \ + TH_ISSYM \ + th_get_crc \ + th_get_devmajor \ + th_get_devminor \ + th_get_gid \ + th_get_linkname \ + th_get_mode \ + th_get_mtime \ + th_get_size \ + th_get_uid +TH_PRINT_LONG_LS_SO = th_print +TAR_EXTRACT_ALL_SO = tar_extract_glob \ + tar_append_tree +@LISTHASH_PREFIX@_HASH_NEW_SO = \ + @LISTHASH_PREFIX@_hash_free \ + @LISTHASH_PREFIX@_hash_next \ + @LISTHASH_PREFIX@_hash_prev \ + @LISTHASH_PREFIX@_hash_search \ + @LISTHASH_PREFIX@_hash_add \ + @LISTHASH_PREFIX@_hash_getkey \ + @LISTHASH_PREFIX@_hash_del +@LISTHASH_PREFIX@_LIST_NEW_SO = \ + @LISTHASH_PREFIX@_list_free \ + @LISTHASH_PREFIX@_list_next \ + @LISTHASH_PREFIX@_list_prev \ + @LISTHASH_PREFIX@_list_search \ + @LISTHASH_PREFIX@_list_add \ + @LISTHASH_PREFIX@_list_add_str \ + @LISTHASH_PREFIX@_list_del \ + @LISTHASH_PREFIX@_list_dup \ + @LISTHASH_PREFIX@_list_merge + +DISTCLEANFILES = ../listhash/@LISTHASH_PREFIX@_hash_new.3 \ + ../listhash/@LISTHASH_PREFIX@_list_new.3 + + +all: + +.PHONY: clean distclean install + +clean: + +distclean: clean + rm -f Makefile ${DISTCLEANFILES} + +install: all + ${MKDIR} ${DESTDIR}${mandir}/man3 + ${INSTALL_DATA} ${srcdir}/tar_open.3 ${DESTDIR}${mandir}/man3 + for i in ${TAR_OPEN_SO}; do \ + echo ".so man3/tar_open.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/tar_append_file.3 ${DESTDIR}${mandir}/man3 + for i in ${TAR_APPEND_FILE_SO}; do \ + echo ".so man3/tar_append_file.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/tar_block_read.3 ${DESTDIR}${mandir}/man3 + for i in ${TAR_BLOCK_READ_SO}; do \ + echo ".so man3/tar_block_read.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/th_read.3 ${DESTDIR}${mandir}/man3 + for i in ${TH_READ_SO}; do \ + echo ".so man3/th_read.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/th_set_from_stat.3 ${DESTDIR}${mandir}/man3 + for i in ${TH_SET_FROM_STAT_SO}; do \ + echo ".so man3/th_set_from_stat.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/tar_extract_file.3 ${DESTDIR}${mandir}/man3 + for i in ${TAR_EXTRACT_FILE_SO}; do \ + echo ".so man3/tar_extract_file.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/th_get_pathname.3 ${DESTDIR}${mandir}/man3 + for i in ${TH_GET_PATHNAME_SO}; do \ + echo ".so man3/th_get_pathname.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/th_print_long_ls.3 ${DESTDIR}${mandir}/man3 + for i in ${TH_PRINT_LONG_LS_SO}; do \ + echo ".so man3/th_print_long_ls.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ${srcdir}/tar_extract_all.3 ${DESTDIR}${mandir}/man3 + for i in ${TAR_EXTRACT_ALL_SO}; do \ + echo ".so man3/tar_extract_all.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ../listhash/@LISTHASH_PREFIX@_hash_new.3 ${DESTDIR}${mandir}/man3 + for i in ${@LISTHASH_PREFIX@_HASH_NEW_SO}; do \ + echo ".so man3/@LISTHASH_PREFIX@_hash_new.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + ${INSTALL_DATA} ../listhash/@LISTHASH_PREFIX@_list_new.3 ${DESTDIR}${mandir}/man3 + for i in ${@LISTHASH_PREFIX@_LIST_NEW_SO}; do \ + echo ".so man3/@LISTHASH_PREFIX@_list_new.3" > ${DESTDIR}${mandir}/man3/$${i}.3; \ + done + diff --git a/doc/tar_append_file.3 b/doc/tar_append_file.3 new file mode 100644 index 0000000..8af5a4e --- /dev/null +++ b/doc/tar_append_file.3 @@ -0,0 +1,50 @@ +.TH tar_append_file 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +tar_append_file, tar_append_eof, tar_append_regfile \- append data to tar archives +.SH SYNOPSIS +.B #include +.P +.BI "int tar_append_file(TAR *" t ", char *" realname "," +.BI "char *" savename ");" + +.BI "int tar_append_regfile(TAR *" t ", char *" realname ");" + +.BI "int tar_append_eof(TAR *" t ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBtar_append_file\fP() function creates a tar file header block +describing the file named by the \fIrealname\fP argument, but with +the encoded filename of \fIsavename\fP. It then sets the current +header associated with the \fITAR\fP handle \fIt\fP to the newly created +header block, and writes this block to the tar archive associated with +\fIt\fP. If the file named by \fIrealname\fP is a regular file (and +is not encoded as a hard link), \fBtar_append_file\fP() will call +\fBtar_append_regfile\fP() to append the contents of the file. + +The \fBtar_append_regfile\fP() function appends the contents of a regular +file to the tar archive associated with \fIt\fP. Since this function is +called by \fBtar_append_file\fP(), it should only be necessary for +applications that construct and write the tar file header on their own. + +The \fBtar_append_eof\fP() function writes an EOF marker (two blocks of +all zeros) to the tar file associated with \fIt\fP. +.SH RETURN VALUES +On successful completion, these functions will return 0. On failure, +they will return \-1 and set \fIerrno\fP to an appropriate value. +.SH ERRORS +The \fBtar_append_*\fP() functions will fail if: +.IP \fBEINVAL\fP +Less than \fBT_BLOCKSIZE\fP bytes were written to the tar archive. +.IP \fBEINVAL\fP +Less than \fBT_BLOCKSIZE\fP bytes were read from the \fIrealname\fP file. +.PP +They may also fail if any of the following functions fail: \fBlstat\fP(), +\fBmalloc\fP(), \fBopen\fP(), \fBread\fP(), \fBth_write\fP(), or the +write function for the file type associated with the \fITAR\fP handle +\fIt\fP. +.SH SEE ALSO +.BR read (2), +.BR open (2), +.BR lstat (2), +.BR th_write (3) diff --git a/doc/tar_block_read.3 b/doc/tar_block_read.3 new file mode 100644 index 0000000..1443570 --- /dev/null +++ b/doc/tar_block_read.3 @@ -0,0 +1,24 @@ +.TH tar_block_read 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +tar_block_read, tar_block_write \- macros to call read and write functions +for the correct tar archive type +.SH SYNOPSIS +.B #include +.P +.BI "int tar_block_read(TAR *" t ", char *" buf ");" + +.BI "int tar_block_write(TAR *" t ", char *" buf ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBtar_block_read\fP() and \fBtar_block_write\fP() macros call +the read and write functions for the tar archive type associated with the +\fITAR\fP handle \fIt\fP. This type is set when the \fITAR\fP handle +is created using \fBtar_open\fP(). +.SH RETURN VALUE +These macros return the same values as the corresponding read and write +functions. +.SH SEE ALSO +.BR read (2), +.BR write (2), +.BR tar_open (3) diff --git a/doc/tar_extract_all.3 b/doc/tar_extract_all.3 new file mode 100644 index 0000000..b2b9383 --- /dev/null +++ b/doc/tar_extract_all.3 @@ -0,0 +1,43 @@ +.TH tar_extract_all 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +tar_extract_all, tar_extract_glob, tar_append_tree \- high-level tar +archive manipulation functions +.SH SYNOPSIS +.B #include +.P +.BI "int tar_extract_all(TAR *" t ", char *" prefix ");" + +.BI "int tar_extract_glob(TAR *" t ", char *" globname "," +.BI "char *" prefix ");" + +.BI "int tar_append_tree(TAR *" t ", char *" realdir "," +.BI "char *" savedir ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBtar_extract_all\fP() function extracts all files from the tar +archive associated with the \fITAR\fP handle \fIt\fP into the path +named by the \fIprefix\fP argument. + +The \fBtar_extract_glob\fP() function extracts all files matching +the given \fIglob\fP pattern from the tar archive associated with the +\fITAR\fP handle \fIt\fP into the path named by the \fIprefix\fP argument. + +The \fBtar_append_tree\fP() function appends all files from the +directory tree named by \fIrealdir\fP to the tar archive associated with +the \fITAR\fP handle \fIt\fP. The pathnames stored in the tar archive +are modified by replacing \fIrealdir\fP with \fIsavedir\fP, so that the +files will be extracted into \fIsavedir\fP. +.SH RETURN VALUES +On successful completion, these functions will return 0. On failure, +they will return \-1 and set \fIerrno\fP to an appropriate value. +.SH ERRORS +These functions will fail under the same conditions that the +\fBtar_skip_regfile\fP(), \fBtar_extract_regfile\fP(), \fBopendir\fP(), +\fBlstat\fP(), or \fBtar_append_file\fP() functions fail. +.SH SEE ALSO +.BR opendir (2), +.BR lstat (2), +.BR tar_skip_regfile (3), +.BR tar_extract_regfile (3), +.BR tar_append_file (3) diff --git a/doc/tar_extract_file.3 b/doc/tar_extract_file.3 new file mode 100644 index 0000000..de12bda --- /dev/null +++ b/doc/tar_extract_file.3 @@ -0,0 +1,84 @@ +.TH tar_extract_file 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +tar_extract_file, tar_extract_regfile, tar_extract_hardlink, +tar_extract_symlink, tar_extract_chardev, tar_extract_blockdev, +tar_extract_dir, tar_extract_fifo, tar_skip_regfile, tar_set_file_perms \- +extract files from a tar archive +.SH SYNOPSIS +.B #include +.P +.BI "int tar_extract_file(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_regfile(TAR *" t ", char *" realname ");" + +.BI "int tar_skip_regfile(TAR *" t ");" + +.BI "int tar_extract_dir(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_hardlink(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_symlink(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_blockdev(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_chardev(TAR *" t ", char *" realname ");" + +.BI "int tar_extract_fifo(TAR *" t ", char *" realname ");" + +.BI "int tar_set_file_perms(TAR *" t ", char *" realname ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBtar_extract_file\fP() function acts as a front-end to the other +\fBtar_extract_*\fP() functions. It checks the current tar header +associated with the \fITAR\fP handle \fIt\fP (which must be initialized +first by calling \fBth_read\fP()) to determine what kind of file the +header refers to. It then calls the appropriate \fBtar_extract_*\fP() +function to extract that kind of file. + +The \fBtar_skip_regfile\fP() function skips over the +file content blocks and positions the file pointer at the expected +location of the next tar header block. + +The \fBtar_set_file_perms\fP() function sets the attributes of the +extracted file to match the encoded values. This includes the file's +modification time, mode, owner, and group. This function is automatically +called by \fBtar_extract_file\fP(), but applications which call the +other \fBtar_extract_*\fP() functions directly will need to call +\fBtar_set_file_perms\fP() manually if this behavior is desired. +.SH RETURN VALUES +On successful completion, the functions documented here will +return 0. On failure, they will return \-1 and set \fIerrno\fP to an +appropriate value. + +The \fBtar_extract_dir\fP() function will return 1 if the directory +already exists. +.SH ERRORS +The \fBtar_extract_file\fP() function will fail if: +.IP \fBEEXIST\fP +If the \fBO_NOOVERWRITE\fP flag is set and the file already exists. +.PP +The \fBtar_extract_*\fP() functions will fail if: +.IP \fBEINVAL\fP +An entry could not be added to the internal file hash. +.IP \fBEINVAL\fP +Less than \fBT_BLOCKSIZE\fP bytes were read from the tar archive. +.IP \fBEINVAL\fP +The current file header associated with \fIt\fP refers to a kind of file +other than the one which the called function knows about. +.PP +They may also fail if any of the following functions fail: \fBmkdir\fP(), +\fBwrite\fP(), \fBlink\fP(), \fBsymlink\fP(), \fBmknod\fP(), \fBmkfifo\fP(), +\fButime\fP(), \fBchown\fP(), \fBlchown\fP(), \fBchmod\fP(), or \fBlstat\fP(). +.SH SEE ALSO +.BR mkdir (2), +.BR write (2), +.BR link (2), +.BR symlink (2), +.BR mknod (2), +.BR mkfifo (2), +.BR utime (2), +.BR chown (2), +.BR lchown (2), +.BR chmod (2), +.BR lstat (2) diff --git a/doc/tar_open.3 b/doc/tar_open.3 new file mode 100644 index 0000000..4b28cbf --- /dev/null +++ b/doc/tar_open.3 @@ -0,0 +1,97 @@ +.TH tar_open 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +tar_open, tar_close \- access a tar archive via a handle +.SH SYNOPSIS +.B #include +.P +.BI "int tar_open(TAR **" t ", char *" pathname "," +.BI "tartype_t *" type ", int " oflags "," +.BI "int " mode ", int " options ");" + +.BI "int tar_fdopen(TAR **" t ", int " fd "," +.BI "char *" pathname ", tartype_t *" type "," +.BI "int " oflags ", int " mode "," +.BI "int " options ");" + +.BI "int tar_fd(TAR *" t");" + +.BI "int tar_close(TAR *" t");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBtar_open\fP() function opens a tar archive file corresponding to +the filename named by the \fIpathname\fP argument. The \fIoflags\fP +argument must be either \fBO_RDONLY\fP or \fBO_WRONLY\fP. + +The \fItype\fP argument specifies the access methods for the given file +type. The \fItartype_t\fP structure has members named \fIopenfunc\fP, +\fIclosefunc\fP, \fIreadfunc\fP() and \fIwritefunc\fP(), which are +pointers to the functions for opening, closing, reading, and writing +the file, respectively. If \fItype\fP is \fINULL\fP, the file type +defaults to a normal file, and the standard \fIopen\fP(), \fIclose\fP(), +\fIread\fP(), and \fIwrite\fP() functions are used. + +The \fIoptions\fP argument is a logical-or'ed combination of zero or more +of the following: +.IP \fBTAR_GNU\fP +Use GNU extensions. +.IP \fBTAR_VERBOSE\fP +Send status messages to \fIstdout\fP. +.IP \fBTAR_NOOVERWRITE\fP +Do not overwrite pre-existing files. +.IP \fBTAR_IGNORE_EOT\fP +Skip all-zero blocks instead of treating them as EOT. +.IP \fBTAR_IGNORE_MAGIC\fP +Do not validate the magic field in file headers. +.IP \fBTAR_CHECK_VERSION\fP +Check the version field in file headers. (This field is normally ignored.) +.IP \fBTAR_IGNORE_CRC\fP +Do not validate the CRC of file headers. +.PP + +The \fBtar_open\fP() function allocates memory for a \fITAR\fP handle, +and a pointer to the allocated memory is saved in the location specified +by \fIt\fP. The \fITAR\fP handle may be passed to other \fIlibtar\fP +calls to modify the opened tar archive. The \fITAR\fP handle maintains +all of the information about the open tar archive, including the archive +\fItype\fP, \fIoptions\fP, and \fIoflags\fP selected when \fBtar_open\fP() +was called. + +The \fITAR\fP handle generated by \fBtar_open\fP() contains a file header +structure. When reading a tar archive, this structure contains the last +file header read from the tar archive. When writing a tar archive, +this structure is used as a staging area to construct the next file +header to be written to the archive. In addition, the \fITAR\fP handle +contains a hash table which is used to keep track of the device and +inode information for each file which gets written to the tar archive. +This is used to detect hard links, so that files do not need to be +duplicated in the archive. + +The \fBtar_fdopen\fP() function is identical to the \fBtar_open\fP() function, +except that \fIfd\fP is used as the previously-opened file descriptor for +the tar file instead of calling \fItype->openfunc\fP() to open the file. + +The \fBtar_fd\fP() function returns the file descriptor associated with +the \fITAR\fP handle \fIt\fP. + +The \fBtar_close\fP() function closes the file descriptor associated +with the \fITAR\fP handle \fIt\fP and frees all dynamically-allocated +memory. +.SH RETURN VALUE +The \fBtar_open\fP(), \fBtar_fdopen\fP(), and \fBtar_close\fP() functions +return 0 on success. On failure, they return \-1 and set \fIerrno\fP. + +The \fBtar_fd\fP() function returns the file descriptor associated with +the \fITAR\fP handle \fIt\fP. +.SH ERRORS +\fBtar_open\fP() will fail if: +.IP \fBEINVAL\fP +The \fIoflags\fP argument was something other than \fBO_RDONLY\fP or \fBO_WRONLY\fP. +.PP +In addition, \fBtar_open\fP() and \fBtar_close\fP() may fail if it +cannot allocate memory using \fBcalloc\fP(), or if the +open or close functions for the specified tar archive \fItype\fP fail. +.SH SEE ALSO +.BR open (2), +.BR close (2), +.BR calloc (3) diff --git a/doc/th_get_pathname.3 b/doc/th_get_pathname.3 new file mode 100644 index 0000000..41b532a --- /dev/null +++ b/doc/th_get_pathname.3 @@ -0,0 +1,63 @@ +.TH th_get_pathname 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +th_get_pathname, th_get_uid, th_get_gid, th_get_mode, th_get_crc, th_get_size, th_get_mtime, th_get_devmajor, th_get_devminor, th_get_linkname \- extract individual fields of a tar header + +TH_ISREG, TH_ISLNK, TH_ISSYM, TH_ISCHR, TH_ISBLK, TH_ISDIR, TH_ISFIFO \- determine what kind of file a tar header refers to + +TH_ISLONGNAME, TH_ISLONGLINK \- determine whether the GNU extensions are in use +.SH SYNOPSIS +.B #include +.P +.BI "char *th_get_linkname(TAR *" t ");" + +.BI "char *th_get_pathname(TAR *" t ");" + +.BI "mode_t th_get_mode(TAR *" t ");" + +.BI "uid_t th_get_uid(TAR *" t ");" + +.BI "gid_t th_get_gid(TAR *" t ");" + +.BI "int th_get_crc(TAR *" t ");" + +.BI "off_t th_get_size(TAR *" t ");" + +.BI "time_t th_get_mtime(TAR *" t ");" + +.BI "major_t th_get_devmajor(TAR *" t ");" + +.BI "minor_t th_get_devminor(TAR *" t ");" + +.BI "int TH_ISREG(TAR *" t ");" + +.BI "int TH_ISLNK(TAR *" t ");" + +.BI "int TH_ISSYM(TAR *" t ");" + +.BI "int TH_ISCHR(TAR *" t ");" + +.BI "int TH_ISBLK(TAR *" t ");" + +.BI "int TH_ISDIR(TAR *" t ");" + +.BI "int TH_ISFIFO(TAR *" t ");" + +.BI "int TH_ISLONGNAME(TAR *" t ");" + +.BI "int TH_ISLONGLINK(TAR *" t ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBth_get_*\fP() functions extract individual fields from the current +tar header associated with the \fITAR\fP handle \fIt\fP. + +The \fBTH_IS*\fP() macros are used to evaluate what kind of file is +pointed to by the current tar header associated with the \fITAR\fP +handle \fIt\fP. + +The \fBTH_ISLONGNAME\fP() and \fBTH_ISLONGLINK\fP() macros evaluate +whether or not the GNU extensions are used by the current tar header +associated with the \fITAR\fP handle \fIt\fP. This is only relevant +if the \fBTAR_GNU\fP option was used when \fItar_open\fP() was called. +.SH SEE ALSO +.BR tar_open (3) diff --git a/doc/th_print_long_ls.3 b/doc/th_print_long_ls.3 new file mode 100644 index 0000000..9561e50 --- /dev/null +++ b/doc/th_print_long_ls.3 @@ -0,0 +1,22 @@ +.TH th_print_long_ls 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +th_print, th_print_long_ls \- print out information about a tar file header +.SH SYNOPSIS +.B #include +.P +.BI "void th_print_long_ls(TAR *" t ");" + +.BI "void th_print(TAR *" t ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBth_print_long_ls\fP() function prints a line to \fIstdout\fP which +describes the file pointed to by the current file header associated with +the \fITAR\fP handle \fIt\fP. The output is similar to that of "ls \-l". + +The \fBth_print\fP() function prints the value of each field of the +current file header associated with the \fITAR\fP handle \fIt\fP to +\fIstdout\fP. This is mainly used for debugging purposes. +.SH SEE ALSO +.BR tar_open (3), +.BR th_read (3) diff --git a/doc/th_read.3 b/doc/th_read.3 new file mode 100644 index 0000000..97ad0bf --- /dev/null +++ b/doc/th_read.3 @@ -0,0 +1,34 @@ +.TH th_read 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +th_read, th_write \- read and write a file header block from a tar archive +.SH SYNOPSIS +.B #include +.P +.BI "int th_read(TAR *" t ");" + +.BI "int th_write(TAR *" t ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBth_read\fP() function reads the next block from the tar archive +associated with the \fITAR\fP handle \fIt\fP. It then sets the +current tar header associated with \fIt\fP to the contents of the block +read. + +The \fBth_write\fP() function writes the contents of the current +tar header associated with \fIt\fP to the tar archive associated +with \fIt\fP. +.SH RETURN VALUE +On successful completion, \fBth_read\fP() and \fBth_write\fP() will +return 0. On failure, they will return \-1 and set \fIerrno\fP to an +appropriate value. + +On \fIEOF\fP, \fBth_read\fP() will return 1. +.SH ERRORS +\fBth_read\fP() and \fBth_write\fP() will fail if: +.IP \fBEINVAL\fP +Less than \fBT_BLOCKSIZE\fP blocks were read or written. +.PP +.SH SEE ALSO +.BR tar_block_read (3), +.BR tar_block_write (3) diff --git a/doc/th_set_from_stat.3 b/doc/th_set_from_stat.3 new file mode 100644 index 0000000..0448f89 --- /dev/null +++ b/doc/th_set_from_stat.3 @@ -0,0 +1,45 @@ +.TH th_set_from_stat 3 "Jan 2001" "University of Illinois" "C Library Calls" +.SH NAME +th_set_from_stat, th_finish, th_set_type, th_set_path, th_set_link, th_set_device, th_set_user, th_set_group, th_set_mode, th_set_mtime, th_set_size \- set fields of a tar file header +.SH SYNOPSIS +.B #include +.P +.BI "void th_set_from_stat(TAR *" t ", struct stat *" s ");" + +.BI "void th_set_type(TAR *" t ", mode_t " mode ");" + +.BI "void th_set_path(TAR *" t ", char *" pathname ");" + +.BI "void th_set_link(TAR *" t ", char *" linkname ");" + +.BI "void th_set_device(TAR *" t ", dev_t " device ");" + +.BI "void th_set_user(TAR *" t ", uid_t " uid ");" + +.BI "void th_set_group(TAR *" t ", gid_t " gid ");" + +.BI "void th_set_mode(TAR *" t ", mode_t " mode ");" + +.BI "void th_set_mtime(TAR *" t ", time_t " fmtime ");" + +.BI "void th_set_size(TAR *" t ", off_t " fsize ");" + +.BI "void th_finish(TAR *" t ");" +.SH VERSION +This man page documents version 1.2 of \fBlibtar\fP. +.SH DESCRIPTION +The \fBth_set_*\fP() functions each set an individual field of the +current tar header associated with the \fITAR\fP handle \fIt\fP. The +\fBth_set_user\fP() and \fBth_set_group\fP() functions set both the +numeric user/group ID fields and the user/group name text fields. +The other functions set only the field that they refer to. + +The \fBth_set_from_stat\fP() function uses the other \fBth_set_*\fP() +functions to set all of the fields at once, based on the data passed to +it in the argument \fIs\fP. + +The \fBth_finish\fP() function sets the appropriate constants for the +magic and version fields. It then calculates the header checksum and +fills in the checksum field. +.SH SEE ALSO +.BR lstat (2) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..60acbe6 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,106 @@ +# @configure_input@ + +### Path settings +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +includedir = @includedir@ +datarootdir = @datarootdir@ + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +@ENCAP_DEFS@ + +SHELL = @SHELL@ + +### Installation programs and flags +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s +INSTALL_DATA = @INSTALL_DATA@ +LN_S = @LN_S@ +MKDIR = @MKDIR@ + +### Compiler and link options +CC = @CC@ +LIBTOOL = @LIBTOOL@ +CPPFLAGS = -I. \ + -I.. \ + -I${srcdir} \ + -I${top_srcdir}/compat \ + -I../listhash \ + @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +RANLIB = @RANLIB@ +@SET_MAKE@ +VPATH = @srcdir@:@top_srcdir@/compat:../listhash + + +### Makefile rules - no user-servicable parts below + +LIBTAR_OBJS = append.lo \ + block.lo \ + decode.lo \ + encode.lo \ + extract.lo \ + handle.lo \ + libtar_hash.lo \ + libtar_list.lo \ + output.lo \ + util.lo \ + wrapper.lo +LIBTAR_HDRS = ../config.h \ + ${top_srcdir}/compat/compat.h \ + ${srcdir}/libtar.h \ + ${srcdir}/internal.h \ + ../listhash/libtar_listhash.h +LIBTAR_LIBS = ./libtar.la +ALL = libtar.la +LDFLAGS = @LDFLAGS@ +CFLAGS = @CFLAGS@ + +DISTCLEANFILES = ../listhash/libtar_listhash.h \ + ../listhash/libtar_list.c \ + ../listhash/libtar_hash.c + + +all: ${ALL} + +.PHONY: clean distclean install + +libtar.la: ${LIBTAR_OBJS} ${LTLIBOBJS} + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -o libtar.la $(LIBTAR_OBJS) $(LTLIBOBJS) -rpath $(libdir) +# ${AR} rc libtar.a ${LIBTAR_OBJS} ${LIBOBJS} +# ${RANLIB} libtar.a + +${LIBTAR_OBJS}: ${LIBTAR_HDRS} + +%.lo: $(srcdir)/%.c + $(LIBTOOL) --mode=compile ${CC} ${CFLAGS} ${CPPFLAGS} -c -o $@ $< + +%.lo: ../listhash/%.c + $(LIBTOOL) --mode=compile ${CC} ${CFLAGS} ${CPPFLAGS} -c -o $@ $< + +%.lo: ${top_srcdir}/compat/%.c + $(LIBTOOL) --mode=compile ${CC} ${CFLAGS} ${CPPFLAGS} -c -o $@ $< + +clean: + rm -rf *~ *.o $(LIBTAR_OBJS) ${ALL} core .libs $(LTLIBOBJS) + +distclean: clean + rm -f Makefile ${DISTCLEANFILES} + +install: ${ALL} + ${MKDIR} ${DESTDIR}${libdir} + $(LIBTOOL) --mode=install ${INSTALL_DATA} libtar.la ${DESTDIR}${libdir} + ${MKDIR} ${DESTDIR}${includedir} + ${INSTALL_DATA} ${srcdir}/libtar.h ${DESTDIR}${includedir} + ${INSTALL_DATA} ../listhash/libtar_listhash.h ${DESTDIR}${includedir} + diff --git a/lib/append.c b/lib/append.c new file mode 100644 index 0000000..13e1ace --- /dev/null +++ b/lib/append.c @@ -0,0 +1,256 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** append.c - libtar code to append files to a tar archive +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + + +struct tar_dev +{ + dev_t td_dev; + libtar_hash_t *td_h; +}; +typedef struct tar_dev tar_dev_t; + +struct tar_ino +{ + ino_t ti_ino; + char ti_name[MAXPATHLEN]; +}; +typedef struct tar_ino tar_ino_t; + + +/* free memory associated with a tar_dev_t */ +void +tar_dev_free(tar_dev_t *tdp) +{ + libtar_hash_free(tdp->td_h, free); + free(tdp); +} + + +/* appends a file to the tar archive */ +int +tar_append_file(TAR *t, const char *realname, const char *savename) +{ + struct stat s; + int i; + libtar_hashptr_t hp; + tar_dev_t *td = NULL; + tar_ino_t *ti = NULL; + char path[MAXPATHLEN]; + +#ifdef DEBUG + printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", " + "savename=\"%s\")\n", t, t->pathname, realname, + (savename ? savename : "[NULL]")); +#endif + + if (lstat(realname, &s) != 0) + { +#ifdef DEBUG + perror("lstat()"); +#endif + return -1; + } + + /* set header block */ +#ifdef DEBUG + puts(" tar_append_file(): setting header block..."); +#endif + memset(&(t->th_buf), 0, sizeof(struct tar_header)); + th_set_from_stat(t, &s); + + /* set the header path */ +#ifdef DEBUG + puts(" tar_append_file(): setting header path..."); +#endif + th_set_path(t, (savename ? savename : realname)); + + /* check if it's a hardlink */ +#ifdef DEBUG + puts(" tar_append_file(): checking inode cache for hardlink..."); +#endif + libtar_hashptr_reset(&hp); + if (libtar_hash_getkey(t->h, &hp, &(s.st_dev), + (libtar_matchfunc_t)dev_match) != 0) + td = (tar_dev_t *)libtar_hashptr_data(&hp); + else + { +#ifdef DEBUG + printf("+++ adding hash for device (0x%lx, 0x%lx)...\n", + major(s.st_dev), minor(s.st_dev)); +#endif + td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t)); + td->td_dev = s.st_dev; + td->td_h = libtar_hash_new(256, (libtar_hashfunc_t)ino_hash); + if (td->td_h == NULL) + return -1; + if (libtar_hash_add(t->h, td) == -1) + return -1; + } + libtar_hashptr_reset(&hp); + if (libtar_hash_getkey(td->td_h, &hp, &(s.st_ino), + (libtar_matchfunc_t)ino_match) != 0) + { + ti = (tar_ino_t *)libtar_hashptr_data(&hp); +#ifdef DEBUG + printf(" tar_append_file(): encoding hard link \"%s\" " + "to \"%s\"...\n", realname, ti->ti_name); +#endif + t->th_buf.typeflag = LNKTYPE; + th_set_link(t, ti->ti_name); + } + else + { +#ifdef DEBUG + printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld " + "(\"%s\")...\n", major(s.st_dev), minor(s.st_dev), + s.st_ino, realname); +#endif + ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t)); + if (ti == NULL) + return -1; + ti->ti_ino = s.st_ino; + snprintf(ti->ti_name, sizeof(ti->ti_name), "%s", + savename ? savename : realname); + libtar_hash_add(td->td_h, ti); + } + + /* check if it's a symlink */ + if (TH_ISSYM(t)) + { + i = readlink(realname, path, sizeof(path)); + if (i == -1) + return -1; + if (i >= MAXPATHLEN) + i = MAXPATHLEN - 1; + path[i] = '\0'; +#ifdef DEBUG + printf(" tar_append_file(): encoding symlink \"%s\" -> " + "\"%s\"...\n", realname, path); +#endif + th_set_link(t, path); + } + + /* print file info */ + if (t->options & TAR_VERBOSE) + th_print_long_ls(t); + +#ifdef DEBUG + puts(" tar_append_file(): writing header"); +#endif + /* write header */ + if (th_write(t) != 0) + { +#ifdef DEBUG + printf("t->fd = %d\n", t->fd); +#endif + return -1; + } +#ifdef DEBUG + puts(" tar_append_file(): back from th_write()"); +#endif + + /* if it's a regular file, write the contents as well */ + if (TH_ISREG(t) && tar_append_regfile(t, realname) != 0) + return -1; + + return 0; +} + + +/* write EOF indicator */ +int +tar_append_eof(TAR *t) +{ + int i, j; + char block[T_BLOCKSIZE]; + + memset(&block, 0, T_BLOCKSIZE); + for (j = 0; j < 2; j++) + { + i = tar_block_write(t, &block); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } + + return 0; +} + + +/* add file contents to a tarchive */ +int +tar_append_regfile(TAR *t, const char *realname) +{ + char block[T_BLOCKSIZE]; + int filefd; + int i, j; + size_t size; + + filefd = open(realname, O_RDONLY); + if (filefd == -1) + { +#ifdef DEBUG + perror("open()"); +#endif + return -1; + } + + size = th_get_size(t); + for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE) + { + j = read(filefd, &block, T_BLOCKSIZE); + if (j != T_BLOCKSIZE) + { + if (j != -1) + errno = EINVAL; + return -1; + } + if (tar_block_write(t, &block) == -1) + return -1; + } + + if (i > 0) + { + j = read(filefd, &block, i); + if (j == -1) + return -1; + memset(&(block[i]), 0, T_BLOCKSIZE - i); + if (tar_block_write(t, &block) == -1) + return -1; + } + + close(filefd); + + return 0; +} + + diff --git a/lib/block.c b/lib/block.c new file mode 100644 index 0000000..092bc28 --- /dev/null +++ b/lib/block.c @@ -0,0 +1,394 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** block.c - libtar code to handle tar archive header blocks +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + + +#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit)) + + +/* read a header block */ +int +th_read_internal(TAR *t) +{ + int i; + int num_zero_blocks = 0; + +#ifdef DEBUG + printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname); +#endif + + while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE) + { + /* two all-zero blocks mark EOF */ + if (t->th_buf.name[0] == '\0') + { + num_zero_blocks++; + if (!BIT_ISSET(t->options, TAR_IGNORE_EOT) + && num_zero_blocks >= 2) + return 0; /* EOF */ + else + continue; + } + + /* verify magic and version */ + if (BIT_ISSET(t->options, TAR_CHECK_MAGIC) + && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0) + { +#ifdef DEBUG + puts("!!! unknown magic value in tar header"); +#endif + return -2; + } + + if (BIT_ISSET(t->options, TAR_CHECK_VERSION) + && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0) + { +#ifdef DEBUG + puts("!!! unknown version value in tar header"); +#endif + return -2; + } + + /* check chksum */ + if (!BIT_ISSET(t->options, TAR_IGNORE_CRC) + && !th_crc_ok(t)) + { +#ifdef DEBUG + puts("!!! tar header checksum error"); +#endif + return -2; + } + + break; + } + +#ifdef DEBUG + printf("<== th_read_internal(): returning %d\n", i); +#endif + return i; +} + + +/* wrapper function for th_read_internal() to handle GNU extensions */ +int +th_read(TAR *t) +{ + int i; + size_t sz, j, blocks; + char *ptr; + +#ifdef DEBUG + printf("==> th_read(t=0x%lx)\n", t); +#endif + + if (t->th_buf.gnu_longname != NULL) + free(t->th_buf.gnu_longname); + if (t->th_buf.gnu_longlink != NULL) + free(t->th_buf.gnu_longlink); + memset(&(t->th_buf), 0, sizeof(struct tar_header)); + + i = th_read_internal(t); + if (i == 0) + return 1; + else if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* check for GNU long link extention */ + if (TH_ISLONGLINK(t)) + { + sz = th_get_size(t); + blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0); + if (blocks > ((size_t)-1 / T_BLOCKSIZE)) + { + errno = E2BIG; + return -1; + } +#ifdef DEBUG + printf(" th_read(): GNU long linkname detected " + "(%ld bytes, %d blocks)\n", sz, blocks); +#endif + t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE); + if (t->th_buf.gnu_longlink == NULL) + return -1; + + for (j = 0, ptr = t->th_buf.gnu_longlink; j < blocks; + j++, ptr += T_BLOCKSIZE) + { +#ifdef DEBUG + printf(" th_read(): reading long linkname " + "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); +#endif + i = tar_block_read(t, ptr); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } +#ifdef DEBUG + printf(" th_read(): read block == \"%s\"\n", ptr); +#endif + } +#ifdef DEBUG + printf(" th_read(): t->th_buf.gnu_longlink == \"%s\"\n", + t->th_buf.gnu_longlink); +#endif + + i = th_read_internal(t); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } + + /* check for GNU long name extention */ + if (TH_ISLONGNAME(t)) + { + sz = th_get_size(t); + blocks = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0); + if (blocks > ((size_t)-1 / T_BLOCKSIZE)) + { + errno = E2BIG; + return -1; + } +#ifdef DEBUG + printf(" th_read(): GNU long filename detected " + "(%ld bytes, %d blocks)\n", sz, blocks); +#endif + t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE); + if (t->th_buf.gnu_longname == NULL) + return -1; + + for (j = 0, ptr = t->th_buf.gnu_longname; j < blocks; + j++, ptr += T_BLOCKSIZE) + { +#ifdef DEBUG + printf(" th_read(): reading long filename " + "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); +#endif + i = tar_block_read(t, ptr); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } +#ifdef DEBUG + printf(" th_read(): read block == \"%s\"\n", ptr); +#endif + } +#ifdef DEBUG + printf(" th_read(): t->th_buf.gnu_longname == \"%s\"\n", + t->th_buf.gnu_longname); +#endif + + i = th_read_internal(t); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } + +#if 0 + /* + ** work-around for old archive files with broken typeflag fields + ** NOTE: I fixed this in the TH_IS*() macros instead + */ + + /* + ** (directories are signified with a trailing '/') + */ + if (t->th_buf.typeflag == AREGTYPE + && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/') + t->th_buf.typeflag = DIRTYPE; + + /* + ** fallback to using mode bits + */ + if (t->th_buf.typeflag == AREGTYPE) + { + mode = (mode_t)oct_to_int(t->th_buf.mode); + + if (S_ISREG(mode)) + t->th_buf.typeflag = REGTYPE; + else if (S_ISDIR(mode)) + t->th_buf.typeflag = DIRTYPE; + else if (S_ISFIFO(mode)) + t->th_buf.typeflag = FIFOTYPE; + else if (S_ISCHR(mode)) + t->th_buf.typeflag = CHRTYPE; + else if (S_ISBLK(mode)) + t->th_buf.typeflag = BLKTYPE; + else if (S_ISLNK(mode)) + t->th_buf.typeflag = SYMTYPE; + } +#endif + + return 0; +} + + +/* write a header block */ +int +th_write(TAR *t) +{ + int i, j; + char type2; + size_t sz, sz2; + char *ptr; + char buf[T_BLOCKSIZE]; + +#ifdef DEBUG + printf("==> th_write(TAR=\"%s\")\n", t->pathname); + th_print(t); +#endif + + if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL) + { +#ifdef DEBUG + printf("th_write(): using gnu_longlink (\"%s\")\n", + t->th_buf.gnu_longlink); +#endif + /* save old size and type */ + type2 = t->th_buf.typeflag; + sz2 = th_get_size(t); + + /* write out initial header block with fake size and type */ + t->th_buf.typeflag = GNU_LONGLINK_TYPE; + sz = strlen(t->th_buf.gnu_longlink); + th_set_size(t, sz); + th_finish(t); + i = tar_block_write(t, &(t->th_buf)); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* write out extra blocks containing long name */ + for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0), + ptr = t->th_buf.gnu_longlink; j > 1; + j--, ptr += T_BLOCKSIZE) + { + i = tar_block_write(t, ptr); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } + memset(buf, 0, T_BLOCKSIZE); + strncpy(buf, ptr, T_BLOCKSIZE); + i = tar_block_write(t, &buf); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* reset type and size to original values */ + t->th_buf.typeflag = type2; + th_set_size(t, sz2); + } + + if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL) + { +#ifdef DEBUG + printf("th_write(): using gnu_longname (\"%s\")\n", + t->th_buf.gnu_longname); +#endif + /* save old size and type */ + type2 = t->th_buf.typeflag; + sz2 = th_get_size(t); + + /* write out initial header block with fake size and type */ + t->th_buf.typeflag = GNU_LONGNAME_TYPE; + sz = strlen(t->th_buf.gnu_longname); + th_set_size(t, sz); + th_finish(t); + i = tar_block_write(t, &(t->th_buf)); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* write out extra blocks containing long name */ + for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0), + ptr = t->th_buf.gnu_longname; j > 1; + j--, ptr += T_BLOCKSIZE) + { + i = tar_block_write(t, ptr); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } + memset(buf, 0, T_BLOCKSIZE); + strncpy(buf, ptr, T_BLOCKSIZE); + i = tar_block_write(t, &buf); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* reset type and size to original values */ + t->th_buf.typeflag = type2; + th_set_size(t, sz2); + } + + th_finish(t); + +#ifdef DEBUG + /* print tar header */ + th_print(t); +#endif + + i = tar_block_write(t, &(t->th_buf)); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + +#ifdef DEBUG + puts("th_write(): returning 0"); +#endif + return 0; +} + + diff --git a/lib/decode.c b/lib/decode.c new file mode 100644 index 0000000..c16ea2d --- /dev/null +++ b/lib/decode.c @@ -0,0 +1,120 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** decode.c - libtar code to decode tar header blocks +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + + +/* determine full path name */ +char * +th_get_pathname(TAR *t) +{ + static TLS_THREAD char filename[MAXPATHLEN]; + + if (t->th_buf.gnu_longname) + return t->th_buf.gnu_longname; + + if (t->th_buf.prefix[0] != '\0') + { + snprintf(filename, sizeof(filename), "%.155s/%.100s", + t->th_buf.prefix, t->th_buf.name); + return filename; + } + + snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name); + return filename; +} + + +uid_t +th_get_uid(TAR *t) +{ + int uid; + struct passwd *pw; + + pw = getpwnam(t->th_buf.uname); + if (pw != NULL) + return pw->pw_uid; + + /* if the password entry doesn't exist */ + sscanf(t->th_buf.uid, "%o", &uid); + return uid; +} + + +gid_t +th_get_gid(TAR *t) +{ + int gid; + struct group *gr; + + gr = getgrnam(t->th_buf.gname); + if (gr != NULL) + return gr->gr_gid; + + /* if the group entry doesn't exist */ + sscanf(t->th_buf.gid, "%o", &gid); + return gid; +} + + +mode_t +th_get_mode(TAR *t) +{ + mode_t mode; + + mode = (mode_t)oct_to_int(t->th_buf.mode); + if (! (mode & S_IFMT)) + { + switch (t->th_buf.typeflag) + { + case SYMTYPE: + mode |= S_IFLNK; + break; + case CHRTYPE: + mode |= S_IFCHR; + break; + case BLKTYPE: + mode |= S_IFBLK; + break; + case DIRTYPE: + mode |= S_IFDIR; + break; + case FIFOTYPE: + mode |= S_IFIFO; + break; + case AREGTYPE: + if (t->th_buf.name[strlen(t->th_buf.name) - 1] == '/') + { + mode |= S_IFDIR; + break; + } + /* FALLTHROUGH */ + case LNKTYPE: + case REGTYPE: + default: + mode |= S_IFREG; + } + } + + return mode; +} + + diff --git a/lib/encode.c b/lib/encode.c new file mode 100644 index 0000000..5ea29ff --- /dev/null +++ b/lib/encode.c @@ -0,0 +1,213 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** encode.c - libtar code to encode tar header blocks +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + + +/* magic, version, and checksum */ +void +th_finish(TAR *t) +{ + if (t->options & TAR_GNU) + { + /* we're aiming for this result, but must do it in + * two calls to avoid FORTIFY segfaults on some Linux + * systems: + * strncpy(t->th_buf.magic, "ustar ", 8); + */ + strncpy(t->th_buf.magic, "ustar ", 6); + strncpy(t->th_buf.version, " ", 2); + } + else + { + strncpy(t->th_buf.version, TVERSION, TVERSLEN); + strncpy(t->th_buf.magic, TMAGIC, TMAGLEN); + } + + int_to_oct(th_crc_calc(t), t->th_buf.chksum, 8); +} + + +/* map a file mode to a typeflag */ +void +th_set_type(TAR *t, mode_t mode) +{ + if (S_ISLNK(mode)) + t->th_buf.typeflag = SYMTYPE; + if (S_ISREG(mode)) + t->th_buf.typeflag = REGTYPE; + if (S_ISDIR(mode)) + t->th_buf.typeflag = DIRTYPE; + if (S_ISCHR(mode)) + t->th_buf.typeflag = CHRTYPE; + if (S_ISBLK(mode)) + t->th_buf.typeflag = BLKTYPE; + if (S_ISFIFO(mode) || S_ISSOCK(mode)) + t->th_buf.typeflag = FIFOTYPE; +} + + +/* encode file path */ +void +th_set_path(TAR *t, const char *pathname) +{ + char suffix[2] = ""; + char *tmp; + +#ifdef DEBUG + printf("in th_set_path(th, pathname=\"%s\")\n", pathname); +#endif + + if (t->th_buf.gnu_longname != NULL) + free(t->th_buf.gnu_longname); + t->th_buf.gnu_longname = NULL; + + if (pathname[strlen(pathname) - 1] != '/' && TH_ISDIR(t)) + strcpy(suffix, "/"); + + if (strlen(pathname) > T_NAMELEN-1 && (t->options & TAR_GNU)) + { + /* GNU-style long name */ + t->th_buf.gnu_longname = strdup(pathname); + strncpy(t->th_buf.name, t->th_buf.gnu_longname, T_NAMELEN); + } + else if (strlen(pathname) > T_NAMELEN) + { + /* POSIX-style prefix field */ + tmp = strchr(&(pathname[strlen(pathname) - T_NAMELEN - 1]), '/'); + if (tmp == NULL) + { + printf("!!! '/' not found in \"%s\"\n", pathname); + return; + } + snprintf(t->th_buf.name, 100, "%s%s", &(tmp[1]), suffix); + snprintf(t->th_buf.prefix, + ((tmp - pathname + 1) < + 155 ? (tmp - pathname + 1) : 155), "%s", pathname); + } + else + /* classic tar format */ + snprintf(t->th_buf.name, 100, "%s%s", pathname, suffix); + +#ifdef DEBUG + puts("returning from th_set_path()..."); +#endif +} + + +/* encode link path */ +void +th_set_link(TAR *t, const char *linkname) +{ +#ifdef DEBUG + printf("==> th_set_link(th, linkname=\"%s\")\n", linkname); +#endif + + if (strlen(linkname) > T_NAMELEN-1 && (t->options & TAR_GNU)) + { + /* GNU longlink format */ + t->th_buf.gnu_longlink = strdup(linkname); + strcpy(t->th_buf.linkname, "././@LongLink"); + } + else + { + /* classic tar format */ + strlcpy(t->th_buf.linkname, linkname, + sizeof(t->th_buf.linkname)); + if (t->th_buf.gnu_longlink != NULL) + free(t->th_buf.gnu_longlink); + t->th_buf.gnu_longlink = NULL; + } +} + + +/* encode device info */ +void +th_set_device(TAR *t, dev_t device) +{ +#ifdef DEBUG + printf("th_set_device(): major = %d, minor = %d\n", + major(device), minor(device)); +#endif + int_to_oct(major(device), t->th_buf.devmajor, 8); + int_to_oct(minor(device), t->th_buf.devminor, 8); +} + + +/* encode user info */ +void +th_set_user(TAR *t, uid_t uid) +{ + struct passwd *pw; + + pw = getpwuid(uid); + if (pw != NULL) + strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname)); + + int_to_oct(uid, t->th_buf.uid, 8); +} + + +/* encode group info */ +void +th_set_group(TAR *t, gid_t gid) +{ + struct group *gr; + + gr = getgrgid(gid); + if (gr != NULL) + strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname)); + + int_to_oct(gid, t->th_buf.gid, 8); +} + + +/* encode file mode */ +void +th_set_mode(TAR *t, mode_t fmode) +{ + if (S_ISSOCK(fmode)) + { + fmode &= ~S_IFSOCK; + fmode |= S_IFIFO; + } + int_to_oct(fmode, (t)->th_buf.mode, 8); +} + + +void +th_set_from_stat(TAR *t, struct stat *s) +{ + th_set_type(t, s->st_mode); + if (S_ISCHR(s->st_mode) || S_ISBLK(s->st_mode)) + th_set_device(t, s->st_rdev); + th_set_user(t, s->st_uid); + th_set_group(t, s->st_gid); + th_set_mode(t, s->st_mode); + th_set_mtime(t, s->st_mtime); + if (S_ISREG(s->st_mode)) + th_set_size(t, s->st_size); + else + th_set_size(t, 0); +} + + diff --git a/lib/extract.c b/lib/extract.c new file mode 100644 index 0000000..36357e7 --- /dev/null +++ b/lib/extract.c @@ -0,0 +1,526 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** extract.c - libtar code to extract a file from a tar archive +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + + +static int +tar_set_file_perms(TAR *t, char *realname) +{ + mode_t mode; + uid_t uid; + gid_t gid; + struct utimbuf ut; + char *filename; + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + uid = th_get_uid(t); + gid = th_get_gid(t); + ut.modtime = ut.actime = th_get_mtime(t); + + /* change owner/group */ + if (geteuid() == 0) +#ifdef HAVE_LCHOWN + if (lchown(filename, uid, gid) == -1) + { +# ifdef DEBUG + fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n", + filename, uid, gid, strerror(errno)); +# endif +#else /* ! HAVE_LCHOWN */ + if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1) + { +# ifdef DEBUG + fprintf(stderr, "chown(\"%s\", %d, %d): %s\n", + filename, uid, gid, strerror(errno)); +# endif +#endif /* HAVE_LCHOWN */ + return -1; + } + + /* change access/modification time */ + if (!TH_ISSYM(t) && utime(filename, &ut) == -1) + { +#ifdef DEBUG + perror("utime()"); +#endif + return -1; + } + + /* change permissions */ + if (!TH_ISSYM(t) && chmod(filename, mode) == -1) + { +#ifdef DEBUG + perror("chmod()"); +#endif + return -1; + } + + return 0; +} + + +/* switchboard */ +int +tar_extract_file(TAR *t, char *realname) +{ + int i; + char *lnp; + int pathname_len; + int realname_len; + + if (t->options & TAR_NOOVERWRITE) + { + struct stat s; + + if (lstat(realname, &s) == 0 || errno != ENOENT) + { + errno = EEXIST; + return -1; + } + } + + if (TH_ISDIR(t)) + { + i = tar_extract_dir(t, realname); + if (i == 1) + i = 0; + } + else if (TH_ISLNK(t)) + i = tar_extract_hardlink(t, realname); + else if (TH_ISSYM(t)) + i = tar_extract_symlink(t, realname); + else if (TH_ISCHR(t)) + i = tar_extract_chardev(t, realname); + else if (TH_ISBLK(t)) + i = tar_extract_blockdev(t, realname); + else if (TH_ISFIFO(t)) + i = tar_extract_fifo(t, realname); + else /* if (TH_ISREG(t)) */ + i = tar_extract_regfile(t, realname); + + if (i != 0) + return i; + + i = tar_set_file_perms(t, realname); + if (i != 0) + return i; + + pathname_len = strlen(th_get_pathname(t)) + 1; + realname_len = strlen(realname) + 1; + lnp = (char *)calloc(1, pathname_len + realname_len); + if (lnp == NULL) + return -1; + strcpy(&lnp[0], th_get_pathname(t)); + strcpy(&lnp[pathname_len], realname); +#ifdef DEBUG + printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", " + "value=\"%s\"\n", th_get_pathname(t), realname); +#endif + if (libtar_hash_add(t->h, lnp) != 0) + return -1; + + return 0; +} + + +/* extract regular file */ +int +tar_extract_regfile(TAR *t, char *realname) +{ + mode_t mode; + size_t size; + uid_t uid; + gid_t gid; + int fdout; + int i, k; + char buf[T_BLOCKSIZE]; + char *filename; + +#ifdef DEBUG + printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t, + realname); +#endif + + if (!TH_ISREG(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + size = th_get_size(t); + uid = th_get_uid(t); + gid = th_get_gid(t); + + if (mkdirhier(dirname(filename)) == -1) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n", + filename, mode, uid, gid, size); +#endif + fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC +#ifdef O_BINARY + | O_BINARY +#endif + , 0666); + if (fdout == -1) + { +#ifdef DEBUG + perror("open()"); +#endif + return -1; + } + +#if 0 + /* change the owner. (will only work if run as root) */ + if (fchown(fdout, uid, gid) == -1 && errno != EPERM) + { +#ifdef DEBUG + perror("fchown()"); +#endif + return -1; + } + + /* make sure the mode isn't inheritted from a file we're overwriting */ + if (fchmod(fdout, mode & 07777) == -1) + { +#ifdef DEBUG + perror("fchmod()"); +#endif + return -1; + } +#endif + + /* extract the file */ + for (i = size; i > 0; i -= T_BLOCKSIZE) + { + k = tar_block_read(t, buf); + if (k != T_BLOCKSIZE) + { + if (k != -1) + errno = EINVAL; + return -1; + } + + /* write block to output file */ + if (write(fdout, buf, + ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1) + return -1; + } + + /* close output file */ + if (close(fdout) == -1) + return -1; + +#ifdef DEBUG + printf("### done extracting %s\n", filename); +#endif + + return 0; +} + + +/* skip regfile */ +int +tar_skip_regfile(TAR *t) +{ + int i, k; + size_t size; + char buf[T_BLOCKSIZE]; + + if (!TH_ISREG(t)) + { + errno = EINVAL; + return -1; + } + + size = th_get_size(t); + for (i = size; i > 0; i -= T_BLOCKSIZE) + { + k = tar_block_read(t, buf); + if (k != T_BLOCKSIZE) + { + if (k != -1) + errno = EINVAL; + return -1; + } + } + + return 0; +} + + +/* hardlink */ +int +tar_extract_hardlink(TAR * t, char *realname) +{ + char *filename; + char *linktgt = NULL; + char *lnp; + libtar_hashptr_t hp; + + if (!TH_ISLNK(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + if (mkdirhier(dirname(filename)) == -1) + return -1; + libtar_hashptr_reset(&hp); + if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t), + (libtar_matchfunc_t)libtar_str_match) != 0) + { + lnp = (char *)libtar_hashptr_data(&hp); + linktgt = &lnp[strlen(lnp) + 1]; + } + else + linktgt = th_get_linkname(t); + +#ifdef DEBUG + printf(" ==> extracting: %s (link to %s)\n", filename, linktgt); +#endif + if (link(linktgt, filename) == -1) + { +#ifdef DEBUG + perror("link()"); +#endif + return -1; + } + + return 0; +} + + +/* symlink */ +int +tar_extract_symlink(TAR *t, char *realname) +{ + char *filename; + + if (!TH_ISSYM(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + if (mkdirhier(dirname(filename)) == -1) + return -1; + + if (unlink(filename) == -1 && errno != ENOENT) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (symlink to %s)\n", + filename, th_get_linkname(t)); +#endif + if (symlink(th_get_linkname(t), filename) == -1) + { +#ifdef DEBUG + perror("symlink()"); +#endif + return -1; + } + + return 0; +} + + +/* character device */ +int +tar_extract_chardev(TAR *t, char *realname) +{ + mode_t mode; + unsigned long devmaj, devmin; + char *filename; + + if (!TH_ISCHR(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + devmaj = th_get_devmajor(t); + devmin = th_get_devminor(t); + + if (mkdirhier(dirname(filename)) == -1) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (character device %ld,%ld)\n", + filename, devmaj, devmin); +#endif + if (mknod(filename, mode | S_IFCHR, + compat_makedev(devmaj, devmin)) == -1) + { +#ifdef DEBUG + perror("mknod()"); +#endif + return -1; + } + + return 0; +} + + +/* block device */ +int +tar_extract_blockdev(TAR *t, char *realname) +{ + mode_t mode; + unsigned long devmaj, devmin; + char *filename; + + if (!TH_ISBLK(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + devmaj = th_get_devmajor(t); + devmin = th_get_devminor(t); + + if (mkdirhier(dirname(filename)) == -1) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (block device %ld,%ld)\n", + filename, devmaj, devmin); +#endif + if (mknod(filename, mode | S_IFBLK, + compat_makedev(devmaj, devmin)) == -1) + { +#ifdef DEBUG + perror("mknod()"); +#endif + return -1; + } + + return 0; +} + + +/* directory */ +int +tar_extract_dir(TAR *t, char *realname) +{ + mode_t mode; + char *filename; + + if (!TH_ISDIR(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + + if (mkdirhier(dirname(filename)) == -1) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (mode %04o, directory)\n", filename, + mode); +#endif + if (mkdir(filename, mode) == -1) + { + if (errno == EEXIST) + { + if (chmod(filename, mode) == -1) + { +#ifdef DEBUG + perror("chmod()"); +#endif + return -1; + } + else + { +#ifdef DEBUG + puts(" *** using existing directory"); +#endif + return 1; + } + } + else + { +#ifdef DEBUG + perror("mkdir()"); +#endif + return -1; + } + } + + return 0; +} + + +/* FIFO */ +int +tar_extract_fifo(TAR *t, char *realname) +{ + mode_t mode; + char *filename; + + if (!TH_ISFIFO(t)) + { + errno = EINVAL; + return -1; + } + + filename = (realname ? realname : th_get_pathname(t)); + mode = th_get_mode(t); + + if (mkdirhier(dirname(filename)) == -1) + return -1; + +#ifdef DEBUG + printf(" ==> extracting: %s (fifo)\n", filename); +#endif + if (mkfifo(filename, mode) == -1) + { +#ifdef DEBUG + perror("mkfifo()"); +#endif + return -1; + } + + return 0; +} + + diff --git a/lib/handle.c b/lib/handle.c new file mode 100644 index 0000000..33a262c --- /dev/null +++ b/lib/handle.c @@ -0,0 +1,129 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** handle.c - libtar code for initializing a TAR handle +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef STDC_HEADERS +# include +#endif + + +const char libtar_version[] = PACKAGE_VERSION; + +static tartype_t default_type = { open, close, read, write }; + + +static int +tar_init(TAR **t, const char *pathname, tartype_t *type, + int oflags, int mode, int options) +{ + if ((oflags & O_ACCMODE) == O_RDWR) + { + errno = EINVAL; + return -1; + } + + *t = (TAR *)calloc(1, sizeof(TAR)); + if (*t == NULL) + return -1; + + (*t)->pathname = pathname; + (*t)->options = options; + (*t)->type = (type ? type : &default_type); + (*t)->oflags = oflags; + + if ((oflags & O_ACCMODE) == O_RDONLY) + (*t)->h = libtar_hash_new(256, + (libtar_hashfunc_t)path_hashfunc); + else + (*t)->h = libtar_hash_new(16, (libtar_hashfunc_t)dev_hash); + if ((*t)->h == NULL) + { + free(*t); + return -1; + } + + return 0; +} + + +/* open a new tarfile handle */ +int +tar_open(TAR **t, const char *pathname, tartype_t *type, + int oflags, int mode, int options) +{ + if (tar_init(t, pathname, type, oflags, mode, options) == -1) + return -1; + + if ((options & TAR_NOOVERWRITE) && (oflags & O_CREAT)) + oflags |= O_EXCL; + +#ifdef O_BINARY + oflags |= O_BINARY; +#endif + + (*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode); + if ((*t)->fd == -1) + { + free(*t); + return -1; + } + + return 0; +} + + +int +tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type, + int oflags, int mode, int options) +{ + if (tar_init(t, pathname, type, oflags, mode, options) == -1) + return -1; + + (*t)->fd = fd; + return 0; +} + + +int +tar_fd(TAR *t) +{ + return t->fd; +} + + +/* close tarfile handle */ +int +tar_close(TAR *t) +{ + int i; + + i = (*(t->type->closefunc))(t->fd); + + if (t->h != NULL) + libtar_hash_free(t->h, ((t->oflags & O_ACCMODE) == O_RDONLY + ? free + : (libtar_freefunc_t)tar_dev_free)); + free(t); + + return i; +} + + diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..da7be7f --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,23 @@ +/* +** Copyright 2002-2003 University of Illinois Board of Trustees +** Copyright 2002-2003 Mark D. Roth +** All rights reserved. +** +** internal.h - internal header file for libtar +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include + +#include + +#ifdef TLS +#define TLS_THREAD TLS +#else +#define TLS_THREAD +#endif + diff --git a/lib/libtar.h b/lib/libtar.h new file mode 100644 index 0000000..55f509a --- /dev/null +++ b/lib/libtar.h @@ -0,0 +1,300 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** libtar.h - header file for libtar library +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#ifndef LIBTAR_H +#define LIBTAR_H + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* useful constants */ +#define T_BLOCKSIZE 512 +#define T_NAMELEN 100 +#define T_PREFIXLEN 155 +#define T_MAXPATHLEN (T_NAMELEN + T_PREFIXLEN) + +/* GNU extensions for typeflag */ +#define GNU_LONGNAME_TYPE 'L' +#define GNU_LONGLINK_TYPE 'K' + +/* our version of the tar header structure */ +struct tar_header +{ + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char padding[12]; + char *gnu_longname; + char *gnu_longlink; +}; + + +/***** handle.c ************************************************************/ + +typedef int (*openfunc_t)(const char *, int, ...); +typedef int (*closefunc_t)(int); +typedef ssize_t (*readfunc_t)(int, void *, size_t); +typedef ssize_t (*writefunc_t)(int, const void *, size_t); + +typedef struct +{ + openfunc_t openfunc; + closefunc_t closefunc; + readfunc_t readfunc; + writefunc_t writefunc; +} +tartype_t; + +typedef struct +{ + tartype_t *type; + char *pathname; + long fd; + int oflags; + int options; + struct tar_header th_buf; + libtar_hash_t *h; +} +TAR; + +/* constant values for the TAR options field */ +#define TAR_GNU 1 /* use GNU extensions */ +#define TAR_VERBOSE 2 /* output file info to stdout */ +#define TAR_NOOVERWRITE 4 /* don't overwrite existing files */ +#define TAR_IGNORE_EOT 8 /* ignore double zero blocks as EOF */ +#define TAR_CHECK_MAGIC 16 /* check magic in file header */ +#define TAR_CHECK_VERSION 32 /* check version in file header */ +#define TAR_IGNORE_CRC 64 /* ignore CRC in file header */ + +/* this is obsolete - it's here for backwards-compatibility only */ +#define TAR_IGNORE_MAGIC 0 + +extern const char libtar_version[]; + + +/* open a new tarfile handle */ +int tar_open(TAR **t, const char *pathname, tartype_t *type, + int oflags, int mode, int options); + +/* make a tarfile handle out of a previously-opened descriptor */ +int tar_fdopen(TAR **t, int fd, const char *pathname, tartype_t *type, + int oflags, int mode, int options); + +/* returns the descriptor associated with t */ +int tar_fd(TAR *t); + +/* close tarfile handle */ +int tar_close(TAR *t); + + +/***** append.c ************************************************************/ + +/* forward declaration to appease the compiler */ +struct tar_dev; + +/* cleanup function */ +void tar_dev_free(struct tar_dev *tdp); + +/* Appends a file to the tar archive. + * Arguments: + * t = TAR handle to append to + * realname = path of file to append + * savename = name to save the file under in the archive + */ +int tar_append_file(TAR *t, const char *realname, const char *savename); + +/* write EOF indicator */ +int tar_append_eof(TAR *t); + +/* add file contents to a tarchive */ +int tar_append_regfile(TAR *t, const char *realname); + + +/***** block.c *************************************************************/ + +/* macros for reading/writing tarchive blocks */ +#define tar_block_read(t, buf) \ + (*((t)->type->readfunc))((t)->fd, (char *)(buf), T_BLOCKSIZE) +#define tar_block_write(t, buf) \ + (*((t)->type->writefunc))((t)->fd, (char *)(buf), T_BLOCKSIZE) + +/* read/write a header block */ +int th_read(TAR *t); +int th_write(TAR *t); + + +/***** decode.c ************************************************************/ + +/* determine file type */ +#define TH_ISREG(t) ((t)->th_buf.typeflag == REGTYPE \ + || (t)->th_buf.typeflag == AREGTYPE \ + || (t)->th_buf.typeflag == CONTTYPE \ + || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode)) \ + && (t)->th_buf.typeflag != LNKTYPE)) +#define TH_ISLNK(t) ((t)->th_buf.typeflag == LNKTYPE) +#define TH_ISSYM(t) ((t)->th_buf.typeflag == SYMTYPE \ + || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode))) +#define TH_ISCHR(t) ((t)->th_buf.typeflag == CHRTYPE \ + || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode))) +#define TH_ISBLK(t) ((t)->th_buf.typeflag == BLKTYPE \ + || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode))) +#define TH_ISDIR(t) ((t)->th_buf.typeflag == DIRTYPE \ + || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode)) \ + || ((t)->th_buf.typeflag == AREGTYPE \ + && ((t)->th_buf.name[strlen((t)->th_buf.name) - 1] == '/'))) +#define TH_ISFIFO(t) ((t)->th_buf.typeflag == FIFOTYPE \ + || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode))) +#define TH_ISLONGNAME(t) ((t)->th_buf.typeflag == GNU_LONGNAME_TYPE) +#define TH_ISLONGLINK(t) ((t)->th_buf.typeflag == GNU_LONGLINK_TYPE) + +/* decode tar header info */ +#define th_get_crc(t) oct_to_int((t)->th_buf.chksum) +#define th_get_size(t) oct_to_int((t)->th_buf.size) +#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime) +#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor) +#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor) +#define th_get_linkname(t) ((t)->th_buf.gnu_longlink \ + ? (t)->th_buf.gnu_longlink \ + : (t)->th_buf.linkname) +char *th_get_pathname(TAR *t); +mode_t th_get_mode(TAR *t); +uid_t th_get_uid(TAR *t); +gid_t th_get_gid(TAR *t); + + +/***** encode.c ************************************************************/ + +/* encode file info in th_header */ +void th_set_type(TAR *t, mode_t mode); +void th_set_path(TAR *t, const char *pathname); +void th_set_link(TAR *t, const char *linkname); +void th_set_device(TAR *t, dev_t device); +void th_set_user(TAR *t, uid_t uid); +void th_set_group(TAR *t, gid_t gid); +void th_set_mode(TAR *t, mode_t fmode); +#define th_set_mtime(t, fmtime) \ + int_to_oct_nonull((fmtime), (t)->th_buf.mtime, 12) +#define th_set_size(t, fsize) \ + int_to_oct_nonull((fsize), (t)->th_buf.size, 12) + +/* encode everything at once (except the pathname and linkname) */ +void th_set_from_stat(TAR *t, struct stat *s); + +/* encode magic, version, and crc - must be done after everything else is set */ +void th_finish(TAR *t); + + +/***** extract.c ***********************************************************/ + +/* sequentially extract next file from t */ +int tar_extract_file(TAR *t, char *realname); + +/* extract different file types */ +int tar_extract_dir(TAR *t, char *realname); +int tar_extract_hardlink(TAR *t, char *realname); +int tar_extract_symlink(TAR *t, char *realname); +int tar_extract_chardev(TAR *t, char *realname); +int tar_extract_blockdev(TAR *t, char *realname); +int tar_extract_fifo(TAR *t, char *realname); + +/* for regfiles, we need to extract the content blocks as well */ +int tar_extract_regfile(TAR *t, char *realname); +int tar_skip_regfile(TAR *t); + + +/***** output.c ************************************************************/ + +/* print the tar header */ +void th_print(TAR *t); + +/* print "ls -l"-like output for the file described by th */ +void th_print_long_ls(TAR *t); + + +/***** util.c *************************************************************/ + +/* hashing function for pathnames */ +int path_hashfunc(char *key, int numbuckets); + +/* matching function for dev_t's */ +int dev_match(dev_t *dev1, dev_t *dev2); + +/* matching function for ino_t's */ +int ino_match(ino_t *ino1, ino_t *ino2); + +/* hashing function for dev_t's */ +int dev_hash(dev_t *dev); + +/* hashing function for ino_t's */ +int ino_hash(ino_t *inode); + +/* create any necessary dirs */ +int mkdirhier(char *path); + +/* calculate header checksum */ +int th_crc_calc(TAR *t); + +/* calculate a signed header checksum */ +int th_signed_crc_calc(TAR *t); + +/* compare checksums in a forgiving way */ +#define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t) || th_get_crc(t) == th_signed_crc_calc(t)) + +/* string-octal to integer conversion */ +int oct_to_int(char *oct); + +/* integer to NULL-terminated string-octal conversion */ +#define int_to_oct(num, oct, octlen) \ + snprintf((oct), (octlen), "%*lo ", (octlen) - 2, (unsigned long)(num)) + +/* integer to string-octal conversion, no NULL */ +void int_to_oct_nonull(int num, char *oct, size_t octlen); + + +/***** wrapper.c **********************************************************/ + +/* extract groups of files */ +int tar_extract_glob(TAR *t, char *globname, char *prefix); +int tar_extract_all(TAR *t, char *prefix); + +/* add a whole tree of files */ +int tar_append_tree(TAR *t, char *realdir, char *savedir); + + +#ifdef __cplusplus +} +#endif + +#endif /* ! LIBTAR_H */ + diff --git a/lib/output.c b/lib/output.c new file mode 100644 index 0000000..a2db929 --- /dev/null +++ b/lib/output.c @@ -0,0 +1,134 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** output.c - libtar code to print out tar header blocks +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + + +#ifndef _POSIX_LOGIN_NAME_MAX +# define _POSIX_LOGIN_NAME_MAX 9 +#endif + + +void +th_print(TAR *t) +{ + puts("\nPrinting tar header:"); + printf(" name = \"%.100s\"\n", t->th_buf.name); + printf(" mode = \"%.8s\"\n", t->th_buf.mode); + printf(" uid = \"%.8s\"\n", t->th_buf.uid); + printf(" gid = \"%.8s\"\n", t->th_buf.gid); + printf(" size = \"%.12s\"\n", t->th_buf.size); + printf(" mtime = \"%.12s\"\n", t->th_buf.mtime); + printf(" chksum = \"%.8s\"\n", t->th_buf.chksum); + printf(" typeflag = \'%c\'\n", t->th_buf.typeflag); + printf(" linkname = \"%.100s\"\n", t->th_buf.linkname); + printf(" magic = \"%.6s\"\n", t->th_buf.magic); + /*printf(" version = \"%.2s\"\n", t->th_buf.version); */ + printf(" version[0] = \'%c\',version[1] = \'%c\'\n", + t->th_buf.version[0], t->th_buf.version[1]); + printf(" uname = \"%.32s\"\n", t->th_buf.uname); + printf(" gname = \"%.32s\"\n", t->th_buf.gname); + printf(" devmajor = \"%.8s\"\n", t->th_buf.devmajor); + printf(" devminor = \"%.8s\"\n", t->th_buf.devminor); + printf(" prefix = \"%.155s\"\n", t->th_buf.prefix); + printf(" padding = \"%.12s\"\n", t->th_buf.padding); + printf(" gnu_longname = \"%s\"\n", + (t->th_buf.gnu_longname ? t->th_buf.gnu_longname : "[NULL]")); + printf(" gnu_longlink = \"%s\"\n", + (t->th_buf.gnu_longlink ? t->th_buf.gnu_longlink : "[NULL]")); +} + + +void +th_print_long_ls(TAR *t) +{ + char modestring[12]; + struct passwd *pw; + struct group *gr; + uid_t uid; + gid_t gid; + char username[_POSIX_LOGIN_NAME_MAX]; + char groupname[_POSIX_LOGIN_NAME_MAX]; + time_t mtime; + struct tm *mtm; + +#ifdef HAVE_STRFTIME + char timebuf[18]; +#else + const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; +#endif + + uid = th_get_uid(t); + pw = getpwuid(uid); + if (pw == NULL) + snprintf(username, sizeof(username), "%d", uid); + else + strlcpy(username, pw->pw_name, sizeof(username)); + + gid = th_get_gid(t); + gr = getgrgid(gid); + if (gr == NULL) + snprintf(groupname, sizeof(groupname), "%d", gid); + else + strlcpy(groupname, gr->gr_name, sizeof(groupname)); + + strmode(th_get_mode(t), modestring); + printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname); + + if (TH_ISCHR(t) || TH_ISBLK(t)) + printf(" %3d, %3d ", th_get_devmajor(t), th_get_devminor(t)); + else + printf("%9ld ", (long)th_get_size(t)); + + mtime = th_get_mtime(t); + mtm = localtime(&mtime); +#ifdef HAVE_STRFTIME + strftime(timebuf, sizeof(timebuf), "%h %e %H:%M %Y", mtm); + printf("%s", timebuf); +#else + printf("%.3s %2d %2d:%02d %4d", + months[mtm->tm_mon], + mtm->tm_mday, mtm->tm_hour, mtm->tm_min, mtm->tm_year + 1900); +#endif + + printf(" %s", th_get_pathname(t)); + + if (TH_ISSYM(t) || TH_ISLNK(t)) + { + if (TH_ISSYM(t)) + printf(" -> "); + else + printf(" link to "); + if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL) + printf("%s", t->th_buf.gnu_longlink); + else + printf("%.100s", t->th_buf.linkname); + } + + putchar('\n'); +} + + diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 0000000..31e8315 --- /dev/null +++ b/lib/util.c @@ -0,0 +1,165 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** util.c - miscellaneous utility code for libtar +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + + +/* hashing function for pathnames */ +int +path_hashfunc(char *key, int numbuckets) +{ + char buf[MAXPATHLEN]; + char *p; + + strcpy(buf, key); + p = basename(buf); + + return (((unsigned int)p[0]) % numbuckets); +} + + +/* matching function for dev_t's */ +int +dev_match(dev_t *dev1, dev_t *dev2) +{ + return !memcmp(dev1, dev2, sizeof(dev_t)); +} + + +/* matching function for ino_t's */ +int +ino_match(ino_t *ino1, ino_t *ino2) +{ + return !memcmp(ino1, ino2, sizeof(ino_t)); +} + + +/* hashing function for dev_t's */ +int +dev_hash(dev_t *dev) +{ + return *dev % 16; +} + + +/* hashing function for ino_t's */ +int +ino_hash(ino_t *inode) +{ + return *inode % 256; +} + + +/* +** mkdirhier() - create all directories in a given path +** returns: +** 0 success +** 1 all directories already exist +** -1 (and sets errno) error +*/ +int +mkdirhier(char *path) +{ + char src[MAXPATHLEN], dst[MAXPATHLEN] = ""; + char *dirp, *nextp = src; + int retval = 1; + + if (strlcpy(src, path, sizeof(src)) > sizeof(src)) + { + errno = ENAMETOOLONG; + return -1; + } + + if (path[0] == '/') + strcpy(dst, "/"); + + while ((dirp = strsep(&nextp, "/")) != NULL) + { + if (*dirp == '\0') + continue; + + if (dst[0] != '\0') + strcat(dst, "/"); + strcat(dst, dirp); + + if (mkdir(dst, 0777) == -1) + { + if (errno != EEXIST) + return -1; + } + else + retval = 0; + } + + return retval; +} + + +/* calculate header checksum */ +int +th_crc_calc(TAR *t) +{ + int i, sum = 0; + + for (i = 0; i < T_BLOCKSIZE; i++) + sum += ((unsigned char *)(&(t->th_buf)))[i]; + for (i = 0; i < 8; i++) + sum += (' ' - (unsigned char)t->th_buf.chksum[i]); + + return sum; +} + + +/* calculate a signed header checksum */ +int +th_signed_crc_calc(TAR *t) +{ + int i, sum = 0; + + for (i = 0; i < T_BLOCKSIZE; i++) + sum += ((signed char *)(&(t->th_buf)))[i]; + for (i = 0; i < 8; i++) + sum += (' ' - (signed char)t->th_buf.chksum[i]); + + return sum; +} + + +/* string-octal to integer conversion */ +int +oct_to_int(char *oct) +{ + int i; + + sscanf(oct, "%o", &i); + + return i; +} + + +/* integer to string-octal conversion, no NULL */ +void +int_to_oct_nonull(int num, char *oct, size_t octlen) +{ + snprintf(oct, octlen, "%*lo", octlen - 1, (unsigned long)num); + oct[octlen - 1] = ' '; +} + + diff --git a/lib/wrapper.c b/lib/wrapper.c new file mode 100644 index 0000000..4cd0652 --- /dev/null +++ b/lib/wrapper.c @@ -0,0 +1,152 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** wrapper.c - libtar high-level wrapper code +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include + +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + + +int +tar_extract_glob(TAR *t, char *globname, char *prefix) +{ + char *filename; + char buf[MAXPATHLEN]; + int i; + + while ((i = th_read(t)) == 0) + { + filename = th_get_pathname(t); + if (fnmatch(globname, filename, FNM_PATHNAME | FNM_PERIOD)) + { + if (TH_ISREG(t) && tar_skip_regfile(t)) + return -1; + continue; + } + if (t->options & TAR_VERBOSE) + th_print_long_ls(t); + if (prefix != NULL) + snprintf(buf, sizeof(buf), "%s/%s", prefix, filename); + else + strlcpy(buf, filename, sizeof(buf)); + if (tar_extract_file(t, buf) != 0) + return -1; + } + + return (i == 1 ? 0 : -1); +} + + +int +tar_extract_all(TAR *t, char *prefix) +{ + char *filename; + char buf[MAXPATHLEN]; + int i; + +#ifdef DEBUG + printf("==> tar_extract_all(TAR *t, \"%s\")\n", + (prefix ? prefix : "(null)")); +#endif + + while ((i = th_read(t)) == 0) + { +#ifdef DEBUG + puts(" tar_extract_all(): calling th_get_pathname()"); +#endif + filename = th_get_pathname(t); + if (t->options & TAR_VERBOSE) + th_print_long_ls(t); + if (prefix != NULL) + snprintf(buf, sizeof(buf), "%s/%s", prefix, filename); + else + strlcpy(buf, filename, sizeof(buf)); +#ifdef DEBUG + printf(" tar_extract_all(): calling tar_extract_file(t, " + "\"%s\")\n", buf); +#endif + if (tar_extract_file(t, buf) != 0) + return -1; + } + + return (i == 1 ? 0 : -1); +} + + +int +tar_append_tree(TAR *t, char *realdir, char *savedir) +{ + char realpath[MAXPATHLEN]; + char savepath[MAXPATHLEN]; + struct dirent *dent; + DIR *dp; + struct stat s; + +#ifdef DEBUG + printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n", + t, realdir, (savedir ? savedir : "[NULL]")); +#endif + + if (tar_append_file(t, realdir, savedir) != 0) + return -1; + +#ifdef DEBUG + puts(" tar_append_tree(): done with tar_append_file()..."); +#endif + + dp = opendir(realdir); + if (dp == NULL) + { + if (errno == ENOTDIR) + return 0; + return -1; + } + while ((dent = readdir(dp)) != NULL) + { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + snprintf(realpath, MAXPATHLEN, "%s/%s", realdir, + dent->d_name); + if (savedir) + snprintf(savepath, MAXPATHLEN, "%s/%s", savedir, + dent->d_name); + + if (lstat(realpath, &s) != 0) + return -1; + + if (S_ISDIR(s.st_mode)) + { + if (tar_append_tree(t, realpath, + (savedir ? savepath : NULL)) != 0) + return -1; + continue; + } + + if (tar_append_file(t, realpath, + (savedir ? savepath : NULL)) != 0) + return -1; + } + + closedir(dp); + + return 0; +} + + diff --git a/libtar/Makefile.in b/libtar/Makefile.in new file mode 100644 index 0000000..7c6f1bc --- /dev/null +++ b/libtar/Makefile.in @@ -0,0 +1,78 @@ +# @configure_input@ + +### Path settings +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +includedir = @includedir@ +datarootdir = @datarootdir@ + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +@ENCAP_DEFS@ + +SHELL = @SHELL@ + +### Installation programs and flags +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s +INSTALL_DATA = @INSTALL_DATA@ +LN_S = @LN_S@ +MKDIR = @MKDIR@ + +### Compiler and link options +CC = @CC@ +LIBTOOL = @LIBTOOL@ +CPPFLAGS = -I.. \ + -I../lib \ + -I../listhash \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/compat \ + @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ -ltar +LIBOBJS = @LIBOBJS@ +RANLIB = @RANLIB@ +@SET_MAKE@ +VPATH = @srcdir@ + + +### Makefile rules - no user-servicable parts below + +LIBTAR_OBJS = libtar.lo +LIBTAR_HDRS = ../config.h \ + ${top_srcdir}/compat/compat.h \ + ${top_srcdir}/lib/libtar.h \ + ../listhash/libtar_listhash.h +LIBTAR_LIBS = $(top_builddir)/lib +ALL = libtar + + +all: ${ALL} + +.PHONY: clean distclean install + +libtar: ${LIBTAR_OBJS} ${LIBTAR_LIBS} ${LIBTAR_HDRS} + $(LIBTOOL) --mode=link ${CC} ${CFLAGS} ${LDFLAGS} -o libtar $(LIBTAR_OBJS) -L${LIBTAR_LIBS} ${LIBS} + +${LIBTAR_OBJS}: ${LIBTAR_HDRS} + +%.lo: %.c + $(LIBTOOL) --mode=compile ${CC} ${CFLAGS} ${CPPFLAGS} -c -o $@ $< + +clean: + rm -rf *~ *.o ${ALL} $(LIBTAR_OBJS) core .libs + +distclean: clean + rm -f Makefile + +install: ${ALL} + ${MKDIR} ${DESTDIR}${bindir} + $(LIBTOOL) --mode=install ${INSTALL_PROGRAM} libtar ${DESTDIR}${bindir} + diff --git a/libtar/libtar.c b/libtar/libtar.c new file mode 100644 index 0000000..9fa92b2 --- /dev/null +++ b/libtar/libtar.c @@ -0,0 +1,368 @@ +/* +** Copyright 1998-2003 University of Illinois Board of Trustees +** Copyright 1998-2003 Mark D. Roth +** All rights reserved. +** +** libtar.c - demo driver program for libtar +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef DEBUG +# include +#endif + +#ifdef HAVE_LIBZ +# include +#endif + +#include + + +char *progname; +int verbose = 0; +int use_gnu = 0; + +#ifdef DEBUG +void +segv_handler(int sig) +{ + puts("OOPS! Caught SIGSEGV, bailing out..."); + fflush(stdout); + fflush(stderr); +} +#endif + + +#ifdef HAVE_LIBZ + +int use_zlib = 0; + +int +gzopen_frontend(char *pathname, int oflags, int mode) +{ + char *gzoflags; + gzFile gzf; + int fd; + + switch (oflags & O_ACCMODE) + { + case O_WRONLY: + gzoflags = "wb"; + break; + case O_RDONLY: + gzoflags = "rb"; + break; + default: + case O_RDWR: + errno = EINVAL; + return -1; + } + + fd = open(pathname, oflags, mode); + if (fd == -1) + return -1; + + if ((oflags & O_CREAT) && fchmod(fd, mode)) + return -1; + + gzf = gzdopen(fd, gzoflags); + if (!gzf) + { + errno = ENOMEM; + return -1; + } + + /* This is a bad thing to do on big-endian lp64 systems, where the + size and placement of integers is different than pointers. + However, to fix the problem 4 wrapper functions would be needed and + an extra bit of data associating GZF with the wrapper functions. */ + return (int)gzf; +} + +tartype_t gztype = { (openfunc_t) gzopen_frontend, (closefunc_t) gzclose, + (readfunc_t) gzread, (writefunc_t) gzwrite +}; + +#endif /* HAVE_LIBZ */ + + +int +create(char *tarfile, char *rootdir, libtar_list_t *l) +{ + TAR *t; + char *pathname; + char buf[MAXPATHLEN]; + libtar_listptr_t lp; + + if (tar_open(&t, tarfile, +#ifdef HAVE_LIBZ + (use_zlib ? &gztype : NULL), +#else + NULL, +#endif + O_WRONLY | O_CREAT, 0644, + (verbose ? TAR_VERBOSE : 0) + | (use_gnu ? TAR_GNU : 0)) == -1) + { + fprintf(stderr, "tar_open(): %s\n", strerror(errno)); + return -1; + } + + libtar_listptr_reset(&lp); + while (libtar_list_next(l, &lp) != 0) + { + pathname = (char *)libtar_listptr_data(&lp); + if (pathname[0] != '/' && rootdir != NULL) + snprintf(buf, sizeof(buf), "%s/%s", rootdir, pathname); + else + strlcpy(buf, pathname, sizeof(buf)); + if (tar_append_tree(t, buf, pathname) != 0) + { + fprintf(stderr, + "tar_append_tree(\"%s\", \"%s\"): %s\n", buf, + pathname, strerror(errno)); + tar_close(t); + return -1; + } + } + + if (tar_append_eof(t) != 0) + { + fprintf(stderr, "tar_append_eof(): %s\n", strerror(errno)); + tar_close(t); + return -1; + } + + if (tar_close(t) != 0) + { + fprintf(stderr, "tar_close(): %s\n", strerror(errno)); + return -1; + } + + return 0; +} + + +int +list(char *tarfile) +{ + TAR *t; + int i; + + if (tar_open(&t, tarfile, +#ifdef HAVE_LIBZ + (use_zlib ? &gztype : NULL), +#else + NULL, +#endif + O_RDONLY, 0, + (verbose ? TAR_VERBOSE : 0) + | (use_gnu ? TAR_GNU : 0)) == -1) + { + fprintf(stderr, "tar_open(): %s\n", strerror(errno)); + return -1; + } + + while ((i = th_read(t)) == 0) + { + th_print_long_ls(t); +#ifdef DEBUG + th_print(t); +#endif + if (TH_ISREG(t) && tar_skip_regfile(t) != 0) + { + fprintf(stderr, "tar_skip_regfile(): %s\n", + strerror(errno)); + return -1; + } + } + +#ifdef DEBUG + printf("th_read() returned %d\n", i); + printf("EOF mark encountered after %ld bytes\n", +# ifdef HAVE_LIBZ + (use_zlib + ? gzseek((gzFile) t->fd, 0, SEEK_CUR) + : +# endif + lseek(t->fd, 0, SEEK_CUR) +# ifdef HAVE_LIBZ + ) +# endif + ); +#endif + + if (tar_close(t) != 0) + { + fprintf(stderr, "tar_close(): %s\n", strerror(errno)); + return -1; + } + + return 0; +} + + +int +extract(char *tarfile, char *rootdir) +{ + TAR *t; + +#ifdef DEBUG + puts("opening tarfile..."); +#endif + if (tar_open(&t, tarfile, +#ifdef HAVE_LIBZ + (use_zlib ? &gztype : NULL), +#else + NULL, +#endif + O_RDONLY, 0, + (verbose ? TAR_VERBOSE : 0) + | (use_gnu ? TAR_GNU : 0)) == -1) + { + fprintf(stderr, "tar_open(): %s\n", strerror(errno)); + return -1; + } + +#ifdef DEBUG + puts("extracting tarfile..."); +#endif + if (tar_extract_all(t, rootdir) != 0) + { + fprintf(stderr, "tar_extract_all(): %s\n", strerror(errno)); + return -1; + } + +#ifdef DEBUG + puts("closing tarfile..."); +#endif + if (tar_close(t) != 0) + { + fprintf(stderr, "tar_close(): %s\n", strerror(errno)); + return -1; + } + + return 0; +} + + +void +usage() +{ + printf("Usage: %s [-C rootdir] [-g] [-z] -x|-t filename.tar\n", + progname); + printf(" %s [-C rootdir] [-g] [-z] -c filename.tar ...\n", + progname); + exit(-1); +} + + +#define MODE_LIST 1 +#define MODE_CREATE 2 +#define MODE_EXTRACT 3 + +int +main(int argc, char *argv[]) +{ + char *tarfile = NULL; + char *rootdir = NULL; + int c; + int mode = 0; + libtar_list_t *l; + + progname = basename(argv[0]); + + while ((c = getopt(argc, argv, "cC:gtvVxz")) != -1) + switch (c) + { + case 'V': + printf("libtar %s by Mark D. Roth \n", + libtar_version); + break; + case 'C': + rootdir = strdup(optarg); + break; + case 'v': + verbose = 1; + break; + case 'g': + use_gnu = 1; + break; + case 'c': + if (mode) + usage(); + mode = MODE_CREATE; + break; + case 'x': + if (mode) + usage(); + mode = MODE_EXTRACT; + break; + case 't': + if (mode) + usage(); + mode = MODE_LIST; + break; +#ifdef HAVE_LIBZ + case 'z': + use_zlib = 1; + break; +#endif /* HAVE_LIBZ */ + default: + usage(); + } + + if (!mode || ((argc - optind) < (mode == MODE_CREATE ? 2 : 1))) + { +#ifdef DEBUG + printf("argc - optind == %d\tmode == %d\n", argc - optind, + mode); +#endif + usage(); + } + +#ifdef DEBUG + signal(SIGSEGV, segv_handler); +#endif + + switch (mode) + { + case MODE_EXTRACT: + return extract(argv[optind], rootdir); + case MODE_CREATE: + tarfile = argv[optind]; + l = libtar_list_new(LIST_QUEUE, NULL); + for (c = optind + 1; c < argc; c++) + libtar_list_add(l, argv[c]); + return create(tarfile, rootdir, l); + case MODE_LIST: + return list(argv[optind]); + default: + break; + } + + /* NOTREACHED */ + return -2; +} + + diff --git a/listhash/ChangeLog b/listhash/ChangeLog new file mode 100644 index 0000000..cde7675 --- /dev/null +++ b/listhash/ChangeLog @@ -0,0 +1,15 @@ +2002-12-09 added list_empty() and hash_empty() functions + +2002-09-12 fixed list_iterate function to return -1 if it gets + an invalid argument + + include and from source files, not + from header file, since header file is sometimes + installed as part of a user-visible API + (those APIs should eventually be redesigned without the + listhash code being publicly visible, but for now we + need to accomodate this) + +2002-07-07 modified list iterate function to return int + (returns -1 if plugin function returns -1) + diff --git a/listhash/TODO b/listhash/TODO new file mode 100644 index 0000000..f2f859f --- /dev/null +++ b/listhash/TODO @@ -0,0 +1,21 @@ +Functionality: +-------------- + +* add list mode to allow nodes to be inserted in any arbitrary location +* add "*_hash_iterate()" function +* add flags argument to *_list_del() that allows the listptr to be set + to the previous or next element +* add a generic pointer type to replace *_listptr_t and *_hashptr_t ??? + + +Code Cleanup: +------------- + +* rename functions: + *_list_next => *_listptr_next() + *_list_prev => *_listptr_prev() + *_hash_next => *_hashptr_next() +* start using "*_list_t" and "*_hash_t" instead of "*_list_t *" and + "*_hash_t *" ? +* add prefixes to structure member field names + diff --git a/listhash/hash.c.in b/listhash/hash.c.in new file mode 100644 index 0000000..2736f54 --- /dev/null +++ b/listhash/hash.c.in @@ -0,0 +1,344 @@ +/* @configure_input@ */ + +/* +** Copyright 1998-2002 University of Illinois Board of Trustees +** Copyright 1998-2002 Mark D. Roth +** All rights reserved. +** +** @LISTHASH_PREFIX@_hash.c - hash table routines +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include + +#include <@LISTHASH_PREFIX@_listhash.h> + +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + + +/* +** @LISTHASH_PREFIX@_hashptr_reset() - reset a hash pointer +*/ +void +@LISTHASH_PREFIX@_hashptr_reset(@LISTHASH_PREFIX@_hashptr_t *hp) +{ + @LISTHASH_PREFIX@_listptr_reset(&(hp->node)); + hp->bucket = -1; +} + + +/* +** @LISTHASH_PREFIX@_hashptr_data() - retrieve the data being pointed to +*/ +void * +@LISTHASH_PREFIX@_hashptr_data(@LISTHASH_PREFIX@_hashptr_t *hp) +{ + return @LISTHASH_PREFIX@_listptr_data(&(hp->node)); +} + + +/* +** @LISTHASH_PREFIX@_str_hashfunc() - default hash function, optimized for +** 7-bit strings +*/ +unsigned int +@LISTHASH_PREFIX@_str_hashfunc(char *key, unsigned int num_buckets) +{ +#if 0 + register unsigned result = 0; + register int i; + + if (key == NULL) + return 0; + + for (i = 0; *key != '\0' && i < 32; i++) + result = result * 33U + *key++; + + return (result % num_buckets); +#else + if (key == NULL) + return 0; + + return (key[0] % num_buckets); +#endif +} + + +/* +** @LISTHASH_PREFIX@_hash_nents() - return number of elements from hash +*/ +unsigned int +@LISTHASH_PREFIX@_hash_nents(@LISTHASH_PREFIX@_hash_t *h) +{ + return h->nents; +} + + +/* +** @LISTHASH_PREFIX@_hash_new() - create a new hash +*/ +@LISTHASH_PREFIX@_hash_t * +@LISTHASH_PREFIX@_hash_new(int num, @LISTHASH_PREFIX@_hashfunc_t hashfunc) +{ + @LISTHASH_PREFIX@_hash_t *hash; + + hash = (@LISTHASH_PREFIX@_hash_t *)calloc(1, sizeof(@LISTHASH_PREFIX@_hash_t)); + if (hash == NULL) + return NULL; + hash->numbuckets = num; + if (hashfunc != NULL) + hash->hashfunc = hashfunc; + else + hash->hashfunc = (@LISTHASH_PREFIX@_hashfunc_t)@LISTHASH_PREFIX@_str_hashfunc; + + hash->table = (@LISTHASH_PREFIX@_list_t **)calloc(num, sizeof(@LISTHASH_PREFIX@_list_t *)); + if (hash->table == NULL) + { + free(hash); + return NULL; + } + + return hash; +} + + +/* +** @LISTHASH_PREFIX@_hash_next() - get next element in hash +** returns: +** 1 data found +** 0 end of list +*/ +int +@LISTHASH_PREFIX@_hash_next(@LISTHASH_PREFIX@_hash_t *h, + @LISTHASH_PREFIX@_hashptr_t *hp) +{ +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_hash_next(h=0x%lx, hp={%d,0x%lx})\n", + h, hp->bucket, hp->node); +#endif + + if (hp->bucket >= 0 && hp->node != NULL && + @LISTHASH_PREFIX@_list_next(h->table[hp->bucket], &(hp->node)) != 0) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_next(): found additional " + "data in current bucket (%d), returing 1\n", + hp->bucket); +#endif + return 1; + } + +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_next(): done with bucket %d\n", + hp->bucket); +#endif + + for (hp->bucket++; hp->bucket < h->numbuckets; hp->bucket++) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_next(): " + "checking bucket %d\n", hp->bucket); +#endif + hp->node = NULL; + if (h->table[hp->bucket] != NULL && + @LISTHASH_PREFIX@_list_next(h->table[hp->bucket], + &(hp->node)) != 0) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_next(): " + "found data in bucket %d, returing 1\n", + hp->bucket); +#endif + return 1; + } + } + + if (hp->bucket == h->numbuckets) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_next(): hash pointer " + "wrapped to 0\n"); +#endif + hp->bucket = -1; + hp->node = NULL; + } + +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_hash_next(): no more data, " + "returning 0\n"); +#endif + return 0; +} + + +/* +** @LISTHASH_PREFIX@_hash_del() - delete an entry from the hash +** returns: +** 0 success +** -1 (and sets errno) failure +*/ +int +@LISTHASH_PREFIX@_hash_del(@LISTHASH_PREFIX@_hash_t *h, + @LISTHASH_PREFIX@_hashptr_t *hp) +{ + if (hp->bucket < 0 + || hp->bucket >= h->numbuckets + || h->table[hp->bucket] == NULL + || hp->node == NULL) + { + errno = EINVAL; + return -1; + } + + @LISTHASH_PREFIX@_list_del(h->table[hp->bucket], &(hp->node)); + h->nents--; + return 0; +} + + +/* +** @LISTHASH_PREFIX@_hash_empty() - empty the hash +*/ +void +@LISTHASH_PREFIX@_hash_empty(@LISTHASH_PREFIX@_hash_t *h, @LISTHASH_PREFIX@_freefunc_t freefunc) +{ + int i; + + for (i = 0; i < h->numbuckets; i++) + if (h->table[i] != NULL) + @LISTHASH_PREFIX@_list_empty(h->table[i], freefunc); + + h->nents = 0; +} + + +/* +** @LISTHASH_PREFIX@_hash_free() - delete all of the nodes in the hash +*/ +void +@LISTHASH_PREFIX@_hash_free(@LISTHASH_PREFIX@_hash_t *h, @LISTHASH_PREFIX@_freefunc_t freefunc) +{ + int i; + + for (i = 0; i < h->numbuckets; i++) + if (h->table[i] != NULL) + @LISTHASH_PREFIX@_list_free(h->table[i], freefunc); + + free(h->table); + free(h); +} + + +/* +** @LISTHASH_PREFIX@_hash_search() - iterative search for an element in a hash +** returns: +** 1 match found +** 0 no match +*/ +int +@LISTHASH_PREFIX@_hash_search(@LISTHASH_PREFIX@_hash_t *h, + @LISTHASH_PREFIX@_hashptr_t *hp, void *data, + @LISTHASH_PREFIX@_matchfunc_t matchfunc) +{ + while (@LISTHASH_PREFIX@_hash_next(h, hp) != 0) + if ((*matchfunc)(data, @LISTHASH_PREFIX@_listptr_data(&(hp->node))) != 0) + return 1; + + return 0; +} + + +/* +** @LISTHASH_PREFIX@_hash_getkey() - hash-based search for an element in a hash +** returns: +** 1 match found +** 0 no match +*/ +int +@LISTHASH_PREFIX@_hash_getkey(@LISTHASH_PREFIX@_hash_t *h, + @LISTHASH_PREFIX@_hashptr_t *hp, void *key, + @LISTHASH_PREFIX@_matchfunc_t matchfunc) +{ +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_hash_getkey(h=0x%lx, hp={%d,0x%lx}, " + "key=0x%lx, matchfunc=0x%lx)\n", + h, hp->bucket, hp->node, key, matchfunc); +#endif + + if (hp->bucket == -1) + { + hp->bucket = (*(h->hashfunc))(key, h->numbuckets); +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_getkey(): hp->bucket " + "set to %d\n", hp->bucket); +#endif + } + + if (h->table[hp->bucket] == NULL) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_getkey(): no list " + "for bucket %d, returning 0\n", hp->bucket); +#endif + hp->bucket = -1; + return 0; + } + +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_hash_getkey(): " + "returning @LISTHASH_PREFIX@_list_search()\n"); +#endif + return @LISTHASH_PREFIX@_list_search(h->table[hp->bucket], &(hp->node), + key, matchfunc); +} + + +/* +** @LISTHASH_PREFIX@_hash_add() - add an element to the hash +** returns: +** 0 success +** -1 (and sets errno) failure +*/ +int +@LISTHASH_PREFIX@_hash_add(@LISTHASH_PREFIX@_hash_t *h, void *data) +{ + int bucket, i; + +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_hash_add(h=0x%lx, data=0x%lx)\n", + h, data); +#endif + + bucket = (*(h->hashfunc))(data, h->numbuckets); +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_add(): inserting in bucket %d\n", + bucket); +#endif + if (h->table[bucket] == NULL) + { +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_hash_add(): creating new list\n"); +#endif + h->table[bucket] = @LISTHASH_PREFIX@_list_new(LIST_QUEUE, NULL); + } + +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_hash_add(): " + "returning @LISTHASH_PREFIX@_list_add()\n"); +#endif + i = @LISTHASH_PREFIX@_list_add(h->table[bucket], data); + if (i == 0) + h->nents++; + return i; +} + + diff --git a/listhash/hash_new.3.in b/listhash/hash_new.3.in new file mode 100644 index 0000000..22bfde5 --- /dev/null +++ b/listhash/hash_new.3.in @@ -0,0 +1,74 @@ +.TH @LISTHASH_PREFIX@_hash_new 3 "Jan 2000" "University of Illinois" "C Library Calls" +\" @configure_input@ +.SH NAME +@LISTHASH_PREFIX@_hash_new, @LISTHASH_PREFIX@_hash_free, @LISTHASH_PREFIX@_hash_next, +@LISTHASH_PREFIX@_hash_prev, @LISTHASH_PREFIX@_hash_getkey, @LISTHASH_PREFIX@_hash_search, +@LISTHASH_PREFIX@_hash_add, @LISTHASH_PREFIX@_hash_del \- hash table routines +.SH SYNOPSIS +.B #include <@LISTHASH_PREFIX@.h> +.P +.BI "@LISTHASH_PREFIX@_hash_t *@LISTHASH_PREFIX@_hash_new(int " num ", int (*" hashfunc ")());" +.br +.BI "void @LISTHASH_PREFIX@_hash_free(@LISTHASH_PREFIX@_hash_t *" h ", void (*" freefunc ")());" +.br +.BI "int @LISTHASH_PREFIX@_hash_next(@LISTHASH_PREFIX@_hash_t *" h ", @LISTHASH_PREFIX@_hashptr_t *" hp ");" +.br +.BI "int @LISTHASH_PREFIX@_hash_prev(@LISTHASH_PREFIX@_hash_t *" h ", @LISTHASH_PREFIX@_hashptr_t *" hp ");" +.br +.BI "int @LISTHASH_PREFIX@_hash_search(@LISTHASH_PREFIX@_hash_t *" h ", @LISTHASH_PREFIX@_hashptr_t *" hp "," +.BI "void *" data ", int (*" matchfunc ")());" +.br +.BI "int @LISTHASH_PREFIX@_hash_getkey(@LISTHASH_PREFIX@_hash_t *" h ", @LISTHASH_PREFIX@_hashptr_t *" hp "," +.BI "void *" data ", int (*" matchfunc ")());" +.br +.BI "int @LISTHASH_PREFIX@_hash_add(@LISTHASH_PREFIX@_hash_t *" h ", void *" data ");" +.br +.BI "int @LISTHASH_PREFIX@_hash_del(@LISTHASH_PREFIX@_hash_t *" h ", @LISTHASH_PREFIX@_hashptr_t *" hp ");" +.SH DESCRIPTION +The \fB@LISTHASH_PREFIX@_hash_new\fP() function creates a new hash with \fInum\fP +buckets and using hash function pointed to by \fIhashfunc\fP. If +\fIhashfunc\fP is \fINULL\fP, a default hash function designed for +7-bit ASCII strings is used. + +The \fB@LISTHASH_PREFIX@_hash_free\fP() function deallocates all memory associated +with the hash structure \fIh\fP. If \fIfreefunc\fP is not \fINULL\fP, +it is called to free memory associated with each node in the hash. + +The \fB@LISTHASH_PREFIX@_hash_next\fP() and \fB@LISTHASH_PREFIX@_hash_prev\fP() functions are +used to iterate through the hash. The \fI@LISTHASH_PREFIX@_hashptr_t\fP structure +has two fields: \fIbucket\fP, which indicates the current bucket in the +hash, and \fInode\fP, which is a pointer to the current node in the current +bucket. To start at the beginning or end of the hash, the caller should +initialize \fIhp.bucket\fP to \-1 and \fIhp.node\fP to \fINULL\fP. + +The \fB@LISTHASH_PREFIX@_hash_search\fP() function searches iteratively through the +hash \fIh\fP until it finds a node whose contents match \fIdata\fP using +the matching function \fImatchfunc\fP. Searching begins at the location +pointed to by \fIhp\fP. + +The \fB@LISTHASH_PREFIX@_hash_getkey\fP() function uses the hash function associated +with \fIh\fP to determine which bucket \fIdata\fP should be in, and searches +only that bucket for a matching node using \fImatchfunc\fP. Searching +begins at the location pointed to by \fIhp\fP. + +The \fB@LISTHASH_PREFIX@_hash_add\fP() function adds \fIdata\fP into hash \fIh\fP. + +The \fB@LISTHASH_PREFIX@_hash_del\fP() function removes the node referenced by +\fIhp\fP. +.SH RETURN VALUE +The \fB@LISTHASH_PREFIX@_hash_new\fP() function returns a pointer to the new hash +structure, or \fINULL\fP on error. + +The \fB@LISTHASH_PREFIX@_hash_next\fP() and \fB@LISTHASH_PREFIX@_hash_prev\fP() functions +return 1 when valid data is returned, and 0 at the end of the hash. + +The \fB@LISTHASH_PREFIX@_hash_getkey\fP() and \fB@LISTHASH_PREFIX@_hash_search\fP() functions +return 1 when a match is found, or 0 otherwise. + +The \fB@LISTHASH_PREFIX@_hash_add\fP() function returns 0 on success, or \-1 on +error (and sets \fIerrno\fP). + +The \fB@LISTHASH_PREFIX@_hash_del\fP() function returns 0 on success, or \-1 on +error (and sets \fIerrno\fP). +.SH SEE ALSO +.BR @LISTHASH_PREFIX@_list_new (3) diff --git a/listhash/list.c.in b/listhash/list.c.in new file mode 100644 index 0000000..6d0449d --- /dev/null +++ b/listhash/list.c.in @@ -0,0 +1,458 @@ +/* @configure_input@ */ + +/* +** Copyright 1998-2002 University of Illinois Board of Trustees +** Copyright 1998-2002 Mark D. Roth +** All rights reserved. +** +** @LISTHASH_PREFIX@_list.c - linked list routines +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#include +#include + +#include <@LISTHASH_PREFIX@_listhash.h> + +#include +#include +#include + +#ifdef STDC_HEADERS +# include +# include +#endif + + +/* +** @LISTHASH_PREFIX@_listptr_reset() - reset a list pointer +*/ +void +@LISTHASH_PREFIX@_listptr_reset(@LISTHASH_PREFIX@_listptr_t *lp) +{ + *lp = NULL; +} + + +/* +** @LISTHASH_PREFIX@_listptr_data() - retrieve the data pointed to by lp +*/ +void * +@LISTHASH_PREFIX@_listptr_data(@LISTHASH_PREFIX@_listptr_t *lp) +{ + return (*lp)->data; +} + + +/* +** @LISTHASH_PREFIX@_list_new() - create a new, empty list +*/ +@LISTHASH_PREFIX@_list_t * +@LISTHASH_PREFIX@_list_new(int flags, @LISTHASH_PREFIX@_cmpfunc_t cmpfunc) +{ + @LISTHASH_PREFIX@_list_t *newlist; + +#ifdef DS_DEBUG + printf("in @LISTHASH_PREFIX@_list_new(%d, 0x%lx)\n", flags, cmpfunc); +#endif + + if (flags != LIST_USERFUNC + && flags != LIST_STACK + && flags != LIST_QUEUE) + { + errno = EINVAL; + return NULL; + } + + newlist = (@LISTHASH_PREFIX@_list_t *)calloc(1, sizeof(@LISTHASH_PREFIX@_list_t)); + if (cmpfunc != NULL) + newlist->cmpfunc = cmpfunc; + else + newlist->cmpfunc = (@LISTHASH_PREFIX@_cmpfunc_t)strcmp; + newlist->flags = flags; + + return newlist; +} + + +/* +** @LISTHASH_PREFIX@_list_iterate() - call a function for every element +** in a list +*/ +int +@LISTHASH_PREFIX@_list_iterate(@LISTHASH_PREFIX@_list_t *l, + @LISTHASH_PREFIX@_iterate_func_t plugin, + void *state) +{ + @LISTHASH_PREFIX@_listptr_t n; + + if (l == NULL) + return -1; + + for (n = l->first; n != NULL; n = n->next) + { + if ((*plugin)(n->data, state) == -1) + return -1; + } + + return 0; +} + + +/* +** @LISTHASH_PREFIX@_list_empty() - empty the list +*/ +void +@LISTHASH_PREFIX@_list_empty(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_freefunc_t freefunc) +{ + @LISTHASH_PREFIX@_listptr_t n; + + for (n = l->first; n != NULL; n = l->first) + { + l->first = n->next; + if (freefunc != NULL) + (*freefunc)(n->data); + free(n); + } + + l->nents = 0; +} + + +/* +** @LISTHASH_PREFIX@_list_free() - remove and free() the whole list +*/ +void +@LISTHASH_PREFIX@_list_free(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_freefunc_t freefunc) +{ + @LISTHASH_PREFIX@_list_empty(l, freefunc); + free(l); +} + + +/* +** @LISTHASH_PREFIX@_list_nents() - return number of elements in the list +*/ +unsigned int +@LISTHASH_PREFIX@_list_nents(@LISTHASH_PREFIX@_list_t *l) +{ + return l->nents; +} + + +/* +** @LISTHASH_PREFIX@_list_add() - adds an element to the list +** returns: +** 0 success +** -1 (and sets errno) failure +*/ +int +@LISTHASH_PREFIX@_list_add(@LISTHASH_PREFIX@_list_t *l, void *data) +{ + @LISTHASH_PREFIX@_listptr_t n, m; + +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_list_add(\"%s\")\n", (char *)data); +#endif + + n = (@LISTHASH_PREFIX@_listptr_t)malloc(sizeof(struct @LISTHASH_PREFIX@_node)); + if (n == NULL) + return -1; + n->data = data; + l->nents++; + +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_list_add(): allocated data\n"); +#endif + + /* if the list is empty */ + if (l->first == NULL) + { + l->last = l->first = n; + n->next = n->prev = NULL; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): list was empty; " + "added first element and returning 0\n"); +#endif + return 0; + } + +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_list_add(): list not empty\n"); +#endif + + if (l->flags == LIST_STACK) + { + n->prev = NULL; + n->next = l->first; + if (l->first != NULL) + l->first->prev = n; + l->first = n; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): LIST_STACK set; " + "added in front\n"); +#endif + return 0; + } + + if (l->flags == LIST_QUEUE) + { + n->prev = l->last; + n->next = NULL; + if (l->last != NULL) + l->last->next = n; + l->last = n; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): LIST_QUEUE set; " + "added at end\n"); +#endif + return 0; + } + + for (m = l->first; m != NULL; m = m->next) + if ((*(l->cmpfunc))(data, m->data) < 0) + { + /* + ** if we find one that's bigger, + ** insert data before it + */ +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_list_add(): gotcha..." + "inserting data\n"); +#endif + if (m == l->first) + { + l->first = n; + n->prev = NULL; + m->prev = n; + n->next = m; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): " + "added first, returning 0\n"); +#endif + return 0; + } + m->prev->next = n; + n->prev = m->prev; + m->prev = n; + n->next = m; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): added middle," + " returning 0\n"); +#endif + return 0; + } + +#ifdef DS_DEBUG + printf(" @LISTHASH_PREFIX@_list_add(): new data larger than current " + "list elements\n"); +#endif + + /* if we get here, data is bigger than everything in the list */ + l->last->next = n; + n->prev = l->last; + l->last = n; + n->next = NULL; +#ifdef DS_DEBUG + printf("<== @LISTHASH_PREFIX@_list_add(): added end, returning 0\n"); +#endif + return 0; +} + + +/* +** @LISTHASH_PREFIX@_list_del() - remove the element pointed to by n +** from the list l +*/ +void +@LISTHASH_PREFIX@_list_del(@LISTHASH_PREFIX@_list_t *l, @LISTHASH_PREFIX@_listptr_t *n) +{ + @LISTHASH_PREFIX@_listptr_t m; + +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_list_del()\n"); +#endif + + l->nents--; + + m = (*n)->next; + + if ((*n)->prev) + (*n)->prev->next = (*n)->next; + else + l->first = (*n)->next; + if ((*n)->next) + (*n)->next->prev = (*n)->prev; + else + l->last = (*n)->prev; + + free(*n); + *n = m; +} + + +/* +** @LISTHASH_PREFIX@_list_next() - get the next element in the list +** returns: +** 1 success +** 0 end of list +*/ +int +@LISTHASH_PREFIX@_list_next(@LISTHASH_PREFIX@_list_t *l, + @LISTHASH_PREFIX@_listptr_t *n) +{ + if (*n == NULL) + *n = l->first; + else + *n = (*n)->next; + + return (*n != NULL ? 1 : 0); +} + + +/* +** @LISTHASH_PREFIX@_list_prev() - get the previous element in the list +** returns: +** 1 success +** 0 end of list +*/ +int +@LISTHASH_PREFIX@_list_prev(@LISTHASH_PREFIX@_list_t *l, + @LISTHASH_PREFIX@_listptr_t *n) +{ + if (*n == NULL) + *n = l->last; + else + *n = (*n)->prev; + + return (*n != NULL ? 1 : 0); +} + + +/* +** @LISTHASH_PREFIX@_str_match() - string matching function +** returns: +** 1 match +** 0 no match +*/ +int +@LISTHASH_PREFIX@_str_match(char *check, char *data) +{ + return !strcmp(check, data); +} + + +/* +** @LISTHASH_PREFIX@_list_add_str() - splits string str into delim-delimited +** elements and adds them to list l +** returns: +** 0 success +** -1 (and sets errno) failure +*/ +int +@LISTHASH_PREFIX@_list_add_str(@LISTHASH_PREFIX@_list_t *l, + char *str, char *delim) +{ + char tmp[10240]; + char *tokp, *nextp = tmp; + + strlcpy(tmp, str, sizeof(tmp)); + while ((tokp = strsep(&nextp, delim)) != NULL) + { + if (*tokp == '\0') + continue; + if (@LISTHASH_PREFIX@_list_add(l, strdup(tokp))) + return -1; + } + + return 0; +} + + +/* +** @LISTHASH_PREFIX@_list_search() - find an entry in a list +** returns: +** 1 match found +** 0 no match +*/ +int +@LISTHASH_PREFIX@_list_search(@LISTHASH_PREFIX@_list_t *l, + @LISTHASH_PREFIX@_listptr_t *n, void *data, + @LISTHASH_PREFIX@_matchfunc_t matchfunc) +{ +#ifdef DS_DEBUG + printf("==> @LISTHASH_PREFIX@_list_search(l=0x%lx, n=0x%lx, \"%s\")\n", + l, n, (char *)data); +#endif + + if (matchfunc == NULL) + matchfunc = (@LISTHASH_PREFIX@_matchfunc_t)@LISTHASH_PREFIX@_str_match; + + if (*n == NULL) + *n = l->first; + else + *n = (*n)->next; + + for (; *n != NULL; *n = (*n)->next) + { +#ifdef DS_DEBUG + printf("checking against \"%s\"\n", (char *)(*n)->data); +#endif + if ((*(matchfunc))(data, (*n)->data) != 0) + return 1; + } + +#ifdef DS_DEBUG + printf("no matches found\n"); +#endif + return 0; +} + + +/* +** @LISTHASH_PREFIX@_list_dup() - copy an existing list +*/ +@LISTHASH_PREFIX@_list_t * +@LISTHASH_PREFIX@_list_dup(@LISTHASH_PREFIX@_list_t *l) +{ + @LISTHASH_PREFIX@_list_t *newlist; + @LISTHASH_PREFIX@_listptr_t n; + + newlist = @LISTHASH_PREFIX@_list_new(l->flags, l->cmpfunc); + for (n = l->first; n != NULL; n = n->next) + @LISTHASH_PREFIX@_list_add(newlist, n->data); + +#ifdef DS_DEBUG + printf("returning from @LISTHASH_PREFIX@_list_dup()\n"); +#endif + return newlist; +} + + +/* +** @LISTHASH_PREFIX@_list_merge() - merge two lists into a new list +*/ +@LISTHASH_PREFIX@_list_t * +@LISTHASH_PREFIX@_list_merge(@LISTHASH_PREFIX@_cmpfunc_t cmpfunc, int flags, + @LISTHASH_PREFIX@_list_t *list1, + @LISTHASH_PREFIX@_list_t *list2) +{ + @LISTHASH_PREFIX@_list_t *newlist; + @LISTHASH_PREFIX@_listptr_t n; + + newlist = @LISTHASH_PREFIX@_list_new(flags, cmpfunc); + + n = NULL; + while (@LISTHASH_PREFIX@_list_next(list1, &n) != 0) + @LISTHASH_PREFIX@_list_add(newlist, n->data); + n = NULL; + while (@LISTHASH_PREFIX@_list_next(list2, &n) != 0) + @LISTHASH_PREFIX@_list_add(newlist, n->data); + + return newlist; +} + + diff --git a/listhash/list_new.3.in b/listhash/list_new.3.in new file mode 100644 index 0000000..831bd8f --- /dev/null +++ b/listhash/list_new.3.in @@ -0,0 +1,86 @@ +.TH @LISTHASH_PREFIX@_list_new 3 "Jan 2000" "University of Illinois" "C Library Calls" +\" @configure_input@ +.SH NAME +@LISTHASH_PREFIX@_list_new, @LISTHASH_PREFIX@_list_free, @LISTHASH_PREFIX@_list_next, +@LISTHASH_PREFIX@_list_prev, @LISTHASH_PREFIX@_list_add, @LISTHASH_PREFIX@_list_del, +@LISTHASH_PREFIX@_list_search, @LISTHASH_PREFIX@_list_dup, @LISTHASH_PREFIX@_list_merge, +@LISTHASH_PREFIX@_list_add_str \- linked list routines +.SH SYNOPSIS +.B #include <@LISTHASH_PREFIX@.h> +.P +.BI "@LISTHASH_PREFIX@_list_t *@LISTHASH_PREFIX@_list_new(int " flags "," +.BI "int (*" cmpfunc ")());" +.br +.BI "void @LISTHASH_PREFIX@_list_free(@LISTHASH_PREFIX@_list_t *" l ", void (*" freefunc ")());" +.br +.BI "int @LISTHASH_PREFIX@_list_add_str(@LISTHASH_PREFIX@_list_t *" l ", char *" str "," +.BI "char *" delim ");" +.br +.BI "int @LISTHASH_PREFIX@_list_add(@LISTHASH_PREFIX@_list_t *" l ", void *" data ");" +.br +.BI "void @LISTHASH_PREFIX@_list_del(@LISTHASH_PREFIX@_list_t *" l ", @LISTHASH_PREFIX@_node_t **" n ");" +.br +.BI "int @LISTHASH_PREFIX@_list_search(@LISTHASH_PREFIX@_list_t *" l ", @LISTHASH_PREFIX@_node_t **" n "," +.BI "void *" data ", int (*" matchfunc ")());" +.br +.BI "int @LISTHASH_PREFIX@_list_next(@LISTHASH_PREFIX@_list_t *" l ", @LISTHASH_PREFIX@_node_t **" n ");" +.br +.BI "int @LISTHASH_PREFIX@_list_prev(@LISTHASH_PREFIX@_list_t *" l ", @LISTHASH_PREFIX@_node_t **" n ");" +.br +.BI "@LISTHASH_PREFIX@_list_t *@LISTHASH_PREFIX@_list_dup(@LISTHASH_PREFIX@_list_t *" l ");" +.br +.BI "@LISTHASH_PREFIX@_list_t *@LISTHASH_PREFIX@_list_merge(int (*" cmpfunc ")(), int " flags "," +.BI "@LISTHASH_PREFIX@_list_t *" list1 ", @LISTHASH_PREFIX@_list_t *" list2 ");" +.SH DESCRIPTION +The \fB@LISTHASH_PREFIX@_list_new\fP() function creates a new list. The \fIflags\fP +argument must be one of the following values: +.IP \fBLIST_USERFUNC\fP +The \fIcmpfunc\fP argument points to a user-supplied function which +determines the ordering of the list. +.IP \fBLIST_STACK\fP +Use the list as a stack. New elements are added to the front of the list. +.IP \fBLIST_QUEUE\fP +Use the list as a queue. New elements are added to the end of the list. +.PP +The \fB@LISTHASH_PREFIX@_list_free\fP() function deallocates all memory associated +with the list \fIl\fP. If \fIfreefunc\fP is not \fINULL\fP, it is called +to free memory associated with each node in the list. + +The \fB@LISTHASH_PREFIX@_list_add\fP() function adds the element pointed to by +\fIdata\fP to the list \fIl\fP. The position of the new element will +be determined by the flags passed to \fB@LISTHASH_PREFIX@_list_new\fP() when the +list was created. + +The \fB@LISTHASH_PREFIX@_list_add_str\fP() function tokenizes the string \fIstr\fP +using the delimiter characters in the string \fIdelim\fP. The resulting +tokens are added to list \fIl\fP using \fB@LISTHASH_PREFIX@_list_add\fP(). + +The \fB@LISTHASH_PREFIX@_list_search\fP() function searches for an element which +matches \fIdata\fP using the matching function \fImatchfunc\fP. If +\fImatchfunc\fP is \fINULL\fP, a default matching function designed for +ASCII strings is used. Searching begins from the node pointed to by +\fIn\fP. + +The \fB@LISTHASH_PREFIX@_list_del\fP() function removes the entry pointed to by +\fIn\fP from the list pointed to by \fIl\fP. + +The \fB@LISTHASH_PREFIX@_list_dup\fP() function creates a copy of the list \fIl\fP +using dynamically allocated memory. + +The \fB@LISTHASH_PREFIX@_list_merge\fP() function creates a new +list with \fIflags\fP and \fIcmpfunc\fP, in the same way as +\fB@LISTHASH_PREFIX@_list_new\fP(). It then adds all elements from \fIlist1\fP +and \fIlist2\fP using \fB@LISTHASH_PREFIX@_list_add\fP(). +.SH RETURN VALUE +The \fB@LISTHASH_PREFIX@_list_new\fP(), \fB@LISTHASH_PREFIX@_list_dup\fP(), and +\fB@LISTHASH_PREFIX@_list_merge\fP() functions return a pointer to the new list +structure, or \fINULL\fP on error. + +The \fB@LISTHASH_PREFIX@_list_next\fP(), \fB@LISTHASH_PREFIX@_list_prev\fP(), and +\fB@LISTHASH_PREFIX@_list_search\fP() functions return 1 when valid data is +returned, or 0 otherwise. + +The \fB@LISTHASH_PREFIX@_list_add\fP() and \fB@LISTHASH_PREFIX@_list_add_str\fP() functions +return 0 on success, or \-1 on error. +.SH SEE ALSO +.BR @LISTHASH_PREFIX@_hash_new (3) diff --git a/listhash/listhash.h.in b/listhash/listhash.h.in new file mode 100644 index 0000000..3d654a4 --- /dev/null +++ b/listhash/listhash.h.in @@ -0,0 +1,203 @@ +/* @configure_input@ */ + +/* +** Copyright 1998-2002 University of Illinois Board of Trustees +** Copyright 1998-2002 Mark D. Roth +** All rights reserved. +** +** @LISTHASH_PREFIX@_listhash.h - header file for listhash module +** +** Mark D. Roth +** Campus Information Technologies and Educational Services +** University of Illinois at Urbana-Champaign +*/ + +#ifndef @LISTHASH_PREFIX@_LISTHASH_H +#define @LISTHASH_PREFIX@_LISTHASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/***** list.c **********************************************************/ + +/* +** Comparison function (used to determine order of elements in a list) +** returns less than, equal to, or greater than 0 +** if data1 is less than, equal to, or greater than data2 +*/ +typedef int (*@LISTHASH_PREFIX@_cmpfunc_t)(void *, void *); + +/* +** Free function (for freeing allocated memory in each element) +*/ +typedef void (*@LISTHASH_PREFIX@_freefunc_t)(void *); + +/* +** Plugin function for @LISTHASH_PREFIX@_list_iterate() +*/ +typedef int (*@LISTHASH_PREFIX@_iterate_func_t)(void *, void *); + +/* +** Matching function (used to find elements in a list) +** first argument is the data to search for +** second argument is the list element it's being compared to +** returns 0 if no match is found, non-zero otherwise +*/ +typedef int (*@LISTHASH_PREFIX@_matchfunc_t)(void *, void *); + + +struct @LISTHASH_PREFIX@_node +{ + void *data; + struct @LISTHASH_PREFIX@_node *next; + struct @LISTHASH_PREFIX@_node *prev; +}; +typedef struct @LISTHASH_PREFIX@_node *@LISTHASH_PREFIX@_listptr_t; + +struct @LISTHASH_PREFIX@_list +{ + @LISTHASH_PREFIX@_listptr_t first; + @LISTHASH_PREFIX@_listptr_t last; + @LISTHASH_PREFIX@_cmpfunc_t cmpfunc; + int flags; + unsigned int nents; +}; +typedef struct @LISTHASH_PREFIX@_list @LISTHASH_PREFIX@_list_t; + + +/* values for flags */ +#define LIST_USERFUNC 0 /* use cmpfunc() to order */ +#define LIST_STACK 1 /* new elements go in front */ +#define LIST_QUEUE 2 /* new elements go at the end */ + + +/* reset a list pointer */ +void @LISTHASH_PREFIX@_listptr_reset(@LISTHASH_PREFIX@_listptr_t *); + +/* retrieve the data being pointed to */ +void *@LISTHASH_PREFIX@_listptr_data(@LISTHASH_PREFIX@_listptr_t *); + +/* creates a new, empty list */ +@LISTHASH_PREFIX@_list_t *@LISTHASH_PREFIX@_list_new(int, @LISTHASH_PREFIX@_cmpfunc_t); + +/* call a function for every element in a list */ +int @LISTHASH_PREFIX@_list_iterate(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_iterate_func_t, void *); + +/* empty the list */ +void @LISTHASH_PREFIX@_list_empty(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_freefunc_t); + +/* remove and free() the entire list */ +void @LISTHASH_PREFIX@_list_free(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_freefunc_t); + +/* add elements */ +int @LISTHASH_PREFIX@_list_add(@LISTHASH_PREFIX@_list_t *, void *); + +/* removes an element from the list - returns -1 on error */ +void @LISTHASH_PREFIX@_list_del(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_listptr_t *); + +/* returns 1 when valid data is returned, or 0 at end of list */ +int @LISTHASH_PREFIX@_list_next(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_listptr_t *); + +/* returns 1 when valid data is returned, or 0 at end of list */ +int @LISTHASH_PREFIX@_list_prev(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_listptr_t *); + +/* return 1 if the data matches a list entry, 0 otherwise */ +int @LISTHASH_PREFIX@_list_search(@LISTHASH_PREFIX@_list_t *, + @LISTHASH_PREFIX@_listptr_t *, void *, + @LISTHASH_PREFIX@_matchfunc_t); + +/* return number of elements from list */ +unsigned int @LISTHASH_PREFIX@_list_nents(@LISTHASH_PREFIX@_list_t *); + +/* adds elements from a string delimited by delim */ +int @LISTHASH_PREFIX@_list_add_str(@LISTHASH_PREFIX@_list_t *, char *, char *); + +/* string matching function */ +int @LISTHASH_PREFIX@_str_match(char *, char *); + + +/***** hash.c **********************************************************/ + +/* +** Hashing function (determines which bucket the given key hashes into) +** first argument is the key to hash +** second argument is the total number of buckets +** returns the bucket number +*/ +typedef unsigned int (*@LISTHASH_PREFIX@_hashfunc_t)(void *, unsigned int); + + +struct @LISTHASH_PREFIX@_hashptr +{ + int bucket; + @LISTHASH_PREFIX@_listptr_t node; +}; +typedef struct @LISTHASH_PREFIX@_hashptr @LISTHASH_PREFIX@_hashptr_t; + +struct @LISTHASH_PREFIX@_hash +{ + int numbuckets; + @LISTHASH_PREFIX@_list_t **table; + @LISTHASH_PREFIX@_hashfunc_t hashfunc; + unsigned int nents; +}; +typedef struct @LISTHASH_PREFIX@_hash @LISTHASH_PREFIX@_hash_t; + + +/* reset a hash pointer */ +void @LISTHASH_PREFIX@_hashptr_reset(@LISTHASH_PREFIX@_hashptr_t *); + +/* retrieve the data being pointed to */ +void *@LISTHASH_PREFIX@_hashptr_data(@LISTHASH_PREFIX@_hashptr_t *); + +/* default hash function, optimized for 7-bit strings */ +unsigned int @LISTHASH_PREFIX@_str_hashfunc(char *, unsigned int); + +/* return number of elements from hash */ +unsigned int @LISTHASH_PREFIX@_hash_nents(@LISTHASH_PREFIX@_hash_t *); + +/* create a new hash */ +@LISTHASH_PREFIX@_hash_t *@LISTHASH_PREFIX@_hash_new(int, @LISTHASH_PREFIX@_hashfunc_t); + +/* empty the hash */ +void @LISTHASH_PREFIX@_hash_empty(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_freefunc_t); + +/* delete all the @LISTHASH_PREFIX@_nodes of the hash and clean up */ +void @LISTHASH_PREFIX@_hash_free(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_freefunc_t); + +/* returns 1 when valid data is returned, or 0 at end of list */ +int @LISTHASH_PREFIX@_hash_next(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_hashptr_t *); + +/* return 1 if the data matches a list entry, 0 otherwise */ +int @LISTHASH_PREFIX@_hash_search(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_hashptr_t *, void *, + @LISTHASH_PREFIX@_matchfunc_t); + +/* return 1 if the key matches a list entry, 0 otherwise */ +int @LISTHASH_PREFIX@_hash_getkey(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_hashptr_t *, void *, + @LISTHASH_PREFIX@_matchfunc_t); + +/* inserting data */ +int @LISTHASH_PREFIX@_hash_add(@LISTHASH_PREFIX@_hash_t *, void *); + +/* delete an entry */ +int @LISTHASH_PREFIX@_hash_del(@LISTHASH_PREFIX@_hash_t *, + @LISTHASH_PREFIX@_hashptr_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* ! @LISTHASH_PREFIX@_LISTHASH_H */ + diff --git a/listhash/module.ac b/listhash/module.ac new file mode 100644 index 0000000..9258fe8 --- /dev/null +++ b/listhash/module.ac @@ -0,0 +1,21 @@ +AC_DEFUN(subdir[_INIT], [ + AC_REQUIRE([COMPAT_FUNC_STRLCPY]) + AC_REQUIRE([COMPAT_FUNC_STRSEP]) + + if test -z "$2"; then + LISTHASH_PREFIX="${PACKAGE_NAME}"; + else + LISTHASH_PREFIX="$2"; + fi + AC_SUBST([LISTHASH_PREFIX]) + + LISTHASH_DIR="$1"; + AC_SUBST([LISTHASH_DIR]) + + AC_CONFIG_FILES([$1/${LISTHASH_PREFIX}_listhash.h:$1/listhash.h.in]) + AC_CONFIG_FILES([$1/${LISTHASH_PREFIX}_list.c:$1/list.c.in]) + AC_CONFIG_FILES([$1/${LISTHASH_PREFIX}_hash.c:$1/hash.c.in]) + AC_CONFIG_FILES([$1/${LISTHASH_PREFIX}_list_new.3:$1/list_new.3.in]) + AC_CONFIG_FILES([$1/${LISTHASH_PREFIX}_hash_new.3:$1/hash_new.3.in]) +]) +