diff --git a/.fix-Changelog b/.fix-Changelog index b8b36fc9..9da1ce9d 100644 --- a/.fix-Changelog +++ b/.fix-Changelog @@ -1,6 +1,6 @@ #!/bin/sh -FILES=`find . -name ChangeLog` +FILES=`find . -name ChangeLog -print` header=/tmp/revheader diff --git a/.missing-copyright b/.missing-copyright new file mode 100644 index 00000000..d99cb32e --- /dev/null +++ b/.missing-copyright @@ -0,0 +1,4 @@ +#!/bin/sh + +find . -type f \! -name \*~ \! -exec grep -q Begin-Header \{\} \; -print \ + | grep -v ^./build diff --git a/ChangeLog b/ChangeLog index face968a..3ccc1a10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Wed Jan 15 11:37:36 1997 Theodore Ts'o + + * config.sub (basic_machine): Added i686-* as another name for the + Pentium Pro. + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Mon Oct 7 08:22:31 1996 Theodore Ts'o * Makefile.in (all): Don't run "make check" by default. User diff --git a/INSTALL.dllbin b/INSTALL.dllbin index 3b0ecb82..234c1f02 100644 --- a/INSTALL.dllbin +++ b/INSTALL.dllbin @@ -9,7 +9,7 @@ system management programs, just follow the steps: 1) Install this tar file using the following command: - gunzip < e2fsprogs-1.04-dllbin.tar.gz | (cd /; tar xvf - ) + gunzip < e2fsprogs-1.04-dllbin.tar.gz | (cd /; tar Sxvpf - ) 2) Run ldconfig to update the shared library pointers. diff --git a/INSTALL.elfbin b/INSTALL.elfbin index 4acf08c6..c69834c3 100644 --- a/INSTALL.elfbin +++ b/INSTALL.elfbin @@ -9,7 +9,7 @@ system management programs, just follow the steps: 1) Install this tar file using the following command: - gunzip < e2fsprogs-1.04-elfbin.tar.gz | (cd /; tar xvf - ) + gunzip < e2fsprogs-1.04-elfbin.tar.gz | (cd /; tar Sxvpf - ) 2) Run ldconfig to update the shared library pointers. diff --git a/MCONFIG.in b/MCONFIG.in index 1776cf8a..f01e4e2f 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -17,6 +17,7 @@ man1dir = $(usr_prefix)/man/man1 man8dir = $(usr_prefix)/man/man8 cat1dir = $(usr_prefix)/man/cat1 cat8dir = $(usr_prefix)/man/cat8 +infodir = $(usr_prefix)/info @SET_MAKE@ diff --git a/Makefile.in b/Makefile.in index ad96438c..9769bcbf 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,18 +8,26 @@ INSTALL = @INSTALL@ @MCONFIG@ LIB_SUBDIRS=lib/et lib/ss lib/ext2fs lib/e2p lib/uuid -PROG_SUBDIRS=e2fsck debugfs misc +PROG_SUBDIRS=e2fsck debugfs misc tests/progs SUBDIRS=$(LIB_SUBDIRS) $(PROG_SUBDIRS) tests SUBS= include/linux/types.h -all:: $(SUBS) libs progs +TAR=tar + +all:: $(SUBS) libs progs docs progs: $(SUBS) all-progs-recursive libs: $(SUBS) all-libs-recursive +docs: + (cd doc; make libext2fs.info) + +install-doc-libs: + (cd doc; make install-doc-libs) + install: all-libs-recursive install-progs-recursive \ - install-shlibs-libs-recursive + install-shlibs-libs-recursive install-doc-libs (export MANPATH=$(DESTDIR)$(mandir); $(srcdir)/install-utils/compile_manpages) install-libs: install-libs-recursive @@ -64,7 +72,7 @@ mostlyclean-local: $(RM) -f \#* *~ core MAKELOG clean-local: mostlyclean-local distclean-local: clean-local - $(RM) -f include/linux/types.h + $(RM) -f include/linux/types.h $(SUBSTITUTE) $(RM) -f config.status config.log config.cache MCONFIG Makefile realclean-local: distclean-local $(RM) -f configure @@ -78,7 +86,7 @@ distribution_tar_file: cd .. cp -r $(srcdir)/README $(srcdir)/install-utils /tmp/dest cp $(srcdir)/INSTALL.@BINARY_TYPE@ /tmp/dest/INSTALL - (cd /tmp/dest; tar cf - . ) | gzip -9 \ + (cd /tmp/dest; $(TAR) cf - . ) | gzip -9 \ > e2fsprogs-@E2FSPROGS_VERSION@-@BINARY_TYPE@.tar.gz SRCROOT = `echo e2fsprogs-@E2FSPROGS_VERSION@ | sed -e 's/-WIP//'` @@ -87,14 +95,14 @@ $(srcdir)/.exclude-file: (cd $(srcdir)/.. ; find $(SRCROOT) \( -name \*~ -o -name \*.orig \ -o -name \*.rej \) -print > $(SRCROOT)/.exclude-file) echo "$(SRCROOT)/build" >> $(srcdir)/.exclude-file - echo "$(SRCROOT)/todo" >> $(srcdir)/.exclude-file + echo "$(SRCROOT)/rpm.log" >> $(srcdir)/.exclude-file echo "$(SRCROOT)/.exclude-file" >> $(srcdir)/.exclude-file echo $(SRCROOT)/e2fsprogs-@E2FSPROGS_VERSION@.tar.gz \ >> $(srcdir)/.exclude-file source_tar_file: $(srcdir)/.exclude-file - (cd $(srcdir) ; tar -C .. -c -v -f - \ + (cd $(srcdir) ; $(TAR) -C .. -c -v -f - \ -X .exclude-file $(SRCROOT) | \ gzip -9 > e2fsprogs-@E2FSPROGS_VERSION@.tar.gz) rm -f $(srcdir)/.exclude-file diff --git a/README b/README index c8e26260..8763d224 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ - This is the new version (1.05) of the second extended file system -management programs, otherwise known as the Monomonac Release. + This is the new version (1.07) of the second extended file system +management programs. See the file INSTALL for installation instructions. This is important! Note that your /etc/fstab file may need modifying before diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 511cd632..611fb2da 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,125 @@ +E2fsprogs 1.07 (March 9, 1997) +============================== + +E2fsck is now uses much less memory when checking really large +filesystems (or rather, filesystems with a large number of inodes). +Previously a filesystem with 1 million inodes required 4 megabytes of +memory to store inode count statistics; that storage requirement has +now been reduced to roughly half a megabyte. + +E2fsck can now properly deal with bad blocks appearing inside the +inode table. Instead of trying to relocate the inode table (which +often failed because there wasn't enough space), the inodes in the bad +block are marked as in use. + +E2fsck will automatically try to use the backup superblocks if the +primary superblocks have a bad magic number or have missing meta-data +blocks (or meta-data blocks which are out of range). + +E2fsck's pass 3 has been made more efficient; most noticeable on +filesystems with a very large number of directories. + +Completely revamped e2fsck's system of printing problem reports. It +is now table driven, to make them more easily customizeable and +extendable. Error messages which can be printed out during preen mode +are now one line long. + +Fixed e2fsck's filesystem swapping code so that it won't try to swap +fast symbolic links or deleted files. + +Fixed e2fsck core dumping when fixing a filesystem which has no +directories (not even a root directory). + +Added a check to e2fsck to make sure that the length of every +directory entry is a multiple of 4 (since the kernel complains if it +isn't). + +Added a check to e2fsck to make sure that a directory entry isn't a +link to the root directory, since that isn't allowed. + +Added a check to e2fsk to now make sure the '.' and '..' directory +entries are null terminated, since the 2.0 kernel requires it. + +Added check to write_bitmaps() to make sure the superblock doesn't get +trashed if the inode or block bitmap is marked as being block zero. + +Added checking of the new feature set fields in the superblock, to +avoid dealing with new filesystem features that this package wasn't +set up to handle. + +Fixed a fencepost error in ext2fs_new_block() which would occasionally +try to allocate a block beyond the end of a filesystem. + +When the UUID library picks a random IEEE 802 address (because it +can't find one from a network card), it sets the multicast bit, to +avoid conflicting with a legitimate IEEE 802 address. + +Mke2fs now sets the root directory's owner to be the real uid of the +user running mke2fs. If the real uid is non-zero, it also sets +the group ownership of the root directory to be the real group-id of +the user running mke2fs. + +Mke2fs now has more intelligent error checking when it is given a +non-existent device. + +When badblocks is given the -vv option, it now updates the block that +it is currently testing on every block. + +Fixed a bug in fsck where it wouldn't modify the PATH envirnoment +currently correctly if PATH wasn't already set. + +Shared libraries now built with dependencies. This allows the shared +library files to be used with dlopen(); it also makes the transition +to libc 6 easier, since ld.so can tell which libc a particular shared +library expects to use. + +Programmer's notes: +------------------- + +Added new abstraction (defined in dblist.c) for maintaining a list of +blocks which belongs to directories. This is used in e2fsck and other +programs which need to iterate over all directories. + +Added new functions which test to see if a contiguous range of blocks +(or inodes) are available. (ext2fs_*_bitmap_range). + +Added new function (ext2_inode_has_valid_blocks) which returns true if +an inode has valid blocks. (moved from e2fsck code). + +Added new function (ext2fs_allocate_tables) which allocates the +meta-data blocks as part of initializing a filesystem. (moved from +mke2fs code). + +Added a new I/O manager for testing purposes. It will either allow a +program to intercept I/O requests, or print debugging messages to +trace the activity of a program using the I/O manager. + +The badblocks_list functions now store the bad blocks in a sorted +order, and use a binary search to speed up badblocks_list_test. + +The inode scan function ext2fs_get_next_inode() may now return a soft +error returns: MISSING_INODE_TABLE and BAD_BLOCK_IN_INODE_TABLE in +those cases where part of an inode table is missing or there is a bad +block in the inode table. + +Added a new function (ext2fs_block_iterate2) which adds new arguments to +the callback function to return a pointer (block and offset) to the +reference of the block. + +Added new function (ext2fs_inode_scan_goto_blockgroup) which allows an +application to jump to a particular block group while doing an inode +scan. + +The badblocks list functions were renamed from badblocks_* to +ext2fs_badblocks_*. Backwards compatibility functions are available +for now, but programs should be modified to use the new interface. + +Some of the library functions were reorganized into separate files to +reduce the size of some programs which statically link against the +ext2 library. + +Put in some miscellaneous fixes for the Alpha platform. + E2fsprogs 1.06 (October 7, 1996) ================================ diff --git a/SHLIBS b/SHLIBS index 8e5371d7..2fcbbc33 100644 --- a/SHLIBS +++ b/SHLIBS @@ -25,3 +25,11 @@ Maintainer: Theodore Ts'o Email: tytso@mit.edu Start: 0x66980000 End: 0x669fffff + +Library:libuuid.so +Description: DCE Universally Unique ID (UUID) library +Maintainer: Theodore Ts'o +Email: tytso@mit.edu +Start: 0x67900000 +End: 0x679fffff + diff --git a/config.sub b/config.sub index 93371be1..124029dd 100644 --- a/config.sub +++ b/config.sub @@ -422,7 +422,7 @@ case $basic_machine in # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium basic_machine=i586-intel ;; - pentium-* | p5-* | p6-*) + pentium-* | p5-* | p6-* | i686-* ) # We don't have specific support for the Intel Pentium (p6) followon yet, so just call it a Pentium basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; diff --git a/configure b/configure index 6c5fc701..be4f88f4 100644 --- a/configure +++ b/configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated automatically using autoconf version 2.4 -# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -61,9 +61,24 @@ target=NONE verbose= x_includes=NONE x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' # Initialize some other variables. subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 ac_prev= for ac_option @@ -85,9 +100,14 @@ do case "$ac_option" in - -build | --build | --buil | --bui | --bu | --b) + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) ac_prev=build ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*) + -build=* | --build=* | --buil=* | --bui=* | --bu=*) build="$ac_optarg" ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ @@ -97,6 +117,12 @@ do | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file="$ac_optarg" ;; + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + -disable-* | --disable-*) ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` # Reject names that are not valid shell variable names. @@ -147,12 +173,29 @@ Configuration: Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] - --exec-prefix=PREFIX install architecture-dependent files in PREFIX + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] --srcdir=DIR find the sources in DIR [configure dir or ..] --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names - --program-transform-name=PROGRAM run sed PROGRAM on installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF Host type: --build=BUILD configure for building on BUILD [BUILD=HOST] --host=HOST configure for HOST [guessed] @@ -164,8 +207,10 @@ Features and packages: --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR ---enable and --with options recognized:$ac_help EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi exit 0 ;; -host | --host | --hos | --ho) @@ -173,6 +218,44 @@ EOF -host=* | --host=* | --hos=* | --ho=*) host="$ac_optarg" ;; + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; @@ -185,6 +268,15 @@ EOF | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) @@ -225,6 +317,23 @@ EOF | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) @@ -235,6 +344,13 @@ EOF -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir="$ac_optarg" ;; + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) @@ -244,7 +360,7 @@ EOF verbose=yes ;; -version | --version | --versio | --versi | --vers) - echo "configure generated by autoconf version 2.4" + echo "configure generated by autoconf version 2.12" exit 0 ;; -with-* | --with-*) @@ -290,7 +406,7 @@ EOF -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; - *) + *) if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then echo "configure: warning: $ac_option: invalid host type" 1>&2 fi @@ -346,11 +462,14 @@ do done # NLS nuisances. -# Only set LANG and LC_ALL to C if already set. -# These must not be set unconditionally because not all systems understand -# e.g. LANG=C (notably SCO). -if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h @@ -410,8 +529,9 @@ fi ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' -ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5' -ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. @@ -491,6 +611,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } fi echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:615: checking host system type" >&5 host_alias=$host case "$host_alias" in @@ -505,14 +626,14 @@ NONE) esac host=`$ac_config_sub $host_alias` -host_cpu=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'` -host_vendor=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'` -host_os=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$host" 1>&6 # Check whether --with-cc or --without-cc was given. -withval="$with_cc" -if test -n "$withval"; then +if test "${with_cc+set}" = set; then + withval="$with_cc" echo "$ac_t""CC=$withval" 1>&6 CC=$withval else @@ -522,8 +643,8 @@ fi export CC # Check whether --with-linker or --without-linker was given. -withval="$with_linker" -if test -n "$withval"; then +if test "${with_linker+set}" = set; then + withval="$with_linker" echo "$ac_t""LD=$withval" 1>&6 LD=$withval else @@ -533,8 +654,8 @@ fi export LD # Check whether --with-ccopts or --without-ccopts was given. -withval="$with_ccopts" -if test -n "$withval"; then +if test "${with_ccopts+set}" = set; then + withval="$with_ccopts" echo "$ac_t""CCOPTS is $withval" 1>&6 CCOPTS=$withval CFLAGS="$CFLAGS $withval" @@ -546,8 +667,8 @@ LIB_EXT=.a STATIC_LIB_EXT=.a PROFILE_LIB_EXT=.a # Check whether --with-ldopts or --without-ldopts was given. -withval="$with_ldopts" -if test -n "$withval"; then +if test "${with_ldopts+set}" = set; then + withval="$with_ldopts" echo "$ac_t""LDFLAGS is $withval" 1>&6 LDFLAGS=$withval else @@ -555,15 +676,15 @@ else fi # Check whether --with-usr-prefix or --without-usr-prefix was given. -withval="$with_usr_prefix" -if test -n "$withval"; then +if test "${with_usr_prefix+set}" = set; then + withval="$with_usr_prefix" usr_prefix=$withval else usr_prefix=NONE fi # Check whether --enable-dll-shlibs or --disable-dll-shlibs was given. -enableval="$enable_dll_shlibs" -if test -n "$enableval"; then +if test "${enable_dll_shlibs+set}" = set; then + enableval="$enable_dll_shlibs" if test "$enableval" = "no" then DLL_CMT=# @@ -587,8 +708,8 @@ fi # Check whether --enable-elf-shlibs or --disable-elf-shlibs was given. -enableval="$enable_elf_shlibs" -if test -n "$enableval"; then +if test "${enable_elf_shlibs+set}" = set; then + enableval="$enable_elf_shlibs" if test "$enableval" = "no" then ELF_CMT=# @@ -612,8 +733,8 @@ fi # Check whether --enable-bsd-shlibs or --disable-bsd-shlibs was given. -enableval="$enable_bsd_shlibs" -if test -n "$enableval"; then +if test "${enable_bsd_shlibs+set}" = set; then + enableval="$enable_bsd_shlibs" if test "$enableval" = "no" then BSDLIB_CMT=# @@ -636,8 +757,8 @@ fi # Check whether --enable-profile or --disable-profile was given. -enableval="$enable_profile" -if test -n "$enableval"; then +if test "${enable_profile+set}" = set; then + enableval="$enable_profile" if test "$enableval" = "no" then PROFILE_CMT=# @@ -660,8 +781,8 @@ fi # Check whether --enable-checker or --disable-checker was given. -enableval="$enable_checker" -if test -n "$enableval"; then +if test "${enable_checker+set}" = set; then + enableval="$enable_checker" if test "$enableval" = "no" then CHECKER_CMT=# @@ -686,8 +807,8 @@ fi # Check whether --enable-gcc-wall or --disable-gcc-wall was given. -enableval="$enable_gcc_wall" -if test -n "$enableval"; then +if test "${enable_gcc_wall+set}" = set; then + enableval="$enable_gcc_wall" if test "$enableval" = "no" then W=# @@ -705,8 +826,8 @@ fi # Check whether --enable-dynamic-e2fsck or --disable-dynamic-e2fsck was given. -enableval="$enable_dynamic_e2fsck" -if test -n "$enableval"; then +if test "${enable_dynamic_e2fsck+set}" = set; then + enableval="$enable_dynamic_e2fsck" if test "$enableval" = "no" then E2FSCK_TYPE=static @@ -724,8 +845,8 @@ fi # Check whether --enable-fsck or --disable-fsck was given. -enableval="$enable_fsck" -if test -n "$enableval"; then +if test "${enable_fsck+set}" = set; then + enableval="$enable_fsck" if test "$enableval" = "no" then FSCK_PROG='' FSCK_MAN='' @@ -753,8 +874,8 @@ fi MAKEFILE_LIBRARY=$srcdir/lib/Makefile.library # Check whether --enable-old-bitops or --disable-old-bitops was given. -enableval="$enable_old_bitops" -if test -n "$enableval"; then +if test "${enable_old_bitops+set}" = set; then + enableval="$enable_old_bitops" if test "$enableval" = "no" then echo "Using new (standard) bitmask operations" @@ -774,7 +895,8 @@ fi echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -set dummy ${MAKE-make}; ac_make=$2 +echo "configure:899: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -802,6 +924,7 @@ fi # Extract the first word of "ln", so it can be a program name with args. set dummy ln; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:928: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_LN'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -831,6 +954,7 @@ else fi echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 +echo "configure:958: checking whether ln -s works" >&5 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -853,6 +977,7 @@ fi # Extract the first word of "mv", so it can be a program name with args. set dummy mv; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:981: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_MV'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -884,6 +1009,7 @@ fi # Extract the first word of "cp", so it can be a program name with args. set dummy cp; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1013: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_CP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -915,6 +1041,7 @@ fi # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1045: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_RM'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -946,6 +1073,7 @@ fi # Extract the first word of "chmod", so it can be a program name with args. set dummy chmod; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1077: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_CHMOD'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -977,6 +1105,7 @@ fi # Extract the first word of "awk", so it can be a program name with args. set dummy awk; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1109: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_AWK'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1008,6 +1137,7 @@ fi # Extract the first word of "sed", so it can be a program name with args. set dummy sed; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1141: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_path_SED'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1037,6 +1167,7 @@ else fi echo $ac_n "checking build system type""... $ac_c" 1>&6 +echo "configure:1171: checking build system type" >&5 build_alias=$build case "$build_alias" in @@ -1048,9 +1179,9 @@ NONE) esac build=`$ac_config_sub $build_alias` -build_cpu=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'` -build_vendor=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'` -build_os=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'` +build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$ac_t""$build" 1>&6 if test $host != $build; then @@ -1062,6 +1193,7 @@ fi # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1197: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1092,6 +1224,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1228: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1125,6 +1258,7 @@ fi # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1262: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1155,6 +1289,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1293: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1188,6 +1323,7 @@ fi # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1327: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1218,6 +1354,7 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1358: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_STRIP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1251,6 +1388,7 @@ fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1392: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1266,7 +1404,6 @@ else fi done IFS="$ac_save_ifs" - test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc" fi fi CC="$ac_cv_prog_CC" @@ -1276,8 +1413,98 @@ else echo "$ac_t""no" 1>&6 fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1421: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1469: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:1503: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:1508: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -1286,35 +1513,42 @@ else yes; #endif EOF -if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1517: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no fi fi + echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + if test $ac_cv_prog_gcc = yes; then GCC=yes - if test "${CFLAGS+set}" != set; then - echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:1532: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then - ac_cv_prog_gcc_g=yes + ac_cv_prog_cc_g=yes else - ac_cv_prog_gcc_g=no + ac_cv_prog_cc_g=no fi rm -f conftest* fi - echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6 - if test $ac_cv_prog_gcc_g = yes; then - CFLAGS="-g -O" - else - CFLAGS="-O" - fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" fi else GCC= @@ -1332,11 +1566,12 @@ fi # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # ./install, which can be erroneously created by make from ./install.sh. echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1570: checking for a BSD compatible install" >&5 if test -z "$INSTALL"; then if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" for ac_dir in $PATH; do # Account for people who put trailing slashes in PATH elements. case "$ac_dir/" in @@ -1359,11 +1594,18 @@ else ;; esac done - IFS="$ac_save_ifs" - # As a last resort, use the slow shell script. - test -z "$ac_cv_path_install" && ac_cv_path_install="$ac_install_sh" + IFS="$ac_save_IFS" + fi - INSTALL="$ac_cv_path_install" + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi fi echo "$ac_t""$INSTALL" 1>&6 @@ -1373,32 +1615,9 @@ test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' -# If we cannot run a trivial program, we must be cross compiling. -echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_c_cross'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test "$cross_compiling" = yes; then - ac_cv_c_cross=yes -else -cat > conftest.$ac_ext </dev/null; then - ac_cv_c_cross=no -else - ac_cv_c_cross=yes -fi -fi -rm -fr conftest* -fi -cross_compiling=$ac_cv_c_cross -echo "$ac_t""$ac_cv_c_cross" 1>&6 echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1621: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= @@ -1413,31 +1632,37 @@ else # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF -eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1642: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : else echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF -eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1659: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then : else echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* CPP=/lib/cpp fi @@ -1454,23 +1679,27 @@ echo "$ac_t""$CPP" 1>&6 for ac_hdr in stdlib.h unistd.h stdarg.h errno.h mntent.h dirent.h getopt.h linux/fd.h linux/major.h sys/disklabel.h sys/sockio.h net/if.h netinet/in.h do -ac_safe=`echo "$ac_hdr" | tr './\055' '___'` +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1685: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF -eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1695: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi @@ -1478,7 +1707,7 @@ rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 - ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'` + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 +echo "configure:1722: checking for vprintf" >&5 if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ -char vprintf(); +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); -int main() { return 0; } -int t() { +int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named @@ -1515,16 +1746,18 @@ vprintf(); ; return 0; } EOF -if eval $ac_link; then +if { (eval echo configure:1750: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_vprintf=yes" else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_vprintf=no" fi rm -f conftest* - fi + if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF @@ -1537,20 +1770,22 @@ fi if test "$ac_cv_func_vprintf" != yes; then echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:1774: checking for _doprnt" >&5 if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ -char _doprnt(); +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); -int main() { return 0; } -int t() { +int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named @@ -1563,16 +1798,18 @@ _doprnt(); ; return 0; } EOF -if eval $ac_link; then +if { (eval echo configure:1802: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func__doprnt=yes" else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func__doprnt=no" fi rm -f conftest* - fi + if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then echo "$ac_t""yes" 1>&6 cat >> confdefs.h <<\EOF @@ -1586,28 +1823,29 @@ fi fi echo $ac_n "checking whether struct dirent has a d_namlen field""... $ac_c" 1>&6 +echo "configure:1827: checking whether struct dirent has a d_namlen field" >&5 if eval "test \"`echo '$''{'e2fsprogs_cv_struct_d_namlen'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include -int main() { return 0; } -int t() { +int main() { struct dirent de; de.d_namlen = 0; ; return 0; } EOF -if eval $ac_compile; then +if { (eval echo configure:1840: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* e2fsprogs_cv_struct_d_namlen=yes else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* e2fsprogs_cv_struct_d_namlen=no fi rm -f conftest* - fi echo "$ac_t""$e2fsprogs_cv_struct_d_namlen" 1>&6 @@ -1626,14 +1864,15 @@ if test "$cross_compiling" = yes -a "$ac_cv_sizeof_long" = ""; then echo "configure: warning: Cross-compiling, so cannot check type sizes; assuming short=2, int=4, long=4" 1>&2 fi echo $ac_n "checking size of short""... $ac_c" 1>&6 +echo "configure:1868: checking size of short" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else -cat > conftest.$ac_ext < conftest.$ac_ext < main() @@ -1644,14 +1883,18 @@ main() exit(0); } EOF -eval $ac_link -if test -s conftest && (./conftest; exit) 2>/dev/null; then +if { (eval echo configure:1887: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then ac_cv_sizeof_short=`cat conftestval` else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* ac_cv_sizeof_short=0 fi -fi rm -fr conftest* +fi + fi echo "$ac_t""$ac_cv_sizeof_short" 1>&6 cat >> confdefs.h <&6 +echo "configure:1907: checking size of int" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else -cat > conftest.$ac_ext < conftest.$ac_ext < main() @@ -1678,14 +1922,18 @@ main() exit(0); } EOF -eval $ac_link -if test -s conftest && (./conftest; exit) 2>/dev/null; then +if { (eval echo configure:1926: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then ac_cv_sizeof_int=`cat conftestval` else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* ac_cv_sizeof_int=0 fi -fi rm -fr conftest* +fi + fi echo "$ac_t""$ac_cv_sizeof_int" 1>&6 cat >> confdefs.h <&6 +echo "configure:1946: checking size of long" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else -cat > conftest.$ac_ext < conftest.$ac_ext < main() @@ -1712,14 +1961,18 @@ main() exit(0); } EOF -eval $ac_link -if test -s conftest && (./conftest; exit) 2>/dev/null; then +if { (eval echo configure:1965: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then ac_cv_sizeof_long=`cat conftestval` else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* ac_cv_sizeof_long=0 fi -fi rm -fr conftest* +fi + fi echo "$ac_t""$ac_cv_sizeof_long" 1>&6 cat >> confdefs.h <&6 +echo "configure:1985: checking size of long long" >&5 if eval "test \"`echo '$''{'ac_cv_sizeof_long_long'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test "$cross_compiling" = yes; then { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } else -cat > conftest.$ac_ext < conftest.$ac_ext < main() @@ -1746,14 +2000,18 @@ main() exit(0); } EOF -eval $ac_link -if test -s conftest && (./conftest; exit) 2>/dev/null; then +if { (eval echo configure:2004: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then ac_cv_sizeof_long_long=`cat conftestval` else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* ac_cv_sizeof_long_long=0 fi -fi rm -fr conftest* +fi + fi echo "$ac_t""$ac_cv_sizeof_long_long" 1>&6 cat >> confdefs.h <&6 +echo "configure:2032: checking whether struct stat has a st_flags field" >&5 if eval "test \"`echo '$''{'e2fsprogs_cv_struct_st_flags'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < -int main() { return 0; } -int t() { +int main() { struct stat stat; stat.st_flags = 0; ; return 0; } EOF -if eval $ac_compile; then +if { (eval echo configure:2044: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* e2fsprogs_cv_struct_st_flags=yes else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* e2fsprogs_cv_struct_st_flags=no fi rm -f conftest* - fi echo "$ac_t""$e2fsprogs_cv_struct_st_flags" 1>&6 @@ -1803,20 +2062,22 @@ fi for ac_func in chflags getrusage llseek strdup getmntinfo strcasecmp srandom fchown do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2066: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ -char $ac_func(); +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); -int main() { return 0; } -int t() { +int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named @@ -1829,19 +2090,21 @@ $ac_func(); ; return 0; } EOF -if eval $ac_link; then +if { (eval echo configure:2094: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* - fi + if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 - ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'` + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 +echo "configure:2119: checking ino_t defined by sys/types.h" >&5 if eval "test \"`echo '$''{'e2fsprogs_cv_ino_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < -int main() { return 0; } -int t() { +int main() { ino_t ino; ino = 0; ; return 0; } EOF -if eval $ac_compile; then +if { (eval echo configure:2131: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* e2fsprogs_cv_ino_t=yes else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* e2fsprogs_cv_ino_t=no fi rm -f conftest* - fi echo "$ac_t""$e2fsprogs_cv_ino_t" 1>&6 @@ -1882,23 +2146,27 @@ if test "$e2fsprogs_cv_ino_t" = yes; then EOF fi -ac_safe=`echo "linux/fs.h" | tr './\055' '___'` +ac_safe=`echo "linux/fs.h" | sed 'y%./+-%__p_%'` echo $ac_n "checking for linux/fs.h""... $ac_c" 1>&6 +echo "configure:2152: checking for linux/fs.h" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF -eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2162: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi @@ -1928,33 +2196,40 @@ else fi SOCKET_LIB='' -echo $ac_n "checking for -lsocket""... $ac_c" 1>&6 -if eval "test \"`echo '$''{'ac_cv_lib_socket'+set}'`\" = set"; then +echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6 +echo "configure:2201: checking for socket in -lsocket" >&5 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lsocket $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* - eval "ac_cv_lib_socket=yes" + eval "ac_cv_lib_$ac_lib_var=yes" else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* - eval "ac_cv_lib_socket=no" + eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi -if eval "test \"`echo '$ac_cv_lib_'socket`\" = yes"; then +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 SOCKET_LIB=-lsocket else @@ -1963,11 +2238,12 @@ fi echo $ac_n "checking for optreset""... $ac_c" 1>&6 +echo "configure:2242: checking for optreset" >&5 if eval "test \"`echo '$''{'ac_cv_have_optreset'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF @@ -1990,28 +2266,29 @@ EOF fi echo $ac_n "checking whether the ext2 ioctls compile""... $ac_c" 1>&6 +echo "configure:2270: checking whether the ext2 ioctls compile" >&5 if eval "test \"`echo '$''{'e2fsprogs_cv_ioctl_ext2'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include -int main() { return 0; } -int t() { +int main() { ioctl (0, EXT2_IOC_SETVERSION, 0); ; return 0; } EOF -if eval $ac_compile; then +if { (eval echo configure:2283: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* e2fsprogs_cv_ioctl_ext2=yes else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 rm -rf conftest* e2fsprogs_cv_ioctl_ext2=no fi rm -f conftest* - fi echo "$ac_t""$e2fsprogs_cv_ioctl_ext2" 1>&6 @@ -2082,11 +2359,25 @@ cat > confcache <<\EOF # --recheck option to rerun configure. # EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. (set) 2>&1 | - sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \ - >> confcache + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache if cmp -s $cache_file confcache; then : else @@ -2118,7 +2409,7 @@ trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. cat > conftest.defs <<\EOF -s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%-D\1=\2%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g s%\[%\\&%g s%\]%\\&%g @@ -2153,7 +2444,7 @@ do echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) - echo "$CONFIG_STATUS generated by autoconf version 2.4" + echo "$CONFIG_STATUS generated by autoconf version 2.12" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; @@ -2167,11 +2458,13 @@ ac_given_INSTALL="$INSTALL" trap 'rm -fr `echo "MCONFIG lib/substitute_sh Makefile lib/et/Makefile lib/ss/Makefile lib/ext2fs/Makefile lib/e2p/Makefile lib/uuid/Makefile misc/Makefile e2fsck/Makefile debugfs/Makefile tests/Makefile - relocate/Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 + tests/progs/Makefile doc/Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\CEOF +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF $ac_vpsub $extrasub s%@CFLAGS@%$CFLAGS%g @@ -2183,6 +2476,18 @@ s%@LIBS@%$LIBS%g s%@exec_prefix@%$exec_prefix%g s%@prefix@%$prefix%g s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g /@MCONFIG@/r $MCONFIG s%@MCONFIG@%%g s%@E2FSPROGS_YEAR@%$E2FSPROGS_YEAR%g @@ -2258,23 +2563,59 @@ s%@DO_SUBSTITUTE_SCRIPT@%%g CEOF EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then - # Support "outfile[:infile]", defaulting infile="outfile.in". + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in - *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'` + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac - # Adjust relative srcdir, etc. for subdirectories. + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` @@ -2302,6 +2643,7 @@ for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then [/$]*) INSTALL="$ac_given_INSTALL" ;; *) INSTALL="$ac_dots$ac_given_INSTALL" ;; esac + echo creating "$ac_file" rm -f "$ac_file" configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." @@ -2310,16 +2652,22 @@ for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then # $configure_input" ;; *) ac_comsub= ;; esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` sed -e "$ac_comsub s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g s%@INSTALL@%$INSTALL%g -" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file fi; done -rm -f conftest.subs +rm -f conftest.s* +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF exit 0 EOF diff --git a/configure.in b/configure.in index 3a991612..c1915962 100644 --- a/configure.in +++ b/configure.in @@ -505,4 +505,4 @@ test -d include/linux || mkdir include/linux AC_OUTPUT(MCONFIG lib/substitute_sh Makefile lib/et/Makefile lib/ss/Makefile lib/ext2fs/Makefile lib/e2p/Makefile lib/uuid/Makefile misc/Makefile e2fsck/Makefile debugfs/Makefile tests/Makefile - relocate/Makefile) + tests/progs/Makefile doc/Makefile) diff --git a/debugfs/ChangeLog b/debugfs/ChangeLog index 74cf8404..75c0c5d8 100644 --- a/debugfs/ChangeLog +++ b/debugfs/ChangeLog @@ -1,3 +1,15 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Wed Jan 1 23:53:26 1997 Theodore Ts'o + + * lsdel.c (do_lsdel): Use time_to_string() instead of ctime(). + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Thu Sep 12 15:23:07 1996 Theodore Ts'o * Release of E2fsprogs version 1.05 diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in index da9af492..31ea1605 100644 --- a/debugfs/Makefile.in +++ b/debugfs/Makefile.in @@ -73,8 +73,8 @@ distclean: clean # Makefile dependencies follow. This must be the last section in # the Makefile.in file # -debug_cmds.o: debug_cmds.c $(top_builddir)/lib/ss/ss_err.h \ - $(top_srcdir)/lib/ss/ss.h $(top_srcdir)/lib/ss/copyright.h +debug_cmds.o: debug_cmds.c $(top_srcdir)/lib/ss/ss.h \ + $(top_srcdir)/lib/ss/copyright.h $(top_builddir)/lib/ss/ss_err.h debugfs.o: $(srcdir)/debugfs.c $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/ss/ss.h $(top_srcdir)/lib/ss/copyright.h \ $(top_builddir)/lib/ss/ss_err.h $(srcdir)/debugfs.h \ @@ -101,3 +101,4 @@ dump.o: $(srcdir)/dump.c $(srcdir)/debugfs.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(top_srcdir)/lib/ext2fs/bitops.h + diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct index 7e070451..00cd6c18 100644 --- a/debugfs/debug_cmds.ct +++ b/debugfs/debug_cmds.ct @@ -10,9 +10,12 @@ request do_show_debugfs_params, "Show debugfs parameters", request do_open_filesys, "Open a filesystem", open_filesys, open; -request do_close_filesys, "Close a filesystem", +request do_close_filesys, "Close the filesystem", close_filesys, close; +request do_dirty_filesys, "Mark the filesystem as dirty", + dirty_filesys, dirty; + request do_init_filesys, "Initalize a filesystem (DESTROYS DATA)", init_filesys; diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c index 7a4fc49f..0797a889 100644 --- a/debugfs/debugfs.c +++ b/debugfs/debugfs.c @@ -248,6 +248,14 @@ void do_show_super_stats(int argc, char *argv[]) close_pager(out); } +void do_dirty_filesys(int argc, char *argv[]) +{ + if (check_fs_open(argv[0])) + return; + + ext2fs_mark_super_dirty(current_fs); +} + struct list_blocks_struct { FILE *f; int total; @@ -565,7 +573,7 @@ static void modify_u8(char *com, const char *prompt, const char *format, __u8 *val) { char buf[200]; - u_char v; + unsigned long v; char *tmp; sprintf(buf, format, *val); @@ -575,7 +583,7 @@ static void modify_u8(char *com, const char *prompt, buf[strlen (buf) - 1] = '\0'; if (!buf[0]) return; - v = strtol(buf, &tmp, 0); + v = strtoul(buf, &tmp, 0); if (*tmp) com_err(com, 0, "Bad value - %s", buf); else @@ -586,7 +594,7 @@ static void modify_u16(char *com, const char *prompt, const char *format, __u16 *val) { char buf[200]; - u_short v; + unsigned long v; char *tmp; sprintf(buf, format, *val); @@ -596,7 +604,7 @@ static void modify_u16(char *com, const char *prompt, buf[strlen (buf) - 1] = '\0'; if (!buf[0]) return; - v = strtol(buf, &tmp, 0); + v = strtoul(buf, &tmp, 0); if (*tmp) com_err(com, 0, "Bad value - %s", buf); else @@ -607,7 +615,7 @@ static void modify_u32(char *com, const char *prompt, const char *format, __u32 *val) { char buf[200]; - u_long v; + unsigned long v; char *tmp; sprintf(buf, format, *val); @@ -617,7 +625,7 @@ static void modify_u32(char *com, const char *prompt, buf[strlen (buf) - 1] = '\0'; if (!buf[0]) return; - v = strtol(buf, &tmp, 0); + v = strtoul(buf, &tmp, 0); if (*tmp) com_err(com, 0, "Bad value - %s", buf); else diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c index 8dea6eb2..a307d8be 100644 --- a/debugfs/lsdel.c +++ b/debugfs/lsdel.c @@ -173,7 +173,7 @@ void do_lsdel(int argc, char **argv) printf("%6lu %6d %6o %6lu %4d/%4d %s", delarray[i].ino, delarray[i].uid, delarray[i].mode, delarray[i].size, delarray[i].free_blocks, delarray[i].num_blocks, - ctime(&delarray[i].dtime)); + time_to_string(delarray[i].dtime)); } error_out: diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 00000000..b3588e33 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,59 @@ +# +# Makefile for the doc directory +# + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +top_builddir = .. +my_dir = doc +INSTALL = @INSTALL@ + +@MCONFIG@ + +DVI=texi2dvi +DVIPS=dvips -o "$@" +INFO=makeinfo +HTML=texi2html +RM=/bin/rm -rf + +all:: libext2fs.info libext2fs.dvi + +install-doc-libs: + $(RM) $(DESTDIR)$(infodir)/libext2fs.info*.gz + $(top_srcdir)/mkinstalldirs $(DESTDIR)$(infodir) + for i in libext2fs.info* ; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(infodir)/$$i ; \ + done + gzip -9 $(DESTDIR)$(infodir)/libext2fs.info* + +libext2fs.info: $(srcdir)/libext2fs.texinfo + $(INFO) $(srcdir)/libext2fs.texinfo + +libext2fs.dvi: $(srcdir)/libext2fs.texinfo + $(DVI) $(srcdir)/libext2fs.texinfo + +.PHONY: clean +clean:: clean-all + +.PHONY: clean-all +clean-all:: clean-tex clean-backup clean-final clean-tarfiles + +.PHONY: clean-final +clean-final:: + $(RM) *.ps *.info *.info-? *.html + +.PHONY: clean-tex +clean-tex:: + $(RM) *.aux *.cp *.dvi *.fn *.ky *.log *.pg *.toc *.tp *.vr *.fns + +.PHONY: clean-backup +clean-backup:: + $(RM) *~ #* + +.PHONY: clean-tarfiles +clean-tarfiles:: + $(RM) *.tar *.tar.gz *.tgz + + + diff --git a/doc/libext2fs.texinfo b/doc/libext2fs.texinfo new file mode 100644 index 00000000..9769985e --- /dev/null +++ b/doc/libext2fs.texinfo @@ -0,0 +1,929 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename libext2fs.info +@settitle The EXT2FS Library (version 1.07) +@synindex tp fn +@comment %**end of header + +@ifinfo +@format +START-INFO-DIR-ENTRY +* libext2fs: (libext2fs.info). The EXT2FS library. +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@c smallbook + +@iftex +@finalout +@end iftex + +@c Note: the edition number is listed in *three* places; please update +@c all three. Also, update the month and year where appropriate. + +@c ==> Update edition number for settitle and subtitle, and in the +@c ==> following paragraph; update date, too. + + +@ifinfo +This file documents the ext2fs library, a library for manipulating the +ext2 filesystem. + +Copyright (C) 1997 Theodore Ts'o + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the author. +@end ifinfo + +@setchapternewpage on +@titlepage +@c use the new format for titles + +@title The EXT2FS Library +@subtitle The EXT2FS Library +@subtitle Version 1.07 +@subtitle February 1997 + +@author by Theodore Ts'o + +@c Include the Distribution inside the titlepage so +@c that headings are turned off. + +@tex +\global\parindent=0pt +\global\parskip=8pt +\global\baselineskip=13pt +@end tex + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1997 Theodore Ts'o + +@sp 2 + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by the Foundation. +@end titlepage +@headings double + +@ifinfo +@node Top, Introduction to the EXT2FS Library, (dir), (dir) + +@top The EXT2FS Library + +This manual documents the EXT2FS Library, version 1.07. + +@end ifinfo + +@menu +* Introduction to the EXT2FS Library:: +* EXT2FS Library Functions:: +* Concept Index:: +* Function Index:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Introduction to the EXT2FS Library, EXT2FS Library Functions, Top, Top +@comment node-name, next, previous, up +@chapter Introduction to the EXT2FS Library + +The EXT2FS library is designed to allow user-level programs to +manipulate an ext2 filesystem. + +@node EXT2FS Library Functions, Concept Index, Introduction to the EXT2FS Library, Top +@comment node-name, next, previous, up +@chapter EXT2FS Library Functions + +@menu +* Filesystem-level functions:: +* Inode Functions:: +* Directory functions:: +* Bitmap Functions:: +* Badblocks list management:: +* Directory-block list management:: +* Byte-swapping functions:: +* Other functions:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Filesystem-level functions, Inode Functions, EXT2FS Library Functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Filesystem-level functions + +The following functions operate on a filesystem handle. Most EXT2FS +Library functions require a filesystem handle as their first argument. +There are two functions which create a filesystem handle, +@code{ext2fs_open} and @code{ext2fs_initialize}. + +The filesystem can also be closed using @code{ext2fs_close}, and any +changes to the superblock and group descripts can be written out to disk +using @code{ext2fs_flush}. + +@menu +* Opening an ext2 filesystem:: +* Closing and flushing out changes:: +* Initializing a filesystem:: +* Filesystem flag functions:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Opening an ext2 filesystem, Closing and flushing out changes, Filesystem-level functions, Filesystem-level functions +@comment node-name, next, previous, up +@subsection Opening an ext2 filesystem + +Most libext2fs functions take a filesystem handle of type +@code{ext2_filsys}. A filesystem handle is created either by opening +an existing function using @code{ext2fs_open}, or by initializing a new +filesystem using @code{ext2fs_initialize}. + +@deftypefun errcode_t ext2fs_open (const char *@var{name}, int @var{flags}, int @var{superblock}, int @var{block_size}, io_manager @var{manager}, ext2_filsys *@var{ret_fs}) + +Opens a filesystem named @var{name}, using the the io_manager +@var{manager} to define the input/output routines needed to read and +write the filesystem. In the case of the @code{unix_io} io_manager, +@var{name} is interpreted as the Unix filename of the filesystem image. +This is often a device file, such as @file{/dev/hda1}. + +The @var{superblock} parameter specifies the block number of the +superblock which should be used when opening the filesystem. +If @var{superblock} is zero, @code{ext2fs_open} will use the primary +superblock located at offset 1024 bytes from the start of the filesystem +image. + +The @var{block_size} parameter specifies the block size used by the +filesystem. Normally this is determined automatically from the +filesystem uperblock. If @var{block_size} is non-zero, it must match +the block size found in the superblock, or the error +@code{EXT2_ET_UNEXPECTED_BLOCK_SIZE} will be returned. The +@var{block_size} parameter is also used to help fund the superblock when +@var{superblock} is non-zero. + +The @var{flags} argument contains a bitmask of flags which control how +the filesystem open should be handled. + +@table @code +@item EXT2_FLAG_RW +Open the filesystem for reading and writing. Without this flag, the +filesystem is opened for reading only. + +@item EXT2_FLAG_FORCE +Open the filesystem regardless of the feature sets listed in the +superblock. + +@end table +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Closing and flushing out changes, Initializing a filesystem, Opening an ext2 filesystem, Filesystem-level functions +@comment node-name, next, previous, up +@subsection Closing and flushing out changes + +@deftypefun errcode_t ext2fs_flush (ext2_filsys @var{fs}) + +Write any changes to the high-level filesystem data structures in the +@var{fs} filesystem. The following data structures will be written out: + +@itemize @bullet +@item The filesystem superblock +@item The filesystem group descriptors +@item The filesystem bitmaps, if read in via @code{ext2fs_read_bitmaps}. +@end itemize + +@end deftypefun + +@deftypefun void ext2fs_free (ext2_filsys @var{fs}) + +Close the io_manager abstraction for @var{fs} and release all memory +associated with the filesystem handle. +@end deftypefun + +@deftypefun errcode_t ext2fs_close (ext2_filsys @var{fs}) + +Flush out any changes to the high-level filesystem data structures using +@code{ext2fs_flush} if the filesystem is marked dirty; then close and +free the filesystem using @code{ext2fs_free}. + +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Initializing a filesystem, Filesystem flag functions, Closing and flushing out changes, Filesystem-level functions +@comment node-name, next, previous, up +@subsection Initializing a filesystem + +An ext2 filesystem is initializing by the @code{mke2fs} program. The +two functions described here, @code{ext2fs_initialize} and +@code{ext2fs_allocate_tables} do much of the initial work for setting up +a filesystem. However, they don't do the whole job. @code{mke2fs} +calls @code{ext2fs_initialize} to set up the filesystem superblock, and +calls @code{ext2fs_allocate_tables} to allocate space for the inode +table, and the inode and block bitmaps. In addition, @code{mke2fs} must +also initialize the inode tables by clearing them with zeros, create the +root and lost+found directories, and reserve the reserved inodes. + +@deftypefun errcode_t ext2fs_initialize (const char *@var{name}, int @var{flags}, struct ext2_super_block *@var{param}, io_manager @var{manager}, ext2_filsys *@var{ret_fs}) + +This function is used by the @code{mke2fs} program to initialize a +filesystem. The @code{ext2fs_initialize} function creates a filesystem +handle which is returned in @var{ret_fs} that has been properly setup +for a filesystem to be located in @var{name}, using the io_manager +@var{manager}. The prototype superblock in @var{param} is used to +supply parameters such as the number of blocks in the filesystem, the +block size, etc. + +The @code{ext2fs_initialize} function does not actually do any I/O; that +will be done when the application program calls @code{ext2fs_close} or +@code{ext2fs_flush}. Also, this function only initializes the +superblock and group descriptor structures. It does not create the +inode table or the root directory. This must be done by the calling +application, such as @code{mke2fs}. + +The following values may be set in the @var{param} prototype superblock; +if a value of 0 is found in a field, @code{ext2fs_initialize} will use a +default value. The calling application should zero out the prototype +entire superblock, and then fill in any appropriate values. + +@table @code + +@item s_blocks_count +The number of blocks in the filesystem. This parameter is mandatory and +must be set by the calling application. + +@item s_inodes_count +The number of inodes in the filesystem. The +default value is determined by calculating the size of the filesystem, +and creating one inode for every 4096 bytes. + +@item s_r_blocks_count +The number of blocks which should be reserved for the superuser. The +default value is zero blocks. + +@item s_log_block_size +The blocksize of the filesystem. Valid values are 0 (1024 bytes), 1 +(2048 bytes), or 2 (4096 bytes). The default blocksize is 1024 bytes. + +@item s_log_frag_size +The size of fragments. The ext2 filesystem does not support fragments +(and may never support fragments). Currently this field must be the +same as @code{s_log_block_size}. + +@item s_first_data_block +The first data block for the filesystem. For filesystem with a +blocksize of 1024 bytes, this value must be at least 1, since the +superblock is located in block number 1. For filesystems with larger +blocksizes, the superblock is still located at an offset of 1024 bytes, +so the superblock is located in block number 0. By default, this value +is set to 1 for filesystems with a block size of 1024 bytes, or 0 for +filesystems with larger blocksizes. + +@item s_max_mnt_count +This field defines the number of times that the filesystem can be +mounted before it should be checked using @code{e2fsck}. When +@code{e2fsck} is run without the @samp{-f} option, @code{e2fsck} will +skip the filesystem check if the number of times that the filesystem has +been mounted is less than @code{s_max_mnt_count} and if the interval +between the last time a filesystem check was performed and the current +time is less than @code{s_checkinterval} (see below). The default value +of @code{s_max_mnt_count} is 20. + +@item s_checkinterval +This field defines the minimal interval between filesystem checks. See +the previous entry for a discussion of how this field is used by +@code{e2fsck}. The default value of this field is 180 days (six +months). + +@item s_errors +This field defines the behavior which should be used by the kernel of +errors are detected in the filesystem. Possible values include: + +@table @samp +@item EXT2_ERRORS_CONTINUE +Continue execution when errors are detected. + +@item EXT2_ERRORS_RO +Remount the filesystem read-only. + +@item EXT2_ERRORS_PANIC +Panic. + +@end table + +The default behavior is @samp{EXT2_ERRORS_CONTINUE}. + +@end table + +@end deftypefun + +@deftypefun errcode_t ext2fs_allocate_tables (ext2_filsys @var{fs}) +Allocate space for the inode table and the block and inode bitmaps. The +inode tables and block and inode bitmaps aren't actually initialized; +this function just allocates the space for them. +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Filesystem flag functions, , Initializing a filesystem, Filesystem-level functions +@comment node-name, next, previous, up +@subsection Filesystem flag functions + +The filesystem handle has a number of flags which can be manipulated +using the following function. Some of these flags affect how the +libext2fs filesystem behaves; others are provided solely for the +application's convenience. + +@deftypefun void ext2fs_mark_changed (ext2_filsys @var{fs}) +@deftypefunx int ext2fs_test_changed (ext2_filsys @var{fs}) +This flag indicates whether or not the filesystem has been changed. +It is not used by the ext2fs library. +@end deftypefun + +@deftypefun void ext2fs_mark_super_dirty (ext2_filsys @var{fs}) +Mark the filesystem @var{fs} as being dirty; this will cause +the superblock information to be flushed out when @code{ext2fs_close} is +called. @code{ext2fs_mark_super_dirty} will also set the filesystem +changed flag. The dirty flag is automatically cleared by +@code{ext2fs_flush} when the superblock is written to disk. +@end deftypefun + +@deftypefun void ext2fs_mark_valid (ext2_filsys @var{fs}) +@deftypefunx void ext2fs_unmark_valid (ext2_filsys @var{fs}) +@deftypefunx int ext2fs_test_valid (ext2_filsys @var{fs}) +This flag indicates whether or not the filesystem is free of errors. +It is not used by libext2fs, and is solely for the application's +convenience. +@end deftypefun + +@deftypefun void ext2fs_mark_ib_dirty (ext2_filsys @var{fs}) +@deftypefunx void ext2fs_mark_bb_dirty (ext2_filsys @var{fs}) +@deftypefunx int ext2fs_test_ib_dirty (ext2_filsys @var{fs}) +@deftypefunx int ext2fs_test_bb_dirty (ext2_filsys @var{fs}) +These flags indicate whether or not the inode or block bitmaps have been +modified. If the flag is set, it will cause the appropriate bitmap +to be written when the filesystem is closed or flushed. +@end deftypefun + + + +@c ---------------------------------------------------------------------- + +@node Inode Functions, Directory functions, Filesystem-level functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Inode Functions + +@menu +* Reading and writing inodes:: +* Iterating over inodes in a filesystem:: +* Iterating over blocks in an inode:: +* Inode Convenience Functions:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Reading and writing inodes, Iterating over inodes in a filesystem, Inode Functions, Inode Functions +@comment node-name, next, previous, up +@subsection Reading and writing inodes + +@deftypefun errcode_t ext2fs_read_inode (ext2_filsys @var{fs}, ino_t @var{ino}, struct ext2_inode *@var{inode}) +Read the inode number @var{ino} into @var{inode}. +@end deftypefun + +@deftypefun errcode_t ext2fs_write_inode(ext2_filsys @var{fs}, ino_t @var{ino}, struct ext2_inode *@var{inode}) +Write @var{inode} to inode @var{ino}. +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Iterating over inodes in a filesystem, Iterating over blocks in an inode, Reading and writing inodes, Inode Functions +@comment node-name, next, previous, up +@subsection Iterating over inodes in a filesystem + +The inode_scan abstraction is useful for iterating over all the inodes +in a filesystem. + +@deftypefun errcode_t ext2fs_open_inode_scan (ext2_filsys @var{fs}, int @var{buffer_blocks}, ext2_inode_scan *@var{scan}) +Initialize the iteration variable @var{scan}. This variable is used by +@code{ext2fs_get_next_inode}. The @var{buffer_blocks} parameter +controls how many blocks of the inode table are read in at a time. A +large number of blocks requires more memory, but reduces the overhead in +seeking and reading from the disk. If @var{buffer_blocks} is zero, a +suitable default value will be used. +@end deftypefun + +@deftypefun void ext2fs_close_inode_scan (ext2_inode_scan @var{scan}) +Release the memory associated with @var{scan} and invalidate it. +@end deftypefun + +@deftypefun errcode_t ext2fs_get_next_inode (ext2_inode_scan @var{scan}, ino_t *@var{ino}, struct ext2_inode *@var{inode}) + +This function returns the next inode from the filesystem; the inode +number of the inode is stored in @var{ino}, and the inode is stored in +@var{inode}. + +If the inode is located in a block that has been marked as bad, +@code{ext2fs_get_next_inode} will return the error +@code{EXT2_ET_BAD_BLOCK_IN_INODE_TABLE}. +@end deftypefun + +@deftypefun errcode_t ext2fs_inode_scan_goto_blockgroup (ext2_inode_scan @var{scan}, int @var{group}) +Start the inode scan at a particular ext2 blockgroup, @var{group}. +This function may be safely called at any time while @var{scan} is valid. +@end deftypefun + +@deftypefun void ext2fs_set_inode_callback (ext2_inode_scan @var{scan}, errcode_t (*done_group)(ext2_filsys @var{fs}, ext2_inode_scan @var{scan}, dgrp_t @var{group}, void * @var{private}), void *@var{done_group_data}) +Register a callback function which will be called by +@code{ext2_get_next_inode} when all of the inodes in a block group have +been processed. +@end deftypefun + +@deftypefun int ext2fs_inode_scan_flags (ext2_inode_scan @var{scan}, int @var{set_flags}, int @var{clear_flags}) + +Set the scan_flags @var{set_flags} and clear the scan_flags @var{clear_flags}. +The following flags can be set using this interface: + +@table @samp + +@item EXT2_SF_SKIP_MISSING_ITABLE +When a block group is missing an inode table, skip it. If this flag is +not set @code{ext2fs_get_next_inode} will return the error +EXT2_ET_MISSING_INODE_TABLE. + +@end table + +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Iterating over blocks in an inode, Inode Convenience Functions, Iterating over inodes in a filesystem, Inode Functions +@comment node-name, next, previous, up +@subsection Iterating over blocks in an inode + +@deftypefun errcode_t ext2fs_block_iterate (ext2_filsys @var{fs}, ino_t @var{ino}, int @var{flags}, char *block_buf, int (*func)(ext2_filsys @var{fs}, blk_t *@var{blocknr}, int @var{blockcnt}, void *@var{private}), void *@var{private}) +@end deftypefun + +@deftypefun errcode_t ext2fs_block_iterate2 (ext2_filsys @var{fs}, ino_t @var{ino}, int @var{flags}, char *@var{block}_buf, int (*func)(ext2_filsys @var{fs}, blk_t *@var{blocknr}, int @var{blockcnt}, blk_t @var{ref_blk}, int @var{ref_offset}, void *@var{private}), void *@var{private}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Inode Convenience Functions, , Iterating over blocks in an inode, Inode Functions +@comment node-name, next, previous, up +@subsection Convenience functions for Inodes + +@deftypefun errcode_t ext2fs_get_blocks (ext2_filsys @var{fs}, ino_t @var{ino}, blk_t *@var{blocks}) +@end deftypefun + +@deftypefun errcode_t ext2fs_check_directory (ext2_filsys @var{fs}, ino_t @var{ino}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Directory functions, Bitmap Functions, Inode Functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Directory functions + +@menu +* Directory block functions:: +* Iterating over a directory:: +* Creating and expanding directories:: +* Creating and removing directory entries:: +* Looking up filenames:: +* Translating inode numbers to filenames:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Directory block functions, Iterating over a directory, Directory functions, Directory functions +@comment node-name, next, previous, up +@subsection Directory block functions + +@deftypefun errcode_t ext2fs_read_dir_block (ext2_filsys @var{fs}, blk_t @var{block}, void *@var{buf}) +@end deftypefun + +@deftypefun errcode_t ext2fs_write_dir_block (ext2_filsys @var{fs}, blk_t @var{block}, void *@var{buf}) +@end deftypefun + +@deftypefun errcode_t ext2fs_new_dir_block (ext2_filsys @var{fs}, ino_t @var{dir}_ino, ino_t @var{parent_ino}, char **@var{block}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Iterating over a directory, Creating and expanding directories, Directory block functions, Directory functions +@comment node-name, next, previous, up +@subsection Iterating over a directory + +@deftypefun errcode_t ext2fs_dir_iterate (ext2_filsys @var{fs}, ino_t @var{dir}, int @var{flags}, char *@var{block_buf}, int (*@var{func})(struct ext2_dir_entry *@var{dirent}, int @var{offset}, int @var{blocksize}, char *@var{buf}, void *@var{private}), void *@var{private}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Creating and expanding directories, Creating and removing directory entries, Iterating over a directory, Directory functions +@comment node-name, next, previous, up +@subsection Creating and expanding directories + +@deftypefun errcode_t ext2fs_mkdir (ext2_filsys @var{fs}, ino_t @var{parent}, ino_t @var{inum}, const char *@var{name}) +@end deftypefun + +@deftypefun errcode_t ext2fs_expand_dir (ext2_filsys @var{fs}, ino_t @var{dir}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Creating and removing directory entries, Looking up filenames, Creating and expanding directories, Directory functions +@comment node-name, next, previous, up +@subsection Creating and removing directory entries + +@deftypefun errcode_t ext2fs_link (ext2_filsys @var{fs}, ino_t @var{dir}, const char *@var{name}, ino_t @var{ino}, int flags) +@end deftypefun + +@deftypefun errcode_t ext2fs_unlink (ext2_filsys @var{fs}, ino_t @var{dir}, const char *@var{name}, ino_t @var{ino}, int @var{flags}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Looking up filenames, Translating inode numbers to filenames, Creating and removing directory entries, Directory functions +@comment node-name, next, previous, up +@subsection Looking up filenames + +@deftypefun errcode_t ext2fs_lookup (ext2_filsys @var{fs}, ino_t @var{dir}, const char *@var{name}, int @var{namelen}, char *@var{buf}, ino_t *@var{inode}) +@end deftypefun + +@deftypefun errcode_t ext2fs_namei (ext2_filsys @var{fs}, ino_t @var{root}, ino_t @var{cwd}, const char *@var{name}, ino_t *@var{inode}) +@end deftypefun + +@deftypefun errcode_t ext2fs_namei_follow (ext2_filsys @var{fs}, ino_t @var{root}, ino_t @var{cwd}, const char *@var{name}, ino_t *@var{inode}) +@end deftypefun + +@deftypefun errcode_t ext2fs_follow_link (ext2_filsys @var{fs}, ino_t @var{root}, ino_t @var{cwd}, ino_t @var{inode}, ino_t *@var{res}_inode) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Translating inode numbers to filenames, , Looking up filenames, Directory functions +@comment node-name, next, previous, up +@subsection Translating inode numbers to filenames + +@deftypefun errcode_t ext2fs_get_pathname (ext2_filsys @var{fs}, ino_t @var{dir}, ino_t @var{ino}, char **@var{name}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Bitmap Functions, Badblocks list management, Directory functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Bitmap Functions + +@menu +* Reading and Writing Bitmaps:: +* Allocating Bitmaps:: +* Free bitmaps:: +* Bitmap Operations:: +* Comparing bitmaps:: +* Modifying Bitmaps:: +* Resizing Bitmaps:: +* Clearing Bitmaps:: +@end menu + +@c ---------------------------------------------------------------------- + +@node Reading and Writing Bitmaps, Allocating Bitmaps, Bitmap Functions, Bitmap Functions +@comment node-name, next, previous, up +@subsection Reading and Writing Bitmaps + +@deftypefun errcode_t ext2fs_write_inode_bitmap (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2fs_write_block_bitmap (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2fs_read_inode_bitmap (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2fs_read_block_bitmap (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2fs_read_bitmaps (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2fs_write_bitmaps (ext2_filsys @var{fs}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Allocating Bitmaps, Free bitmaps, Reading and Writing Bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Allocating Bitmaps + +@deftypefun errcode_t ext2fs_allocate_generic_bitmap (__u32 @var{start}, __u32 @var{end}, _u32 @var{real_end}, const char *@var{descr}, ext2fs_generic_bitmap *@var{ret}) +@end deftypefun + +@deftypefun errcode_t ext2fs_allocate_block_bitmap (ext2_filsys @var{fs}, const char *@var{descr}, ext2fs_block_bitmap *@var{ret}) +@end deftypefun + +@deftypefun errcode_t ext2fs_allocate_inode_bitmap (ext2_filsys @var{fs}, const char *@var{descr}, ext2fs_inode_bitmap *@var{ret}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Free bitmaps, Bitmap Operations, Allocating Bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Freeing bitmaps + + +@deftypefun void ext2fs_free_generic_bitmap (ext2fs_inode_bitmap @var{bitmap}) +@end deftypefun + +@deftypefun void ext2fs_free_block_bitmap (ext2fs_block_bitmap @var{bitmap}) +@end deftypefun + +@deftypefun void ext2fs_free_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Bitmap Operations, Comparing bitmaps, Free bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Bitmap Operations + +@deftypefun void ext2fs_mark_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +@deftypefunx void ext2fs_unmark_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +@deftypefunx int ext2fs_test_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +These functions set, clear, and test bits in a block bitmap @var{bitmap}. +@end deftypefun + + +@deftypefun void ext2fs_mark_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +@deftypefunx void ext2fs_unmark_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +@deftypefunx int ext2fs_test_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +These functions set, clear, and test bits in an inode bitmap @var{bitmap}. +@end deftypefun + +@deftypefun void ext2fs_fast_mark_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +@deftypefunx void ext2fs_fast_unmark_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +@deftypefunx int ext2fs_fast_test_block_bitmap (ext2fs_block_bitmap @var{bitmap}, blk_t @var{block}) + +@deftypefunx void ext2fs_fast_mark_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +@deftypefunx void ext2fs_fast_unmark_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +@deftypefunx int ext2fs_fast_test_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{inode}) + +These ``fast'' functions are like their normal counterparts; however, +they are implemented as inline functions and do not perform bounds +checks on the inode number or block number; they are assumed to be +correct. They should only be used in speed-critical applications, where +the inode or block number has already been validated by other means. +@end deftypefun + +@deftypefun blk_t ext2fs_get_block_bitmap_start (ext2fs_block_bitmap @var{bitmap}) +@deftypefunx ino_t ext2fs_get_inode_bitmap_start (ext2fs_inode_bitmap @var{bitmap}) +Return the first inode or block which is stored in the bitmap. +@end deftypefun + +@deftypefun blk_t ext2fs_get_block_bitmap_end (ext2fs_block_bitmap @var{bitmap}) +@deftypefunx ino_t ext2fs_get_inode_bitmap_end (ext2fs_inode_bitmap @var{bitmap}) + +Return the first inode or block which is stored in the bitmap. +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Comparing bitmaps, Modifying Bitmaps, Bitmap Operations, Bitmap Functions +@comment node-name, next, previous, up +@subsection Comparing bitmaps + +@deftypefun errcode_t ext2fs_compare_block_bitmap (ext2fs_block_bitmap @var{bm1}, ext2fs_block_bitmap @var{bm2}) +@end deftypefun + +@deftypefun errcode_t ext2fs_compare_inode_bitmap (ext2fs_inode_bitmap @var{bm1}, ext2fs_inode_bitmap @var{bm2}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Modifying Bitmaps, Resizing Bitmaps, Comparing bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Modifying Bitmaps + +@deftypefun errcode_t ext2fs_fudge_inode_bitmap_end (ext2fs_inode_bitmap @var{bitmap}, ino_t @var{end}, ino_t *@var{oend}) +@end deftypefun + +@deftypefun errcode_t ext2fs_fudge_block_bitmap_end (ext2fs_block_bitmap @var{bitmap}, blk_t @var{end}, blk_t *@var{oend}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Resizing Bitmaps, Clearing Bitmaps, Modifying Bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Resizing Bitmaps + +@deftypefun errcode_t ext2fs_resize_generic_bitmap (__u32 @var{new_end}, __u32 @var{new_real_end}, ext2fs_generic_bitmap @var{bmap}) +@end deftypefun + +@deftypefun errcode_t ext2fs_resize_inode_bitmap (__u32 @var{new_end}, __u32 @var{new_real_end}, ext2fs_inode_bitmap @var{bmap}) +@end deftypefun + +@deftypefun errcode_t ext2fs_resize_block_bitmap (__u32 @var{new_end}, __u32 @var{new_real_end}, ext2fs_block_bitmap @var{bmap}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Clearing Bitmaps, , Resizing Bitmaps, Bitmap Functions +@comment node-name, next, previous, up +@subsection Clearing Bitmaps + +@deftypefun void ext2fs_clear_inode_bitmap (ext2fs_inode_bitmap @var{bitmap}) +@end deftypefun + +@deftypefun void ext2fs_clear_block_bitmap (ext2fs_block_bitmap @var{bitmap}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Badblocks list management, Directory-block list management, Bitmap Functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Badblocks list management + + +@deftypefun errcode_t ext2fs_badblocks_list_create (ext2_badblocks_list *@var{ret}, int @var{size}) +@end deftypefun + +@deftypefun void ext2fs_badblocks_list_free (ext2_badblocks_list @var{bb}) +@end deftypefun + +@deftypefun errcode_t ext2fs_badblocks_list_add (ext2_badblocks_list @var{bb}, blk_t @var{blk}) +@end deftypefun + +@deftypefun int ext2fs_badblocks_list_test (ext2_badblocks_list @var{bb}, blk_t @var{blk}) +@end deftypefun + +@deftypefun errcode_t ext2fs_badblocks_list_iterate_begin (ext2_badblocks_list @var{bb}, ext2_badblocks_iterate *@var{ret}) +@end deftypefun + +@deftypefun int ext2fs_badblocks_list_iterate (ext2_badblocks_iterate iter, blk_t *@var{blk}) +@end deftypefun + +@deftypefun void ext2fs_badblocks_list_iterate_end (ext2_badblocks_iterate @var{iter}) +@end deftypefun + +@deftypefun errcode_t ext2fs_update_bb_inode (ext2_filsys @var{fs}, ext2_badblocks_list @var{bb_list}) +@end deftypefun + +@deftypefun errcode_t ext2fs_read_bb_inode (ext2_filsys @var{fs}, ext2_badblocks_list *@var{bb_list}) +@end deftypefun + +@deftypefun errcode_t ext2fs_read_bb_FILE (ext2_filsys @var{fs}, FILE *f, ext2_badblocks_list *@var{bb_list}, void (*invalid)(ext2_filsys @var{fs}, blk_t @var{blk})) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Directory-block list management, Byte-swapping functions, Badblocks list management, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Directory-block list management + +@deftypefun errcode_t ext2fs_init_dblist (ext2_filsys @var{fs}, ext2_dblist *@var{ret_dblist}) +@end deftypefun + +@deftypefun void ext2fs_free_dblist (ext2_dblist @var{dblist}) +@end deftypefun + +@deftypefun errcode_t ext2fs_add_dir_block (ext2_dblist dblist, ino_t @var{ino}, blk_t @var{blk}, int @var{blockcnt}) +@end deftypefun + +@deftypefun errcode_t ext2fs_dblist_iterate (ext2_dblist dblist, int (*func)(ext2_filsys @var{fs}, struct ext2_db_entry *@var{db_info}, void *@var{private}), void *@var{private}) +@end deftypefun + +@deftypefun errcode_t ext2fs_dblist_dir_iterate +(ext2_dblist dblist, int flags, char *@var{block_buf}, int (*func)(ino_t @var{dir}, int @var{entry}, struct ext2_dir_entry *@var{dirent}, int @var{offset}, int @var{blocksize}, char *@var{buf}, void *@var{private}), void *@var{private}) +@end deftypefun + +@c ---------------------------------------------------------------------- + +@node Byte-swapping functions, Other functions, Directory-block list management, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Byte-swapping functions + +@deftypefun void ext2fs_swap_super (struct ext2_super_block * @var{super}) +@end deftypefun + +@deftypefun void ext2fs_swap_group_desc (struct ext2_group_desc *@var{gdp}) +@end deftypefun + +@deftypefun void ext2fs_swap_inode (ext2_filsys @var{fs}, struct ext2_inode *@var{to}, struct ext2_inode *@var{from}, int @var{hostorder}) +@end deftypefun + +@deftypefun int ext2fs_native_flag (void) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Other functions, , Byte-swapping functions, EXT2FS Library Functions +@comment node-name, next, previous, up +@section Other functions + +/* alloc.c */ +@deftypefun errcode_t ext2fs_new_inode (ext2_filsys @var{fs}, ino_t @var{dir}, int @var{mode}, ext2fs_inode_bitmap @var{map}, ino_t *@var{ret}) +@end deftypefun + +@deftypefun errcode_t ext2fs_new_block (ext2_filsys @var{fs}, blk_t @var{goal}, ext2fs_block_bitmap @var{map}, blk_t *@var{ret}) +@end deftypefun + +@deftypefun errcode_t ext2fs_get_free_blocks (ext2_filsys @var{fs}, blk_t @var{start}, blk_t @var{finish}, int @var{num}, ext2fs_block_bitmap @var{map}, blk_t *@var{ret}) +@end deftypefun + +/* check_desc.c */ +@deftypefun errcode_t ext2fs_check_desc (ext2_filsys @var{fs}) +@end deftypefun + +@deftypefun errcode_t ext2_get_num_dirs (ext2_filsys @var{fs}, ino_t *@var{ret_num_dirs}) +@end deftypefun + + +/* getsize.c */ +@deftypefun errcode_t ext2fs_get_device_size (const char *@var{file}, int @var{blocksize}, blk_t *@var{retblocks}) +@end deftypefun + + +/* ismounted.c */ +@deftypefun errcode_t ext2fs_check_if_mounted (const char *@var{file}, int *@var{mount_flags}) +@end deftypefun + + +/* valid_blk.c */ +@deftypefun int ext2_inode_has_valid_blocks (struct ext2_inode *@var{inode}) +@end deftypefun + + +/* inline functions */ +@deftypefun int ext2fs_group_of_blk (ext2_filsys @var{fs}, blk_t @var{blk}) +@end deftypefun + +@deftypefun int ext2fs_group_of_ino (ext2_filsys @var{fs}, ino_t @var{ino}) +@end deftypefun + + +@c ---------------------------------------------------------------------- + +@node Concept Index, Function Index, EXT2FS Library Functions, Top +@comment node-name, next, previous, up +@unnumbered Concept Index +@printindex cp + +@c ---------------------------------------------------------------------- + +@node Function Index, , Concept Index, Top +@comment node-name, next, previous, up +@unnumbered Function and Type Index +@printindex fn + + +@contents +@bye diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 00000000..4db7c68d --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,4761 @@ +%% TeX macros to handle Texinfo files. +%% $Id$ + +% Copyright (C) 1985, 86, 88, 90, 91, 92, 93, +% 94, 95, 1996 Free Software Foundation, Inc. + +%This texinfo.tex file is free software; you can redistribute it and/or +%modify it under the terms of the GNU General Public License as +%published by the Free Software Foundation; either version 2, or (at +%your option) any later version. + +%This texinfo.tex file 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 texinfo.tex file; see the file COPYING. If not, write +%to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +%Boston, MA 02111-1307, USA. + + +%In other words, you are welcome to use, share and improve this program. +%You are forbidden to forbid anyone else to use, share and improve +%what you give them. Help stamp out software-hoarding! + + +% Send bug reports to bug-texinfo@prep.ai.mit.edu. +% Please include a *precise* test case in each bug report. + + +% Make it possible to create a .fmt file just by loading this file: +% if the underlying format is not loaded, start by loading it now. +% Added by gildea November 1993. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi + +% This automatically updates the version number based on RCS. +\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}} +\deftexinfoversion$Revision$ +\message{Loading texinfo package [Version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}\message{} + \catcode`+=\active \catcode`\_=\active} + +% Save some parts of plain tex whose names we will redefine. + +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv = \equiv +\let\ptexi=\i +\let\ptexlbrace=\{ +\let\ptexrbrace=\} +\let\ptexstar=\* +\let\ptext=\t +\let\ptextilde=\~ + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} +\let\~ = \tie % And make it available as @~. + + +\message{Basics,} +\chardef\other=12 + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Set up fixed words for English. +\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi% +\def\putwordInfo{Info}% +\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi% +\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi% +\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi% +\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi% +\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi% +\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi% +\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi% +\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi% +\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi% + +% Ignore a token. +% +\def\gobble#1{} + +\hyphenation{ap-pen-dix} +\hyphenation{mini-buf-fer mini-buf-fers} +\hyphenation{eshell} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen \bindingoffset +\newdimen \normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{\tracingcommands2 \tracingstats2 + \tracingpages1 \tracingoutput1 \tracinglostchars1 + \tracingmacros2 \tracingparagraphs1 \tracingrestores1 + \showboxbreadth\maxdimen\showboxdepth\maxdimen +}% + +%---------------------Begin change----------------------- +% +%%%% For @cropmarks command. +% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\cornerlong \newdimen\cornerthick +\newdimen \topandbottommargin +\newdimen \outerhsize \newdimen \outervsize +\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks +\outerhsize=7in +%\outervsize=9.5in +% Alternative @smallbook page size is 9.25in +\outervsize=9.25in +\topandbottommargin=.75in +% +%---------------------End change----------------------- + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox \newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \hoffset=\normaloffset + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \escapechar = `\\ % use backslash in output files. + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + \shipout\vbox{% + \unvbox\headlinebox + \pagebody{#1}% + \unvbox\footlinebox + }% + }% + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +%%%% For @cropmarks command %%%% + +% Here is a modification of the main output routine for Near East Publications +% This provides right-angle cropmarks at all four corners. +% The contents of the page are centerlined into the cropmarks, +% and any desired binding offset is added as an \hskip on either +% site of the centerlined box. (P. A. MacKay, 12 November, 1986) +% +\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up +{\escapechar=`\\\relax % makes sure backslash is used in output files. + \shipout + \vbox to \outervsize{\hsize=\outerhsize + \vbox{\line{\ewtop\hfill\ewtop}} + \nointerlineskip + \line{\vbox{\moveleft\cornerthick\nstop} + \hfill + \vbox{\moveright\cornerthick\nstop}} + \vskip \topandbottommargin + \centerline{\ifodd\pageno\hskip\bindingoffset\fi + \vbox{ + {\let\hsize=\pagewidth \makeheadline} + \pagebody{#1} + {\let\hsize=\pagewidth \makefootline}} + \ifodd\pageno\else\hskip\bindingoffset\fi} + \vskip \topandbottommargin plus1fill minus1fill + \boxmaxdepth\cornerthick + \line{\vbox{\moveleft\cornerthick\nsbot} + \hfill + \vbox{\moveright\cornerthick\nsbot}} + \nointerlineskip + \vbox{\line{\ewbot\hfill\ewbot}} + }} + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi} +% +% Do @cropmarks to get crop marks +\def\cropmarks{\let\onepageout=\croppageout } + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg#1{% + \let\next = #1% + \begingroup + \obeylines + \futurelet\temp\parseargx +} + +% If the next token is an obeyed space (from an @example environment or +% the like), remove it and recurse. Otherwise, we're done. +\def\parseargx{% + % \obeyedspace is defined far below, after the definition of \sepspaces. + \ifx\obeyedspace\temp + \expandafter\parseargdiscardspace + \else + \expandafter\parseargline + \fi +} + +% Remove a single space (as the delimiter token to the macro call). +{\obeyspaces % + \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + % + % First remove any @c comment, then any @comment. + % Result of each macro is put in \toks0. + \argremovec #1\c\relax % + \expandafter\argremovecomment \the\toks0 \comment\relax % + % + % Call the caller's macro, saved as \next in \parsearg. + \expandafter\next\expandafter{\the\toks0}% + }% +} + +% Since all \c{,omment} does is throw away the argument, we can let TeX +% do that for us. The \relax here is matched by the \relax in the call +% in \parseargline; it could be more or less anything, its purpose is +% just to delimit the argument to the \c. +\def\argremovec#1\c#2\relax{\toks0 = {#1}} +\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} + +% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% @end itemize @c foo +% will have two active spaces as part of the argument with the +% `itemize'. Here we remove all active spaces from #1, and assign the +% result to \toks0. +% +% This loses if there are any *other* active characters besides spaces +% in the argument -- _ ^ +, for example -- since they get expanded. +% Fortunately, Texinfo does not define any such commands. (If it ever +% does, the catcode of the characters in questionwill have to be changed +% here.) But this means we cannot call \removeactivespaces as part of +% \argremovec{,omment}, since @c uses \parsearg, and thus the argument +% that \parsearg gets might well have any character at all in it. +% +\def\removeactivespaces#1{% + \begingroup + \ignoreactivespaces + \edef\temp{#1}% + \global\toks0 = \expandafter{\temp}% + \endgroup +} + +% Change the active space to expand to nothing. +% +\begingroup + \obeyspaces + \gdef\ignoreactivespaces{\obeyspaces\let =\empty} +\endgroup + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +%% These are used to keep @begin/@end levels from running away +%% Call \inENV within environments (after a \begingroup) +\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} +\def\ENVcheck{% +\ifENV\errmessage{Still within an environment. Type Return to continue.} +\endgroup\fi} % This is not perfect, but it should reduce lossage + +% @begin foo is the same as @foo, for now. +\newhelp\EMsimple{Type to continue.} + +\outer\def\begin{\parsearg\beginxxx} + +\def\beginxxx #1{% +\expandafter\ifx\csname #1\endcsname\relax +{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else +\csname #1\endcsname\fi} + +% @end foo executes the definition of \Efoo. +% +\def\end{\parsearg\endxxx} +\def\endxxx #1{% + \removeactivespaces{#1}% + \edef\endthing{\the\toks0}% + % + \expandafter\ifx\csname E\endthing\endcsname\relax + \expandafter\ifx\csname \endthing\endcsname\relax + % There's no \foo, i.e., no ``environment'' foo. + \errhelp = \EMsimple + \errmessage{Undefined command `@end \endthing'}% + \else + \unmatchedenderror\endthing + \fi + \else + % Everything's ok; the right environment has been started. + \csname E\endthing\endcsname + \fi +} + +% There is an environment #1, but it hasn't been started. Give an error. +% +\def\unmatchedenderror#1{% + \errhelp = \EMsimple + \errmessage{This `@end #1' doesn't have a matching `@#1'}% +} + +% Define the control sequence \E#1 to give an unmatched @end error. +% +\def\defineunmatchedend#1{% + \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +} + + +% Single-spacing is done by various environments (specifically, in +% \nonfillstart and \quotations). +\newskip\singlespaceskip \singlespaceskip = 12.5pt +\def\singlespace{% + % Why was this kern here? It messes up equalizing space above and below + % environments. --karl, 6may93 + %{\advance \baselineskip by -\singlespaceskip + %\kern \baselineskip}% + \setleading \singlespaceskip +} + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt \char '100}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt \char '173}} +\def\myrbrace {{\tt \char '175}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce actual \{ & \} command in an index. + \catcode`\{ = 12 \catcode`\} = 12 + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\@ = 0 \catcode`\\ = 12 + @gdef@lbracecmd[\{]% + @gdef@rbracecmd[\}]% +@endgroup + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown +% Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ptexi + \else\ifx\temp\jmacro \j + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=3000 } + +% @enddots{} is an end-of-sentence ellipsis. +\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000} + +% @! is an end-of-sentence bang. +\gdef\!{!\spacefactor=3000 } + +% @? is an end-of-sentence query. +\gdef\?{?\spacefactor=3000 } + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +\def\group{\begingroup + \ifnum\catcode13=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + % + % The \vtop we start below produces a box with normal height and large + % depth; thus, TeX puts \baselineskip glue before it, and (when the + % next line of text is done) \lineskip glue after it. (See p.82 of + % the TeXbook.) Thus, space below is not quite equal to space + % above. But it's pretty close. + \def\Egroup{% + \egroup % End the \vtop. + \endgroup % End the \group. + }% + % + \vtop\bgroup + % We have to put a strut on the last line in case the @group is in + % the midst of an example, rather than completely enclosing it. + % Otherwise, the interline space between the last line of the group + % and the first line afterwards is too small. But we can't put the + % strut in \Egroup, since there it would be on a line by itself. + % Hence this just inserts a strut at the beginning of each line. + \everypar = {\strut}% + % + % Since we have a strut on every line, we don't need any of TeX's + % normal interline spacing. + \offinterlineskip + % + % OK, but now we have to do something about blank + % lines in the input in @example-like environments, which normally + % just turn into \lisppar, which will insert no space now that we've + % turned off the interline space. Simplest is to make them be an + % empty paragraph. + \ifx\par\lisppar + \edef\par{\leavevmode \par}% + % + % Reset ^^M's definition to new definition of \par. + \obeylines + \fi + % + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\def\need{\parsearg\needx} + +% Old definition--didn't work. +%\def\needx #1{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000 +%\prevdepth=-1000pt +%}} + +\def\needx#1{% + % Go into vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % Don't add any leading before our big empty box, but allow a page + % break, since the best break might be right here. + \allowbreak + \nointerlineskip + \vtop to #1\mil{\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak +} + +% @br forces paragraph break + +\let\br = \par + +% @dots{} output some dots + +\def\dots{$\ldots$} + +% @page forces the start of a new page + +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\def\exdent{\parsearg\exdentyyy} +\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} + +% This defn is used inside nofill environments such as @example. +\def\nofillexdent{\parsearg\nofillexdentyyy} +\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount +\leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{TEXT} puts TEXT in the margin next to the current paragraph. + +\def\inmargin#1{% +\strut\vadjust{\nobreak\kern-\strutdepth + \vtop to \strutdepth{\baselineskip\strutdepth\vss + \llap{\rightskip=\inmarginspacing \vbox{\noindent #1}}\null}}} +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} + +%\hbox{{\rm#1}}\hfil\break}} + +% @include file insert text of that file as input. +% Allow normal characters that we make active in the argument (a file name). +\def\include{\begingroup + \catcode`\\=12 + \catcode`~=12 + \catcode`^=12 + \catcode`_=12 + \catcode`|=12 + \catcode`<=12 + \catcode`>=12 + \catcode`+=12 + \parsearg\includezzz} +% Restore active chars for included file. +\def\includezzz#1{\endgroup\begingroup + % Read the included file in a group so nested @include's work. + \def\thisfile{#1}% + \input\thisfile +\endgroup} + +\def\thisfile{} + +% @center line outputs that line, centered + +\def\center{\parsearg\centerzzz} +\def\centerzzz #1{{\advance\hsize by -\leftskip +\advance\hsize by -\rightskip +\centerline{#1}}} + +% @sp n outputs n lines of vertical space + +\def\sp{\parsearg\spxxx} +\def\spxxx #1{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other% +\parsearg \commentxxx} + +\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 } + +\let\c=\comment + +% @paragraphindent is defined for the Info formatting commands only. +\let\paragraphindent=\comment + +% Prevent errors for section commands. +% Used in @ignore and in failing conditionals. +\def\ignoresections{% +\let\chapter=\relax +\let\unnumbered=\relax +\let\top=\relax +\let\unnumberedsec=\relax +\let\unnumberedsection=\relax +\let\unnumberedsubsec=\relax +\let\unnumberedsubsection=\relax +\let\unnumberedsubsubsec=\relax +\let\unnumberedsubsubsection=\relax +\let\section=\relax +\let\subsec=\relax +\let\subsubsec=\relax +\let\subsection=\relax +\let\subsubsection=\relax +\let\appendix=\relax +\let\appendixsec=\relax +\let\appendixsection=\relax +\let\appendixsubsec=\relax +\let\appendixsubsection=\relax +\let\appendixsubsubsec=\relax +\let\appendixsubsubsection=\relax +\let\contents=\relax +\let\smallbook=\relax +\let\titlepage=\relax +} + +% Used in nested conditionals, where we have to parse the Texinfo source +% and so want to turn off most commands, in case they are used +% incorrectly. +% +\def\ignoremorecommands{% + \let\defcodeindex = \relax + \let\defcv = \relax + \let\deffn = \relax + \let\deffnx = \relax + \let\defindex = \relax + \let\defivar = \relax + \let\defmac = \relax + \let\defmethod = \relax + \let\defop = \relax + \let\defopt = \relax + \let\defspec = \relax + \let\deftp = \relax + \let\deftypefn = \relax + \let\deftypefun = \relax + \let\deftypevar = \relax + \let\deftypevr = \relax + \let\defun = \relax + \let\defvar = \relax + \let\defvr = \relax + \let\ref = \relax + \let\xref = \relax + \let\printindex = \relax + \let\pxref = \relax + \let\settitle = \relax + \let\setchapternewpage = \relax + \let\setchapterstyle = \relax + \let\everyheading = \relax + \let\evenheading = \relax + \let\oddheading = \relax + \let\everyfooting = \relax + \let\evenfooting = \relax + \let\oddfooting = \relax + \let\headings = \relax + \let\include = \relax + \let\lowersections = \relax + \let\down = \relax + \let\raisesections = \relax + \let\up = \relax + \let\set = \relax + \let\clear = \relax + \let\item = \relax +} + +% Ignore @ignore ... @end ignore. +% +\def\ignore{\doignore{ignore}} + +% Also ignore @ifinfo, @ifhtml, @html, @menu, and @direntry text. +% +\def\ifinfo{\doignore{ifinfo}} +\def\ifhtml{\doignore{ifhtml}} +\def\html{\doignore{html}} +\def\menu{\doignore{menu}} +\def\direntry{\doignore{direntry}} + +% Also ignore @macro ... @end macro. The user must run texi2dvi, +% which runs makeinfo to do macro expansion. Ignore @unmacro, too. +\def\macro{\doignore{macro}} +\let\unmacro = \comment + + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory = \comment + +% Ignore text until a line `@end #1'. +% +\def\doignore#1{\begingroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define a command to swallow text until we reach `@end #1'. + \long\def\doignoretext##1\end #1{\enddoignore}% + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \catcode32 = 10 + % + % And now expand that command. + \doignoretext +} + +% What we do to finish off ignored text. +% +\def\enddoignore{\endgroup\ignorespaces}% + +\newif\ifwarnedobs\warnedobsfalse +\def\obstexwarn{% + \ifwarnedobs\relax\else + % We need to warn folks that they may have trouble with TeX 3.0. + % This uses \immediate\write16 rather than \message to get newlines. + \immediate\write16{} + \immediate\write16{***WARNING*** for users of Unix TeX 3.0!} + \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} + \immediate\write16{If you are running another version of TeX, relax.} + \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} + \immediate\write16{ Then upgrade your TeX installation if you can.} + \immediate\write16{ (See ftp://ftp.gnu.ai.mit.edu/pub/gnu/TeX.README.)} + \immediate\write16{If you are stuck with version 3.0, run the} + \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} + \immediate\write16{ to use a workaround.} + \immediate\write16{} + \global\warnedobstrue + \fi +} + +% **In TeX 3.0, setting text in \nullfont hangs tex. For a +% workaround (which requires the file ``dummy.tfm'' to be installed), +% uncomment the following line: +%%%%%\font\nullfont=dummy\let\obstexwarn=\relax + +% Ignore text, except that we keep track of conditional commands for +% purposes of nesting, up to an `@end #1' command. +% +\def\nestedignore#1{% + \obstexwarn + % We must actually expand the ignored text to look for the @end + % command, so that nested ignore constructs work. Thus, we put the + % text into a \vbox and then do nothing with the result. To minimize + % the change of memory overflow, we follow the approach outlined on + % page 401 of the TeXbook: make the current font be a dummy font. + % + \setbox0 = \vbox\bgroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define `@end #1' to end the box, which will in turn undefine the + % @end command again. + \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % + % We are going to be parsing Texinfo commands. Most cause no + % trouble when they are used incorrectly, but some commands do + % complicated argument parsing or otherwise get confused, so we + % undefine them. + % + % We can't do anything about stray @-signs, unfortunately; + % they'll produce `undefined control sequence' errors. + \ignoremorecommands + % + % Set the current font to be \nullfont, a TeX primitive, and define + % all the font commands to also use \nullfont. We don't use + % dummy.tfm, as suggested in the TeXbook, because not all sites + % might have that installed. Therefore, math mode will still + % produce output, but that should be an extremely small amount of + % stuff compared to the main input. + % + \nullfont + \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont + \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont + \let\tensf = \nullfont + % Similarly for index fonts (mostly for their use in + % smallexample) + \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont + \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont + \let\indsf = \nullfont + % + % Don't complain when characters are missing from the fonts. + \tracinglostchars = 0 + % + % Don't bother to do space factor calculations. + \frenchspacing + % + % Don't report underfull hboxes. + \hbadness = 10000 + % + % Do minimal line-breaking. + \pretolerance = 10000 + % + % Do not execute instructions in @tex + \def\tex{\doignore{tex}} +} + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. Make sure the catcode of space is correct to avoid +% losing inside @example, for instance. +% +\def\set{\begingroup\catcode` =10 + \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. + \parsearg\setxxx} +\def\setxxx#1{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + \def\temp{#2}% + \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty + \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. + \fi + \endgroup +} +% Can't use \xdef to pre-expand #2 and save some time, since \temp or +% \next or other control sequences that we've defined might get us into +% an infinite loop. Consider `@set foo @cite{bar}'. +\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\def\clear{\parsearg\clearxxx} +\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} + +% @value{foo} gets the text saved in variable foo. +% +\def\value{\begingroup + \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. + \valuexxx} +\def\valuexxx#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {\{No value for ``#1''\}}% + \else + \csname SET#1\endcsname + \fi +\endgroup} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +\def\ifset{\parsearg\ifsetxxx} +\def\ifsetxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifsetfail + \else + \expandafter\ifsetsucceed + \fi +} +\def\ifsetsucceed{\conditionalsucceed{ifset}} +\def\ifsetfail{\nestedignore{ifset}} +\defineunmatchedend{ifset} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +\def\ifclear{\parsearg\ifclearxxx} +\def\ifclearxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifclearsucceed + \else + \expandafter\ifclearfail + \fi +} +\def\ifclearsucceed{\conditionalsucceed{ifclear}} +\def\ifclearfail{\nestedignore{ifclear}} +\defineunmatchedend{ifclear} + +% @iftex always succeeds; we read the text following, through @end +% iftex). But `@end iftex' should be valid only after an @iftex. +% +\def\iftex{\conditionalsucceed{iftex}} +\defineunmatchedend{iftex} + +% We can't just want to start a group at @iftex (for example) and end it +% at @end iftex, since then @set commands inside the conditional have no +% effect (they'd get reverted at the end of the group). So we must +% define \Eiftex to redefine itself to be its previous value. (We can't +% just define it to fail again with an ``unmatched end'' error, since +% the @ifset might be nested.) +% +\def\conditionalsucceed#1{% + \edef\temp{% + % Remember the current value of \E#1. + \let\nece{prevE#1} = \nece{E#1}% + % + % At the `@end #1', redefine \E#1 to be its previous value. + \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% + }% + \temp +} + +% We need to expand lots of \csname's, but we don't want to expand the +% control sequences after we've constructed them. +% +\def\nece#1{\expandafter\noexpand\csname#1\endcsname} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math means output in math mode. +% We don't use $'s directly in the definition of \math because control +% sequences like \math are expanded when the toc file is written. Then, +% we read the toc file back, the $'s will be normal characters (as they +% should be, according to the definition of Texinfo). So we must use a +% control sequence to switch into and out of math mode. +% +% This isn't quite enough for @math to work properly in indices, but it +% seems unlikely it will ever be needed there. +% +\let\implicitmath = $ +\def\math#1{\implicitmath #1\implicitmath} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{\implicitmath\ptexbullet\implicitmath} +\def\minus{\implicitmath-\implicitmath} + +\def\node{\ENVcheck\parsearg\nodezzz} +\def\nodezzz#1{\nodexxx [#1,]} +\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +\let\nwnode=\node +\let\lastnode=\relax + +\def\donoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\setref{\lastnode}\fi +\global\let\lastnode=\relax} + +\def\unnumbnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi +\global\let\lastnode=\relax} + +\def\appendixnoderef{\ifx\lastnode\relax\else +\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi +\global\let\lastnode=\relax} + +% @refill is a no-op. +\let\refill=\relax + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \readauxfile + \opencontents + \openindices + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + \comment % Ignore the actual filename. +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + +% \def\macro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\macroxxx} +% \def\macroxxx#1#2 \end macro{% +% \expandafter\gdef\macrotemp#1{#2}% +% \endgroup} + +%\def\linemacro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\linemacroxxx} +%\def\linemacroxxx#1#2 \end linemacro{% +%\let\parsearg=\relax +%\edef\macrotempx{\csname M\butfirst\expandafter\string\macrotemp\endcsname}% +%\expandafter\xdef\macrotemp{\parsearg\macrotempx}% +%\expandafter\gdef\macrotempx#1{#2}% +%\endgroup} + +%\def\butfirst#1{} + + +\message{fonts,} + +% Font-change commands. + +% Texinfo supports the sans serif font style, which plain TeX does not. +% So we set up a \sf analogous to plain's \rm, etc. +\newfam\sffam +\def\sf{\fam=\sffam \tensf} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this one. +\def\ttsl{\tenttsl} + +%% Try out Computer Modern fonts at \magstephalf +\let\mainmagstep=\magstephalf + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor +\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4} + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +\ifx\bigger\relax +\let\mainmagstep=\magstep1 +\setfont\textrm\rmshape{12}{1000} +\setfont\texttt\ttshape{12}{1000} +\else +\setfont\textrm\rmshape{10}{\mainmagstep} +\setfont\texttt\ttshape{10}{\mainmagstep} +\fi +% Instead of cmb10, you many want to use cmbx10. +% cmbx10 is a prettier font on its own, but cmb10 +% looks better when embedded in a line with cmr10. +\setfont\textbf\bfshape{10}{\mainmagstep} +\setfont\textit\itshape{10}{\mainmagstep} +\setfont\textsl\slshape{10}{\mainmagstep} +\setfont\textsf\sfshape{10}{\mainmagstep} +\setfont\textsc\scshape{10}{\mainmagstep} +\setfont\textttsl\ttslshape{10}{\mainmagstep} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun, etc. +\setfont\defbf\bxshape{10}{\magstep1} %was 1314 +\setfont\deftt\ttshape{10}{\magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} + +% Fonts for indices and small examples (9pt). +% We actually use the slanted font rather than the italic, +% because texinfo normally uses the slanted fonts for that. +% Do not make many font distinctions in general in the index, since they +% aren't very useful. +\setfont\ninett\ttshape{9}{1000} +\setfont\indrm\rmshape{9}{1000} +\setfont\indit\slshape{9}{1000} +\let\indsl=\indit +\let\indtt=\ninett +\let\indttsl=\ninett +\let\indsf=\indrm +\let\indbf=\indrm +\setfont\indsc\scshape{10}{900} +\font\indi=cmmi9 +\font\indsy=cmsy9 + +% Chapter (and unnumbered) fonts (17.28pt). +\setfont\chaprm\rmbshape{12}{\magstep2} +\setfont\chapit\itbshape{10}{\magstep3} +\setfont\chapsl\slbshape{10}{\magstep3} +\setfont\chaptt\ttbshape{12}{\magstep2} +\setfont\chapttsl\ttslshape{10}{\magstep3} +\setfont\chapsf\sfbshape{12}{\magstep2} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +% Section fonts (14.4pt). +\setfont\secrm\rmbshape{12}{\magstep1} +\setfont\secit\itbshape{10}{\magstep2} +\setfont\secsl\slbshape{10}{\magstep2} +\setfont\sectt\ttbshape{12}{\magstep1} +\setfont\secttsl\ttslshape{10}{\magstep2} +\setfont\secsf\sfbshape{12}{\magstep1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad. +% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded. +% \setfont\ssecsl\slshape{10}{\magstep1} +% \setfont\ssectt\ttshape{10}{\magstep1} +% \setfont\ssecsf\sfshape{10}{\magstep1} + +%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx. +%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than +%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1. +%\setfont\ssectt\ttshape{10}{1315} +%\setfont\ssecsf\sfshape{10}{1315} + +%\let\ssecbf=\ssecrm + +% Subsection fonts (13.15pt). +\setfont\ssecrm\rmbshape{12}{\magstephalf} +\setfont\ssecit\itbshape{10}{1315} +\setfont\ssecsl\slbshape{10}{1315} +\setfont\ssectt\ttbshape{12}{\magstephalf} +\setfont\ssecttsl\ttslshape{10}{\magstep1} +\setfont\ssecsf\sfbshape{12}{\magstephalf} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{\magstep1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled \magstep1 +% The smallcaps and symbol fonts should actually be scaled \magstep1.5, +% but that is not a standard magnification. + +% Fonts for title page: +\setfont\titlerm\rmbshape{12}{\magstep3} +\let\authorrm = \secrm + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts, we +% don't bother to reset \scriptfont and \scriptscriptfont (which would +% also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy + \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf + \textfont\ttfam = \tentt \textfont\sffam = \tensf +} + + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this so that font changes will continue to work +% in math mode, where it is the current \fam that is relevant in most +% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam +% \tenbf}, for example. By redefining \tenbf, we obviate the need to +% redefine \bf itself. +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl + \resetmathfonts} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf? +\def\indexfonts{% + \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl + \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc + \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy \let\tenttsl=\indttsl + \resetmathfonts \setleading{12pt}} + +% Set up the default fonts, so we can use them for creating boxes. +% +\textfonts + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000} +\setfont\shortcontbf\bxshape{12}{1000} +\setfont\shortcontsl\slshape{12}{1000} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} +\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\var=\smartitalic +\let\dfn=\smartitalic +\let\emph=\smartitalic +\let\cite=\smartitalic + +\def\b#1{{\bf #1}} +\let\strong=\b + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +\def\t#1{% + {\tt \rawbackslash \frenchspacing #1}% + \null +} +\let\ttfont=\t +\def\samp #1{`\tclose{#1}'\null} +\setfont\smallrm\rmshape{8}{1000} +\font\smallsy=cmsy9 +\def\key#1{{\smallrm\textfont2=\smallsy \leavevmode\hbox{% + \raise0.4pt\hbox{$\langle$}\kern-.08em\vtop{% + \vbox{\hrule\kern-0.4pt + \hbox{\raise0.4pt\hbox{\vphantom{$\langle$}}#1}}% + \kern-0.4pt\hrule}% + \kern-.06em\raise0.4pt\hbox{$\rangle$}}}} +% The old definition, with no lozenge: +%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +\let\file=\samp +\let\url=\samp % perhaps include a hypertex \special eventually +\def\email#1{$\langle${\tt #1}$\rangle$} + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \frenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in \code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ +\catcode`\-=\active +\catcode`\_=\active +\catcode`\|=\active +\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex} +% The following is used by \doprintindex to insure that long function names +% wrap around. It is necessary for - and _ to be active before the index is +% read from the file, as \entry parses the arguments long before \code is +% ever called. -- mycroft +% _ is always active; and it shouldn't be \let = to an _ that is a +% subscript character anyway. Then, @cindex @samp{_} (for example) +% fails. --karl +\global\def\indexbreaks{% + \catcode`\-=\active \let-\realdash +} +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{\normalunderscore\discretionary{}{}{}} +\def\codex #1{\tclose{#1}\endgroup} + +%\let\exp=\tclose %Was temporary + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. +% +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\ttsl\look}}\fi +\else{\tclose{\ttsl\look}}\fi} + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of +% @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +\def\r#1{{\rm #1}} % roman font +% Use of \lowercase was suggested. +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @pounds{} is a sterling sign. +\def\pounds{{\it\$}} + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\def\titlefont#1{{\titlerm #1}} + +\newif\ifseenauthor +\newif\iffinishedtitlepage + +\def\shorttitlepage{\parsearg\shorttitlepagezzz} +\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\def\titlepage{\begingroup \parindent=0pt \textfonts + \let\subtitlerm=\tenrm +% I deinstalled the following change because \cmr12 is undefined. +% This change was not in the ChangeLog anyway. --rms. +% \let\subtitlerm=\cmr12 + \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% + % + \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% + % + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % + % Now you can print the title using @title. + \def\title{\parsearg\titlezzz}% + \def\titlezzz##1{\leftline{\titlefont{##1}} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Now you can put text using @subtitle. + \def\subtitle{\parsearg\subtitlezzz}% + \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% + % + % @author should come last, but may come many times. + \def\author{\parsearg\authorzzz}% + \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi + {\authorfont \leftline{##1}}}% + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \oldpage + \let\page = \oldpage + \hbox{}}% +% \def\page{\oldpage \hbox{}} +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks \evenheadline % Token sequence for heading line of even pages +\newtoks \oddheadline % Token sequence for heading line of odd pages +\newtoks \evenfootline % Token sequence for footing line of even pages +\newtoks \oddfootline % Token sequence for footing line of odd pages + +% Now make Tex use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + +\def\evenheading{\parsearg\evenheadingxxx} +\def\oddheading{\parsearg\oddheadingxxx} +\def\everyheading{\parsearg\everyheadingxxx} + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\oddfooting{\parsearg\oddfootingxxx} +\def\everyfooting{\parsearg\everyfootingxxx} + +{\catcode`\@=0 % + +\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} +\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} +\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish} +\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} +\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} +\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish} +\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}} +\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} +% +}% unbind the catcode of @. + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{ +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% Produces Day Month Year style of output. +\def\today{\number\day\space +\ifcase\month\or +January\or February\or March\or April\or May\or June\or +July\or August\or September\or October\or November\or December\fi +\space\number\year} + +% Use this if you want the Month Day, Year style of output. +%\def\today{\ifcase\month\or +%January\or February\or March\or April\or May\or June\or +%July\or August\or September\or October\or November\or December\fi +%\space\number\day, \number\year} + +% @settitle line... specifies the title of the document, for headings +% It generates no output of its own + +\def\thistitle{No Title} +\def\settitle{\parsearg\settitlezzz} +\def\settitlezzz #1{\gdef\thistitle{#1}} + + +\message{tables,} + +% @tabs -- simple alignment + +% These don't work. For one thing, \+ is defined as outer. +% So these macros cannot even be defined. + +%\def\tabs{\parsearg\tabszzz} +%\def\tabszzz #1{\settabs\+#1\cr} +%\def\tabline{\parsearg\tablinezzz} +%\def\tablinezzz #1{\+#1\cr} +%\def\&{&} + +% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} +\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} + +\def\internalBkitem{\smallbreak \parsearg\kitemzzz} +\def\internalBkitemx{\itemxpar \parsearg\kitemzzz} + +\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% + \itemzzz {#1}} + +\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% + \itemzzz {#1}} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemfont{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % Be sure we are not still in the middle of a paragraph. + %{\parskip = 0in + %\par + %}% + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. Unfortunately + % we can't prevent a possible page break at the following + % \baselineskip glue. + \nobreak + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. Since that + % text will be indented by \tableindent, we make the item text be in + % a zero-width box. + \noindent + \rlap{\hskip -\tableindent\box0}\ignorespaces% + \endgroup% + \itemxneedsnegativevskiptrue% + \fi +} + +\def\item{\errmessage{@item while not in a table}} +\def\itemx{\errmessage{@itemx while not in a table}} +\def\kitem{\errmessage{@kitem while not in a table}} +\def\kitemx{\errmessage{@kitemx while not in a table}} +\def\xitem{\errmessage{@xitem while not in a table}} +\def\xitemx{\errmessage{@xitemx while not in a table}} + +%% Contains a kludge to get @end[description] to work +\def\description{\tablez{\dontindex}{1}{}{}{}{}} + +\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} +{\obeylines\obeyspaces% +\gdef\tablex #1^^M{% +\tabley\dontindex#1 \endtabley}} + +\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} +{\obeylines\obeyspaces% +\gdef\ftablex #1^^M{% +\tabley\fnitemindex#1 \endtabley +\def\Eftable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} +{\obeylines\obeyspaces% +\gdef\vtablex #1^^M{% +\tabley\vritemindex#1 \endtabley +\def\Evtable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\dontindex #1{} +\def\fnitemindex #1{\doind {fn}{\code{#1}}}% +\def\vritemindex #1{\doind {vr}{\code{#1}}}% + +{\obeyspaces % +\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% +\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} + +\def\tablez #1#2#3#4#5#6{% +\aboveenvbreak % +\begingroup % +\def\Edescription{\Etable}% Necessary kludge. +\let\itemindex=#1% +\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % +\ifnum 0#4>0 \tableindent=#4\mil \fi % +\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % +\def\itemfont{#2}% +\itemmax=\tableindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \tableindent % +\exdentamount=\tableindent +\parindent = 0pt +\parskip = \smallskipamount +\ifdim \parskip=0pt \parskip=2pt \fi% +\def\Etable{\endgraf\afterenvbreak\endgroup}% +\let\item = \internalBitem % +\let\itemx = \internalBitemx % +\let\kitem = \internalBkitem % +\let\kitemx = \internalBkitemx % +\let\xitem = \internalBxitem % +\let\xitemx = \internalBxitemx % +} + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\def\itemize{\parsearg\itemizezzz} + +\def\itemizezzz #1{% + \begingroup % ended by the @end itemsize + \itemizey {#1}{\Eitemize} +} + +\def\itemizey #1#2{% +\aboveenvbreak % +\itemmax=\itemindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \itemindent % +\exdentamount=\itemindent +\parindent = 0pt % +\parskip = \smallskipamount % +\ifdim \parskip=0pt \parskip=2pt \fi% +\def#2{\endgraf\afterenvbreak\endgroup}% +\def\itemcontents{#1}% +\let\item=\itemizeitem} + +% Set sfcode to normal for the chars that usually have another value. +% These are `.?!:;,' +\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 + \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\def\enumerate{\parsearg\enumeratezzz} +\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + \begingroup % ended by the @end enumerate + % + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call itemizey, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \itemizey{#1.}\Eenumerate\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + +% Definition of @item while inside @itemize. + +\def\itemizeitem{% +\advance\itemno by 1 +{\let\par=\endgraf \smallbreak}% +\ifhmode \errmessage{\in hmode at itemizeitem}\fi +{\parskip=0in \hskip 0pt +\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% +\vadjust{\penalty 1200}}% +\flushcr} + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. +% +% For those who want to use more than one line's worth of words in +% the preamble, break the line within one argument and it +% will parse correctly, i.e., +% +% @multitable {Column 1 template} {Column 2 template} {Column 3 +% template} +% Not: +% @multitable {Column 1 template} {Column 2 template} +% {Column 3 template} + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab, @multitable or @end multitable do not need to be on their +% own lines, but it will not hurt if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. + +%%%% +% Dimensions + +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +%%%% +% Macros used to set up halign preamble: +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +%% 2/1/96, to allow fractions to be given with more than one digit. +\def\pickupwholefraction#1 {\global\advance\colcount by1 % +\expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}% +\setuptable} + +\newcount\colcount +\def\setuptable#1{\def\firstarg{#1}% +\ifx\firstarg\xendsetuptable\let\go\relax% +\else + \ifx\firstarg\xcolumnfractions\global\setpercenttrue% + \else + \ifsetpercent + \let\go\pickupwholefraction % In this case arg of setuptable + % is the decimal point before the + % number given in percent of hsize. + % We don't need this so we don't use it. + \else + \global\advance\colcount by1 + \setbox0=\hbox{#1 }% Add a normal word space as a separator; + % typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi% + \fi% +\ifx\go\pickupwholefraction\else\let\go\setuptable\fi% +\fi\go} + +%%%% +% multitable syntax +\def\tab{&\hskip1sp\relax} % 2/2/96 + % tiny skip here makes sure this column space is + % maintained, even if it is never used. + + +%%%% +% @multitable ... @end multitable definitions: + +\def\multitable{\parsearg\dotable} + +\def\dotable#1{\bgroup +\let\item\cr +\tolerance=9500 +\hbadness=9500 +\setmultitablespacing +\parskip=\multitableparskip +\parindent=\multitableparindent +\overfullrule=0pt +\global\colcount=0\relax% +\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}% + % To parse everything between @multitable and @item : +\setuptable#1 \endsetuptable + % Need to reset this to 0 after \setuptable. +\global\colcount=0\relax% + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. +\halign\bgroup&\global\advance\colcount by 1\relax% +\multistrut\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % If user has set preamble in terms of percent of \hsize + % we will use that dimension as the width of the column, and + % the \leftskip will keep entries from bumping into each other. + % Table will start at left margin and final column will justify at + % right margin. +\ifnum\colcount=1 +\else + \ifsetpercent + \else + % If user has set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: +\leftskip=\multitablecolspace +\fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighing sequences respectively marking + % characters. + \noindent\ignorespaces##\unskip\multistrut}\cr + % \everycr will reset column counter, \colcount, at the end of + % each line. Every column entry will cause \colcount to advance by one. + % The table preamble + % looks at the current \colcount to find the correct column width. +\global\everycr{\noalign{% +% \filbreak%% keeps underfull box messages off when table breaks over pages. +% Maybe so, but it also creates really weird page breaks when the table +% breaks over pages Wouldn't \vfil be better? Wait until the problem +% manifests itself, so it can be fixed for real --karl. +\global\colcount=0\relax}} +} + +\def\setmultitablespacing{% test to see if user has set \multitablelinespace. +% If so, do nothing. If not, give it an appropriate dimension based on +% current baselineskip. +\ifdim\multitablelinespace=0pt +%% strut to put in table in case some entry doesn't have descenders, +%% to keep lines equally spaced +\let\multistrut = \strut +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\else +\gdef\multistrut{\vrule height\multitablelinespace depth\dp0 +width0pt\relax} \fi +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within \newindex. +{\catcode`\@=11 +\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. + +\def\newindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#1}} +} + +% @defindex foo == \newindex{foo} + +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. + +\def\newcodeindex #1{ +\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file +\openout \csname#1indfile\endcsname \jobname.#1 % Open the file +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#1}} +} + +\def\defcodeindex{\parsearg\newcodeindex} + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +\def\synindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\doindex {#2}}% +} + +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +\def\syncodeindex #1 #2 {% +\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname +\expandafter\let\csname#1indfile\endcsname=\synindexfoo +\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex +\noexpand\docodeindex {#2}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +\def\indexdummies{% +% Take care of the plain tex accent commands. +\def\"{\realbackslash "}% +\def\`{\realbackslash `}% +\def\'{\realbackslash '}% +\def\^{\realbackslash ^}% +\def\~{\realbackslash ~}% +\def\={\realbackslash =}% +\def\b{\realbackslash b}% +\def\c{\realbackslash c}% +\def\d{\realbackslash d}% +\def\u{\realbackslash u}% +\def\v{\realbackslash v}% +\def\H{\realbackslash H}% +% Take care of the plain tex special European modified letters. +\def\oe{\realbackslash oe}% +\def\ae{\realbackslash ae}% +\def\aa{\realbackslash aa}% +\def\OE{\realbackslash OE}% +\def\AE{\realbackslash AE}% +\def\AA{\realbackslash AA}% +\def\o{\realbackslash o}% +\def\O{\realbackslash O}% +\def\l{\realbackslash l}% +\def\L{\realbackslash L}% +\def\ss{\realbackslash ss}% +% Take care of texinfo commands likely to appear in an index entry. +% (Must be a way to avoid doing expansion at all, and thus not have to +% laboriously list every single command here.) +\def\@{@}% will be @@ when we switch to @ as escape char. +%\let\{ = \lbracecmd +%\let\} = \rbracecmd +\def\_{{\realbackslash _}}% +\def\w{\realbackslash w }% +\def\bf{\realbackslash bf }% +%\def\rm{\realbackslash rm }% +\def\sl{\realbackslash sl }% +\def\sf{\realbackslash sf}% +\def\tt{\realbackslash tt}% +\def\gtr{\realbackslash gtr}% +\def\less{\realbackslash less}% +\def\hat{\realbackslash hat}% +%\def\char{\realbackslash char}% +\def\TeX{\realbackslash TeX}% +\def\dots{\realbackslash dots }% +\def\copyright{\realbackslash copyright }% +\def\tclose##1{\realbackslash tclose {##1}}% +\def\code##1{\realbackslash code {##1}}% +\def\dotless##1{\realbackslash dotless {##1}}% +\def\samp##1{\realbackslash samp {##1}}% +\def\,##1{\realbackslash ,{##1}}% +\def\t##1{\realbackslash t {##1}}% +\def\r##1{\realbackslash r {##1}}% +\def\i##1{\realbackslash i {##1}}% +\def\b##1{\realbackslash b {##1}}% +\def\cite##1{\realbackslash cite {##1}}% +\def\key##1{\realbackslash key {##1}}% +\def\file##1{\realbackslash file {##1}}% +\def\var##1{\realbackslash var {##1}}% +\def\kbd##1{\realbackslash kbd {##1}}% +\def\dfn##1{\realbackslash dfn {##1}}% +\def\emph##1{\realbackslash emph {##1}}% +\unsepspaces +} + +% If an index command is used in an @example environment, any spaces +% therein should become regular spaces in the raw index file, not the +% expansion of \tie (\\leavevmode \penalty \@M \ ). +{\obeyspaces + \gdef\unsepspaces{\obeyspaces\let =\space}} + +% \indexnofonts no-ops all font-change commands. +% This is used when outputting the strings to sort the index by. +\def\indexdummyfont#1{#1} +\def\indexdummytex{TeX} +\def\indexdummydots{...} + +\def\indexnofonts{% +% Just ignore accents. +\let\,=\indexdummyfont +\let\"=\indexdummyfont +\let\`=\indexdummyfont +\let\'=\indexdummyfont +\let\^=\indexdummyfont +\let\~=\indexdummyfont +\let\==\indexdummyfont +\let\b=\indexdummyfont +\let\c=\indexdummyfont +\let\d=\indexdummyfont +\let\u=\indexdummyfont +\let\v=\indexdummyfont +\let\H=\indexdummyfont +\let\dotless=\indexdummyfont +% Take care of the plain tex special European modified letters. +\def\oe{oe}% +\def\ae{ae}% +\def\aa{aa}% +\def\OE{OE}% +\def\AE{AE}% +\def\AA{AA}% +\def\o{o}% +\def\O{O}% +\def\l{l}% +\def\L{L}% +\def\ss{ss}% +\let\w=\indexdummyfont +\let\t=\indexdummyfont +\let\r=\indexdummyfont +\let\i=\indexdummyfont +\let\b=\indexdummyfont +\let\emph=\indexdummyfont +\let\strong=\indexdummyfont +\let\cite=\indexdummyfont +\let\sc=\indexdummyfont +%Don't no-op \tt, since it isn't a user-level command +% and is used in the definitions of the active chars like <, >, |... +%\let\tt=\indexdummyfont +\let\tclose=\indexdummyfont +\let\code=\indexdummyfont +\let\file=\indexdummyfont +\let\samp=\indexdummyfont +\let\kbd=\indexdummyfont +\let\key=\indexdummyfont +\let\var=\indexdummyfont +\let\TeX=\indexdummytex +\let\dots=\indexdummydots +\def\@{@}% +} + +% To define \realbackslash, we must make \ not be an escape. +% We must first make another character (@) an escape +% so we do not become unable to do a definition. + +{\catcode`\@=0 \catcode`\\=\other +@gdef@realbackslash{\}} + +\let\indexbackslash=0 %overridden during \printindex. + +\let\SETmarginindex=\relax %initialize! +% workhorse for all \fooindexes +% #1 is name of index, #2 is stuff to put there +\def\doind #1#2{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}% + \fi + {% + \count255=\lastpenalty + {% + \indexdummies % Must do this here, since \bf, etc expand at this stage + \escapechar=`\\ + {% + \let\folio=0% We will expand all macros now EXCEPT \folio. + \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % First process the index-string with all font commands turned off + % to get the string to sort by. + {\indexnofonts \xdef\indexsorttmp{#2}}% + % + % Now produce the complete index entry, with both the sort key and the + % original text, including any font commands. + \toks0 = {#2}% + \edef\temp{% + \write\csname#1indfile\endcsname{% + \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}% + }% + \temp + }% + }% + \penalty\count255 + }% +} + +\def\dosubind #1#2#3{% +{\count10=\lastpenalty % +{\indexdummies % Must do this here, since \bf, etc expand at this stage +\escapechar=`\\% +{\let\folio=0% +\def\rawbackslashxx{\indexbackslash}% +% +% Now process the index-string once, with all font commands turned off, +% to get the string to sort the index by. +{\indexnofonts +\xdef\temp1{#2 #3}% +}% +% Now produce the complete index entry. We process the index-string again, +% this time with font commands expanded, to get what to print in the index. +\edef\temp{% +\write \csname#1indfile\endcsname{% +\realbackslash entry {\temp1}{\folio}{#2}{#3}}}% +\temp }% +}\penalty\count10}} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% This is what you call to cause a particular index to get printed. +% Write +% @unnumbered Function Index +% @printindex fn + +\def\printindex{\parsearg\doprintindex} + +\def\doprintindex#1{\begingroup + \dobreak \chapheadingskip{10000}% + % + \indexfonts \rm + \tolerance = 9500 + \indexbreaks + \def\indexbackslash{\rawbackslashxx}% + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \catcode`\\ = 0 + \catcode`\@ = 11 + \escapechar = `\\ + \begindoublecolumns + % + % See if the index file exists and is nonempty. + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + (Index is nonexistent) + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + (Index is empty) + \else + \input \jobname.#1s + \fi + \fi + \closein 1 + \enddoublecolumns +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +% Same as \bigskipamount except no shrink. +% \balancecolumns gets confused if there is any shrink. +\newskip\initialskipamount \initialskipamount 12pt plus4pt + +\def\initial #1{% +{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt +\ifdim\lastskip<\initialskipamount +\removelastskip \penalty-200 \vskip \initialskipamount\fi +\line{\secbf#1\hfill}\kern 2pt\penalty10000}} + +% This typesets a paragraph consisting of #1, dot leaders, and then #2 +% flush to the right margin. It is used for index and table of contents +% entries. The paragraph is indented by \leftskip. +% +\def\entry #1#2{\begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent=2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % Start a ``paragraph'' for the index entry so the line breaking + % parameters we've set above will have an effect. + \noindent + % + % Insert the text of the index entry. TeX will do line-breaking on it. + #1% + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#2}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd\ \else% + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ #2% The page number ends the paragraph. + \fi% + \par +\endgroup} + +% Like \dotfill except takes at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm + +\def\secondary #1#2{ +{\parfillskip=0in \parskip=0in +\hangindent =1in \hangafter=1 +\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {\global\setbox\partialpage + =\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}% + \eject + % + % Now switch to the double-column output routine. + \output={\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it once. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +- < + % 1pt) as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@=\pageheight \advance\dimen@ by-\ht\partialpage + % box0 will be the left-hand column, box1 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 \penalty\outputpenalty +} +\def\pagesofar{% + % The contents of the output page -- any previous material, + % followed by the two boxes we just split. + \unvbox\partialpage + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}% +} +\def\enddoublecolumns{% + \output={\balancecolumns}\eject % split what we have + \endgroup + % Back to normal single-column typesetting, but take account of the + % fact that we just accumulated some stuff on the output page. + \pagegoal=\vsize +} +\def\balancecolumns{% + % Called on the last page of the double column material. + \setbox0=\vbox{\unvbox255}% + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {\vbadness=10000 \loop \global\setbox3=\copy0 + \global\setbox1=\vsplit3 to\dimen@ + \ifdim\ht3>\dimen@ \global\advance\dimen@ by1pt \repeat}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + \pagesofar +} +\catcode `\@=\other + + +\message{sectioning,} +% Define chapters, sections, etc. + +\newcount \chapno +\newcount \secno \secno=0 +\newcount \subsecno \subsecno=0 +\newcount \subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount \appendixno \appendixno = `\@ +\def\appendixletter{\char\the\appendixno} + +\newwrite \contentsfile +% This is called from \setfilename. +\def\opencontents{\openout \contentsfile = \jobname.toc} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise + +\def\thischapter{} \def\thissection{} +\def\seccheck#1{\if \pageno<0 % +\errmessage{@#1 not allowed after generating table of contents}\fi +% +} + +\def\chapternofonts{% +\let\rawbackslash=\relax% +\let\frenchspacing=\relax% +\def\result{\realbackslash result} +\def\equiv{\realbackslash equiv} +\def\expansion{\realbackslash expansion} +\def\print{\realbackslash print} +\def\TeX{\realbackslash TeX} +\def\dots{\realbackslash dots} +\def\copyright{\realbackslash copyright} +\def\tt{\realbackslash tt} +\def\bf{\realbackslash bf } +\def\w{\realbackslash w} +\def\less{\realbackslash less} +\def\gtr{\realbackslash gtr} +\def\hat{\realbackslash hat} +\def\char{\realbackslash char} +\def\tclose##1{\realbackslash tclose {##1}} +\def\code##1{\realbackslash code {##1}} +\def\samp##1{\realbackslash samp {##1}} +\def\r##1{\realbackslash r {##1}} +\def\b##1{\realbackslash b {##1}} +\def\key##1{\realbackslash key {##1}} +\def\file##1{\realbackslash file {##1}} +\def\kbd##1{\realbackslash kbd {##1}} +% These are redefined because @smartitalic wouldn't work inside xdef. +\def\i##1{\realbackslash i {##1}} +\def\cite##1{\realbackslash cite {##1}} +\def\var##1{\realbackslash var {##1}} +\def\emph##1{\realbackslash emph {##1}} +\def\dfn##1{\realbackslash dfn {##1}} +} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raise/lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% Choose a numbered-heading macro +% #1 is heading level if unmodified by @raisesections or @lowersections +% #2 is text for heading +\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \chapterzzz{#2} +\or + \seczzz{#2} +\or + \numberedsubseczzz{#2} +\or + \numberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \chapterzzz{#2} + \else + \numberedsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses appendix heading levels +\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \appendixzzz{#2} +\or + \appendixsectionzzz{#2} +\or + \appendixsubseczzz{#2} +\or + \appendixsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \appendixzzz{#2} + \else + \appendixsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses numberless heading levels +\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \unnumberedzzz{#2} +\or + \unnumberedseczzz{#2} +\or + \unnumberedsubseczzz{#2} +\or + \unnumberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \unnumberedzzz{#2} + \else + \unnumberedsubsubseczzz{#2} + \fi +\fi +} + + +\def\thischaptername{No Chapter Title} +\outer\def\chapter{\parsearg\chapteryyy} +\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz #1{\seccheck{chapter}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \chapno by 1 \message{\putwordChapter \the\chapno}% +\chapmacro {#1}{\the\chapno}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +% We don't substitute the actual chapter name into \thischapter +% because we don't want its macros evaluated now. +\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec +}} + +\outer\def\appendix{\parsearg\appendixyyy} +\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz #1{\seccheck{appendix}% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \appendixno by 1 \message{Appendix \appendixletter}% +\chapmacro {#1}{\putwordAppendix{} \appendixletter}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% +{\chapternofonts% +\edef\temp{{\realbackslash chapentry + {#1}{\putwordAppendix{} \appendixletter}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\global\let\section = \appendixsec +\global\let\subsection = \appendixsubsec +\global\let\subsubsection = \appendixsubsubsec +}} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\def\centerchap{\parsearg\centerchapyyy} +\def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}} + +\outer\def\top{\parsearg\unnumberedyyy} +\outer\def\unnumbered{\parsearg\unnumberedyyy} +\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz #1{\seccheck{unnumbered}% +\secno=0 \subsecno=0 \subsubsecno=0 +% +% This used to be simply \message{#1}, but TeX fully expands the +% argument to \message. Therefore, if #1 contained @-commands, TeX +% expanded them. For example, in `@unnumbered The @cite{Book}', TeX +% expanded @cite (which turns out to cause errors because \cite is meant +% to be executed, not expanded). +% +% Anyway, we don't want the fully-expanded definition of @cite to appear +% as a result of the \message, we just want `@cite' itself. We use +% \the to achieve this: TeX expands \the only once, +% simply yielding the contents of the . +\toks0 = {#1}\message{(\the\toks0)}% +% +\unnumbchapmacro {#1}% +\gdef\thischapter{#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\global\let\section = \unnumberedsec +\global\let\subsection = \unnumberedsubsec +\global\let\subsubsection = \unnumberedsubsubsec +}} + +\outer\def\numberedsec{\parsearg\secyyy} +\def\secyyy #1{\numhead1{#1}} % normally calls seczzz +\def\seczzz #1{\seccheck{section}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsection{\parsearg\appendixsecyyy} +\outer\def\appendixsec{\parsearg\appendixsecyyy} +\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz #1{\seccheck{appendixsection}% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% +{\chapternofonts% +\edef\temp{{\realbackslash secentry % +{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} +\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz #1{\seccheck{unnumberedsec}% +\plainsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} +\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz #1{\seccheck{subsection}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} +\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz #1{\seccheck{appendixsubsec}% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsecentry % +{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} +\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}% +\plainsubsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} +\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz #1{\seccheck{subsubsection}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry % + {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno} + {\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\donoderef % +\penalty 10000 % +}} + +\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} +\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +{\chapternofonts% +\edef\temp{{\realbackslash subsubsecentry{#1}% + {\appendixletter} + {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\appendixnoderef % +\penalty 10000 % +}} + +\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} +\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}% +\plainsubsubsecheading {#1}\gdef\thissection{#1}% +{\chapternofonts% +\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}% +\escapechar=`\\% +\write \contentsfile \temp % +\unnumbnoderef % +\penalty 10000 % +}} + +% These are variants which are not "outer", so they can appear in @ifinfo. +% Actually, they should now be obsolete; ordinary section commands should work. +\def\infotop{\parsearg\unnumberedzzz} +\def\infounnumbered{\parsearg\unnumberedzzz} +\def\infounnumberedsec{\parsearg\unnumberedseczzz} +\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} +\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} + +\def\infoappendix{\parsearg\appendixzzz} +\def\infoappendixsec{\parsearg\appendixseczzz} +\def\infoappendixsubsec{\parsearg\appendixsubseczzz} +\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} + +\def\infochapter{\parsearg\chapterzzz} +\def\infosection{\parsearg\sectionzzz} +\def\infosubsection{\parsearg\subsectionzzz} +\def\infosubsubsection{\parsearg\subsubsectionzzz} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and +% such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{\parsearg\majorheadingzzz} +\def\majorheadingzzz #1{% +{\advance\chapheadingskip by 10pt \chapbreak }% +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\chapheading{\parsearg\chapheadingzzz} +\def\chapheadingzzz #1{\chapbreak % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +% @heading, @subheading, @subsubheading. +\def\heading{\parsearg\plainsecheading} +\def\subheading{\parsearg\plainsubsecheading} +\def\subsubheading{\parsearg\plainsubsubsecheading} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{ +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{ +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{ +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +\def\CHAPFplain{ +\global\let\chapmacro=\chfplain +\global\let\unnumbchapmacro=\unnchfplain +\global\let\centerchapmacro=\centerchfplain} + +% Plain chapter opening. +% #1 is the text, #2 the chapter number or empty if unnumbered. +\def\chfplain#1#2{% + \pchapsepmacro + {% + \chapfonts \rm + \def\chapnum{#2}% + \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}% + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% Plain opening for unnumbered. +\def\unnchfplain#1{\chfplain{#1}{}} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerchfplain#1{{% + \def\centerparametersmaybe{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt + }% + \chfplain{#1}{}% +}} + +\CHAPFplain % The default + +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 10000 % +} + +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} + +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rm #1}\hfill}}\bigskip \par\penalty 10000 % +} + +\def\CHAPFopen{ +\global\let\chapmacro=\chfopen +\global\let\unnumbchapmacro=\unnchfopen +\global\let\centerchapmacro=\centerchfopen} + + +% Section titles. +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip {-1000}} +\def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}} +\def\plainsecheading#1{\sectionheading{sec}{}{#1}} + +% Subsection titles. +\newskip \subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} +\def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}} +\def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}} + +% Subsubsection titles. +\let\subsubsecheadingskip = \subsecheadingskip +\let\subsubsecheadingbreak = \subsecheadingbreak +\def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}} +\def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}} + + +% Print any size section title. +% +% #1 is the section type (sec/subsec/subsubsec), #2 is the section +% number (maybe empty), #3 the text. +\def\sectionheading#1#2#3{% + {% + \expandafter\advance\csname #1headingskip\endcsname by \parskip + \csname #1headingbreak\endcsname + }% + {% + % Switch to the right set of fonts. + \csname #1fonts\endcsname \rm + % + % Only insert the separating space if we have a section number. + \def\secnum{#2}% + \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}% + % + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 % zero if no section number + \unhbox0 #3}% + }% + \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak +} + + +\message{toc printing,} +% Finish up the main text and prepare to read what we've written +% to \contentsfile. + +\newskip\contentsrightmargin \contentsrightmargin=1in +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout \contentsfile + \ifnum \pageno>0 + \pageno = -1 % Request roman numbered pages. + \fi + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \unnumbchapmacro{#1}\def\thischapter{}% + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + \catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. +} + + +% Normal (long) toc. +\outer\def\contents{% + \startcontents{\putwordTableofContents}% + \input \jobname.toc + \endgroup + \vfill \eject +} + +% And just the chapters. +\outer\def\summarycontents{% + \startcontents{\putwordShortContents}% + % + \let\chapentry = \shortchapentry + \let\unnumbchapentry = \shortunnumberedentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\secentry ##1##2##3##4{} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{} + \def\unnumbsubsubsecentry ##1##2{} + \input \jobname.toc + \endgroup + \vfill \eject +} +\let\shortcontents = \summarycontents + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapter-level things, for both the long and short contents. +\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} + +% See comments in \dochapentry re vbox and related settings +\def\shortchapentry#1#2#3{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}% +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. +% We could simplify the code here by writing out an \appendixentry +% command in the toc file for appendices, instead of using \chapentry +% for both, but it doesn't seem worth it. +\setbox0 = \hbox{\shortcontrm \putwordAppendix } +\newdimen\shortappendixwidth \shortappendixwidth = \wd0 + +\def\shortchaplabel#1{% + % We typeset #1 in a box of constant width, regardless of the text of + % #1, so the chapter titles will come out aligned. + \setbox0 = \hbox{#1}% + \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi + % + % This space should be plenty, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + \advance\dimen0 by 1.1em + \hbox to \dimen0{#1\hfil}% +} + +\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} +\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}} + +% Sections. +\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} +\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} + +% Subsections. +\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} +\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} + +% And subsubsections. +\def\subsubsecentry#1#2#3#4#5#6{% + \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} +\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} + +% This parameter controls the indentation of the various levels. +\newdimen\tocindent \tocindent = 3pc + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno{#2}}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +% Final typesetting of a toc entry; we use the same \entry macro as for +% the index entries, but we want to suppress hyphenation here. (We +% can't do that in the \entry macro, since index entries might consist +% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) +% +% \turnoffactive is for the sake of @" used for umlauts. +\def\tocentry#1#2{\begingroup + \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks + \entry{\turnoffactive #1}{\turnoffactive #2}% +\endgroup} + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\let\subsecentryfonts = \textfonts +\let\subsubsecentryfonts = \textfonts + + +\message{environments,} + +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% Furthermore, these definitions must come after we define our fonts. +\newbox\dblarrowbox \newbox\longdblarrowbox +\newbox\pushcharbox \newbox\bullbox +\newbox\equivbox \newbox\errorbox + +%{\tentt +%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} +%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} +%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} +%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} +% Adapted from the manmac format (p.420 of TeXbook) +%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex +% depth .1ex\hfil} +%} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +\def\point{$\star$} +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% Adapted from the TeXbook's \boxit. +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} + +\global\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{ + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} + +% The @error{} command. +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\def\tex{\begingroup +\catcode `\\=0 \catcode `\{=1 \catcode `\}=2 +\catcode `\$=3 \catcode `\&=4 \catcode `\#=6 +\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie +\catcode `\%=14 +\catcode 43=12 % plus +\catcode`\"=12 +\catcode`\==12 +\catcode`\|=12 +\catcode`\<=12 +\catcode`\>=12 +\escapechar=`\\ +% +\let\,=\ptexcomma +\let\~=\ptextilde +\let\{=\ptexlbrace +\let\}=\ptexrbrace +\let\.=\ptexdot +\let\*=\ptexstar +\let\dots=\ptexdots +\def\endldots{\mathinner{\ldots\ldots\ldots\ldots}} +\def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi} +\def\@{@}% +\let\bullet=\ptexbullet +\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext +% +\let\Etex=\endgroup} + +% Define @lisp ... @endlisp. +% @lisp does a \begingroup so it can rebind things, +% including the definition of @endlisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% Make each space character in the input produce a normal interword +% space in the output. Don't allow a line break at this space, as this +% is used only in environments like @example, where each line of input +% should produce a line of output anyway. +% +{\obeyspaces % +\gdef\sepspaces{\obeyspaces\let =\tie}} + +% Define \obeyedspace to be our active space, whatever it is. This is +% for use in \parsearg. +{\sepspaces% +\global\let\obeyedspace= } + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip +% +\def\aboveenvbreak{{\advance\envskipamount by \parskip +\endgraf \ifdim\lastskip<\envskipamount +\removelastskip \penalty-50 \vskip\envskipamount \fi}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. +\let\nonarrowing=\relax + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \cartouche: draw rectangle w/rounded corners around argument +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\long\def\cartouche{% +\begingroup + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt %we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18pt % allow for 3pt kerns on either +% side, and for 6pt waste from +% each corner char + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \hsize=\cartinner + \kern3pt + \begingroup + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip +\def\Ecartouche{% + \endgroup + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup +\endgroup +}} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \inENV % This group ends at the end of the body + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \singlespace + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + % @cartouche defines \nonarrowing to inhibit narrowing + % at next level down. + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \let\exdent=\nofillexdent + \let\nonarrowing=\relax + \fi +} + +% To ending an @example-like environment, we first end the paragraph +% (via \afterenvbreak's vertical glue), and then the group. That way we +% keep the zero \parskip that the environments set -- \parskip glue +% will be inserted at the beginning of the next paragraph in the +% document, after the environment. +% +\def\nonfillfinish{\afterenvbreak\endgroup}% + +% This macro is +\def\lisp{\begingroup + \nonfillstart + \let\Elisp = \nonfillfinish + \tt + \rawbackslash % have \ input char produce \ char from current font + \gobble +} + +% Define the \E... control sequence only if we are inside the +% environment, so the error checking in \end will work. +% +% We must call \lisp last in the definition, since it reads the +% return following the @example (or whatever) command. +% +\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} +\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp} +\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp} + +% @smallexample and @smalllisp. This is not used unless the @smallbook +% command is given. Originally contributed by Pavel@xerox. +% +\def\smalllispx{\begingroup + \nonfillstart + \let\Esmalllisp = \nonfillfinish + \let\Esmallexample = \nonfillfinish + % + % Smaller fonts for small examples. + \indexfonts \tt + \rawbackslash % make \ output the \ character from the current font (tt) + \gobble +} + +% This is @display; same as @lisp except use roman font. +% +\def\display{\begingroup + \nonfillstart + \let\Edisplay = \nonfillfinish + \gobble +} + +% This is @format; same as @display except don't narrow margins. +% +\def\format{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eformat = \nonfillfinish + \gobble +} + +% @flushleft (same as @format) and @flushright. +% +\def\flushleft{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushleft = \nonfillfinish + \gobble +} +\def\flushright{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushright = \nonfillfinish + \advance\leftskip by 0pt plus 1fill + \gobble} + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. +% +\def\quotation{% + \begingroup\inENV %This group ends at the end of the @quotation body + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \singlespace + \parindent=0pt + % We have retained a nonzero parskip for the environment, since we're + % doing normal filling. So to avoid extra space below the environment... + \def\Equotation{\parskip = 0pt \nonfillfinish}% + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \let\nonarrowing = \relax + \fi +} + +\message{defuns,} +% Define formatter for defuns +% First, allow user to change definition object font (\df) internally +\def\setdeffont #1 {\csname DEF#1\endcsname} + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deftypemargin \deftypemargin=12pt +\newskip\deflastargmargin \deflastargmargin=18pt + +\newcount\parencount +% define \functionparens, which makes ( and ) and & do special things. +% \functionparens affects the group it is contained in. +\def\activeparens{% +\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active +\catcode`\[=\active \catcode`\]=\active} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +\global\let(=\lparen \global\let)=\rparen +\global\let[=\lbrack \global\let]=\rbrack + +\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } +\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} +% This is used to turn on special parens +% but make & act ordinary (given that it's active). +\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr} + +% Definitions of (, ) and & used in args for functions. +% This is the definition of ( outside of all parentheses. +\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested % +\global\advance\parencount by 1 } +% +% This is the definition of ( when already inside a level of parens. +\gdef\opnested{\char`\(\global\advance\parencount by 1 } +% +\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. +% also in that case restore the outer-level definition of (. +\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi +\global\advance \parencount by -1 } +% If we encounter &foo, then turn on ()-hacking afterwards +\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } +% +\gdef\normalparens{\boldbrax\let&=\ampnr} +} % End of definition inside \activeparens +%% These parens (in \boldbrax) actually are a little bolder than the +%% contained text. This is especially needed for [ and ] +\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&} +\def\lbrb{{\bf\char`\[}} \def\rbrb{{\bf\char`\]}} + +% First, defname, which formats the header line itself. +% #1 should be the function name. +% #2 should be the type of definition, such as "Function". + +\def\defname #1#2{% +% Get the values of \leftskip and \rightskip as they were +% outside the @def... +\dimen2=\leftskip +\advance\dimen2 by -\defbodyindent +\dimen3=\rightskip +\advance\dimen3 by -\defbodyindent +\noindent % +\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% +\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line +\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations +\parshape 2 0in \dimen0 \defargsindent \dimen1 % +% Now output arg 2 ("Function" or some such) +% ending at \deftypemargin from the right margin, +% but stuck inside a box of width 0 so it does not interfere with linebreaking +{% Adjust \hsize to exclude the ambient margins, +% so that \rightline will obey them. +\advance \hsize by -\dimen2 \advance \hsize by -\dimen3 +\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}% +% Make all lines underfull and no complaints: +\tolerance=10000 \hbadness=10000 +\advance\leftskip by -\defbodyindent +\exdentamount=\defbodyindent +{\df #1}\enskip % Generate function name +} + +% Actually process the body of a definition +% #1 should be the terminating control sequence, such as \Edefun. +% #2 should be the "another name" control sequence, such as \defunx. +% #3 should be the control sequence that actually processes the header, +% such as \defunheader. + +\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % 61 is `=' +\obeylines\activeparens\spacesplit#3} + +\def\defmethparsebody #1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} + +\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} + +% These parsing functions are similar to the preceding ones +% except that they do not make parens into active characters. +% These are used for "variables" since they have no arguments. + +\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\spacesplit#3} + +% This is used for \def{tp,vr}parsebody. It could probably be used for +% some of the others, too, with some judicious conditionals. +% +\def\parsebodycommon#1#2#3{% + \begingroup\inENV % + \medbreak % + % Define the end token that this defining construct specifies + % so that it will exit this group. + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% + \parindent=0in + \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines +} + +\def\defvrparsebody#1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{#3{#4}}% +} + +% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the +% type is just `struct', because we lose the braces in `{struct +% termios}' when \spacesplit reads its undelimited argument. Sigh. +% \let\deftpparsebody=\defvrparsebody +% +% So, to get around this, we put \empty in with the type name. That +% way, TeX won't find exactly `{...}' as an undelimited argument, and +% won't strip off the braces. +% +\def\deftpparsebody #1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{\parsetpheaderline{#3{#4}}}\empty +} + +% Fine, but then we have to eventually remove the \empty *and* the +% braces (if any). That's what this does. +% +\def\removeemptybraces\empty#1\relax{#1} + +% After \spacesplit has done its work, this is called -- #1 is the final +% thing to call, #2 the type name (which starts with \empty), and #3 +% (which might be empty) the arguments. +% +\def\parsetpheaderline#1#2#3{% + #1{\removeemptybraces#2\relax}{#3}% +}% + +\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#5}}} + +% Split up #2 at the first space token. +% call #1 with two arguments: +% the first is all of #2 before the space token, +% the second is all of #2 after that space token. +% If #2 contains no space token, all of it is passed as the first arg +% and the second is passed as empty. + +{\obeylines +\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% +\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% +\ifx\relax #3% +#1{#2}{}\else #1{#2}{#3#4}\fi}} + +% So much for the things common to all kinds of definitions. + +% Define @defun. + +% First, define the processing that is wanted for arguments of \defun +% Use this to expand the args and terminate the paragraph they make up + +\def\defunargs #1{\functionparens \sl +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\hyphenchar\tensl=0 +#1% +\hyphenchar\tensl=45 +\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +\def\deftypefunargs #1{% +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +% Use \boldbraxnoamp, not \functionparens, so that & is not special. +\boldbraxnoamp +\tclose{#1}% avoid \code because of side effects on active chars +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\penalty 10000\vskip -\parskip\penalty 10000% +} + +% Do complete processing of one @defun or @defunx line already parsed. + +% @deffn Command forward-char nchars + +\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} + +\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% +\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defun == @deffn Function + +\def\defun{\defparsebody\Edefun\defunx\defunheader} + +\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Function}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefun int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} + +% #1 is the data type. #2 is the name and args. +\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} +% #1 is the data type, #2 the name, #3 the args. +\def\deftypefunheaderx #1#2 #3\relax{% +\doind {fn}{\code{#2}}% Make entry in function index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}% +\deftypefunargs {#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} + +% \defheaderxcond#1\relax$$$ +% puts #1 in @code, followed by a space, but does nothing if #1 is null. +\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} + +% #1 is the classification. #2 is the data type. #3 is the name and args. +\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} +% #1 is the classification, #2 the data type, #3 the name, #4 the args. +\def\deftypefnheaderx #1#2#3 #4\relax{% +\doind {fn}{\code{#3}}% Make entry in function index +\begingroup +\normalparens % notably, turn off `&' magic, which prevents +% at least some C++ text from working +\defname {\defheaderxcond#2\relax$$$#3}{#1}% +\deftypefunargs {#4}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defmac == @deffn Macro + +\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} + +\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Macro}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defspec == @deffn Special Form + +\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} + +\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Special Form}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% This definition is run if you use @defunx +% anywhere other than immediately after a @defun or @defunx. + +\def\deffnx #1 {\errmessage{@deffnx in invalid context}} +\def\defunx #1 {\errmessage{@defunx in invalid context}} +\def\defmacx #1 {\errmessage{@defmacx in invalid context}} +\def\defspecx #1 {\errmessage{@defspecx in invalid context}} +\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}} +\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}} + +% @defmethod, and so on + +% @defop {Funny Method} foo-class frobnicate argument + +\def\defop #1 {\def\defoptype{#1}% +\defopparsebody\Edefop\defopx\defopheader\defoptype} + +\def\defopheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index +\begingroup\defname {#2}{\defoptype{} on #1}% +\defunargs {#3}\endgroup % +} + +% @defmethod == @defop Method + +\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} + +\def\defmethodheader #1#2#3{% +\dosubind {fn}{\code{#2}}{on #1}% entry in function index +\begingroup\defname {#2}{Method on #1}% +\defunargs {#3}\endgroup % +} + +% @defcv {Class Option} foo-class foo-flag + +\def\defcv #1 {\def\defcvtype{#1}% +\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} + +\def\defcvarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{\defcvtype{} of #1}% +\defvarargs {#3}\endgroup % +} + +% @defivar == @defcv {Instance Variable} + +\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} + +\def\defivarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{Instance Variable of #1}% +\defvarargs {#3}\endgroup % +} + +% These definitions are run if you use @defmethodx, etc., +% anywhere other than immediately after a @defmethod, etc. + +\def\defopx #1 {\errmessage{@defopx in invalid context}} +\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}} +\def\defcvx #1 {\errmessage{@defcvx in invalid context}} +\def\defivarx #1 {\errmessage{@defivarx in invalid context}} + +% Now @defvar + +% First, define the processing that is wanted for arguments of @defvar. +% This is actually simple: just print them in roman. +% This must expand the args and terminate the paragraph they make up +\def\defvarargs #1{\normalparens #1% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000} + +% @defvr Counter foo-count + +\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} + +\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% +\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} + +% @defvar == @defvr Variable + +\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} + +\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{Variable}% +\defvarargs {#2}\endgroup % +} + +% @defopt == @defvr {User Option} + +\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} + +\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{User Option}% +\defvarargs {#2}\endgroup % +} + +% @deftypevar int foobar + +\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} + +% #1 is the data type. #2 is the name, perhaps followed by text that +% is actually part of the data type, which should not be put into the index. +\def\deftypevarheader #1#2{% +\dovarind#2 \relax% Make entry in variables index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}% +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} +\def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}} + +% @deftypevr {Global Flag} int enable + +\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} + +\def\deftypevrheader #1#2#3{\dovarind#3 \relax% +\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} +\interlinepenalty=10000 +\endgraf\penalty 10000\vskip -\parskip\penalty 10000 +\endgroup} + +% This definition is run if you use @defvarx +% anywhere other than immediately after a @defvar or @defvarx. + +\def\defvrx #1 {\errmessage{@defvrx in invalid context}} +\def\defvarx #1 {\errmessage{@defvarx in invalid context}} +\def\defoptx #1 {\errmessage{@defoptx in invalid context}} +\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}} +\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}} + +% Now define @deftp +% Args are printed in bold, a slight difference from @defvar. + +\def\deftpargs #1{\bf \defvarargs{#1}} + +% @deftp Class window height width ... + +\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} + +\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% +\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} + +% This definition is run if you use @deftpx, etc +% anywhere other than immediately after a @deftp, etc. + +\def\deftpx #1 {\errmessage{@deftpx in invalid context}} + + +\message{cross reference,} +% Define cross-reference macros +\newwrite \auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% \setref{foo} defines a cross-reference point named foo. + +\def\setref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ysectionnumberandtype}} + +\def\unnumbsetref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Ynothing}} + +\def\appendixsetref#1{% +\dosetq{#1-title}{Ytitle}% +\dosetq{#1-pg}{Ypagenumber}% +\dosetq{#1-snt}{Yappendixletterandtype}} + +% \xref, \pxref, and \ref generate cross-references to specified points. +% For \xrefX, #1 is the node name, #2 the name of the Info +% cross-reference, #3 the printed node name, #4 the name of the Info +% file, #5 the name of the printed manual. All but the node name can be +% omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \def\printedmanual{\ignorespaces #5}% + \def\printednodename{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual}% + \setbox0=\hbox{\printednodename}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printednodename{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1>0pt% + % It is in another manual, so we don't have it. + \def\printednodename{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printednodename{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printednodename{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printednodename'' in \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive \refx{#1-snt}{}}% + \space [\printednodename],\space + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi +\endgroup} + +% \dosetq is the interface for calls from other macros + +% Use \turnoffactive so that punctuation chars such as underscore +% work in node names. +\def\dosetq #1#2{{\let\folio=0 \turnoffactive +\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}% +\next}} + +% \internalsetq {foo}{page} expands into +% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} +% When the aux file is read, ' is the escape character + +\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} + +% Things to be expanded by \internalsetq + +\def\Ypagenumber{\folio} + +\def\Ytitle{\thissection} + +\def\Ynothing{} + +\def\Ysectionnumberandtype{% +\ifnum\secno=0 \putwordChapter\xreftie\the\chapno % +\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\def\Yappendixletterandtype{% +\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% +\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\gdef\xreftie{'tie} + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Non-3.0. +\else + \def\linenumber{\the\inputlineno:\space} +\fi + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. + +\def\refx#1#2{% + \expandafter\ifx\csname X#1\endcsname\relax + % If not defined, say something at least. + $\langle$un\-de\-fined$\rangle$% + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \else + % It's defined, so just use it. + \csname X#1\endcsname + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. +\def\xrdef #1#2{{% + \catcode`\'=\other + \expandafter\gdef\csname X#1\endcsname{#2}% +}} + +% Read the last existing aux file, if any. No error if none exists. +\def\readauxfile{\begingroup + \catcode`\^^@=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\ =\other + \catcode`\^^L=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode`\=\other + \catcode26=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + \catcode`\@=\other + \catcode`\^=\other + % It was suggested to define this as 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + % `\+ does not work, so use 43. + \catcode43=\other + % Make the characters 128-255 be printing characters + {% + \count 1=128 + \def\loop{% + \catcode\count 1=\other + \advance\count 1 by 1 + \ifnum \count 1<256 \loop \fi + }% + }% + % The aux file uses ' as the escape (for now). + % Turn off \ as an escape so we do not lose on + % entries which were dumped with control sequences in their names. + % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ + % Reference to such entries still does not work the way one would wish, + % but at least they do not bomb out when the aux file is read in. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\%=\other + \catcode`\'=0 + \catcode`\\=\other + % + \openin 1 \jobname.aux + \ifeof 1 \else + \closein 1 + \input \jobname.aux + \global\havexrefstrue + \global\warnedobstrue + \fi + % Open the new aux file. TeX will close it automatically at exit. + \openout\auxfile=\jobname.aux +\endgroup} + + +% Footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only.. +\let\footnotestyle=\comment + +\let\ptexfootnote=\footnote + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \footnotezzz +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset and anything else that uses +% \parseargline fail inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\long\gdef\footnotezzz{\insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + % Hang the footnote text off the number. + \hang + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t + \else\let\next\f@t\fi \next} +\def\f@@t{\bgroup\aftergroup\@foot\let\next} +\def\f@t#1{#1\@foot} +\def\@foot{\strut\egroup} + +}%end \catcode `\@=11 + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + + +% End of control word definitions. + +\message{and turning on texinfo input format.} + +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% Set some numeric style parameters, for 8.5 x 11 format. + +\hsize = 6in +\hoffset = .25in +\newdimen\defaultparindent \defaultparindent = 15pt +\parindent = \defaultparindent +\parskip 3pt plus 2pt minus 1pt +\setleading{13.2pt} +\advance\topskip by 1.2cm + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness=10000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. This makes it come to about 9pt for the 8.5x11 format. +% +\ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% +\else + \emergencystretch = \hsize + \divide\emergencystretch by 45 +\fi + +% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25) +\def\smallbook{ + \global\chapheadingskip = 15pt plus 4pt minus 2pt + \global\secheadingskip = 12pt plus 3pt minus 2pt + \global\subsecheadingskip = 9pt plus 2pt minus 2pt + % + \global\lispnarrowing = 0.3in + \setleading{12pt} + \advance\topskip by -1cm + \global\parskip 2pt plus 1pt + \global\hsize = 5in + \global\vsize=7.5in + \global\tolerance=700 + \global\hfuzz=1pt + \global\contentsrightmargin=0pt + \global\deftypemargin=0pt + \global\defbodyindent=.5cm + % + \global\pagewidth=\hsize + \global\pageheight=\vsize + % + \global\let\smalllisp=\smalllispx + \global\let\smallexample=\smalllispx + \global\def\Esmallexample{\Esmalllisp} +} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{ +\global\tolerance=700 +\global\hfuzz=1pt +\setleading{12pt} +\global\parskip 15pt plus 1pt + +\global\vsize= 53\baselineskip +\advance\vsize by \topskip +%\global\hsize= 5.85in % A4 wide 10pt +\global\hsize= 6.5in +\global\outerhsize=\hsize +\global\advance\outerhsize by 0.5in +\global\outervsize=\vsize +\global\advance\outervsize by 0.6in + +\global\pagewidth=\hsize +\global\pageheight=\vsize +} + +\bindingoffset=0pt +\normaloffset=\hoffset +\pagewidth=\hsize +\pageheight=\vsize + +% Allow control of the text dimensions. Parameters in order: textheight; +% textwidth; voffset; hoffset; binding offset; topskip. +% All require a dimension; +% header is additional; added length extends the bottom of the page. + +\def\changepagesizes#1#2#3#4#5#6{ + \global\vsize= #1 + \global\topskip= #6 + \advance\vsize by \topskip + \global\voffset= #3 + \global\hsize= #2 + \global\outerhsize=\hsize + \global\advance\outerhsize by 0.5in + \global\outervsize=\vsize + \global\advance\outervsize by 0.6in + \global\pagewidth=\hsize + \global\pageheight=\vsize + \global\normaloffset= #4 + \global\bindingoffset= #5} + +% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin +% 29mm, hence bottom margin 28mm, nominal side margin 3cm. +\def\afourlatex + {\global\tolerance=700 + \global\hfuzz=1pt + \setleading{12pt} + \global\parskip 15pt plus 1pt + \advance\baselineskip by 1.6pt + \changepagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm} + } + +% Use @afourwide to print on European A4 paper in wide format. +\def\afourwide{\afourpaper +\changepagesizes{9.5in}{6.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} + +% This macro is used to make a character print one way in ttfont +% where it can probably just be output, and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt \char '042}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt \char '176}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}} + +\catcode`\|=\active +\def|{{\tt \char '174}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +%\catcode 27=\active +%\def^^[{$\diamondsuit$} + +% Set up an active definition for =, but don't enable it most of the time. +{\catcode`\==\active +\global\def={{\tt \char 61}}} + +\catcode`+=\active +\catcode`\_=\active + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +\catcode`\@=0 + +% \rawbackslashxx output one backslash character in current font +\global\chardef\rawbackslashxx=`\\ +%{\catcode`\\=\other +%@gdef@rawbackslashxx{\}} + +% \rawbackslash redefines \ as input to do \rawbackslashxx. +{\catcode`\\=\active +@gdef@rawbackslash{@let\=@rawbackslashxx }} + +% \normalbackslash outputs one backslash in fixed width font. +\def\normalbackslash{{\tt\rawbackslashxx}} + +% Say @foo, not \foo, in error messages. +\escapechar=`\@ + +% \catcode 17=0 % Define control-q +\catcode`\\=\active + +% Used sometimes to turn off (effectively) the active characters +% even after parsing them. +@def@turnoffactive{@let"=@normaldoublequote +@let\=@realbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +@def@normalturnoffactive{@let"=@normaldoublequote +@let\=@normalbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\{ in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also back turn on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active @catcode`@_=@active} + +%% These look ok in all fonts, so just make them not special. The @rm below +%% makes sure that the current font starts out as the newly loaded cmr10 +@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other + +@textfonts +@rm + +@c Local variables: +@c page-delimiter: "^\\\\message" +@c End: diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index f69dc083..d37988d1 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,152 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Tue Mar 11 10:31:47 1997 Theodore Ts'o + + * icount.c: New file which implements an inode count abstraction. + This significantly reduces amount of memory needed to + check really large filesystems. + + * pass1.c, pass2.c, pass3.c, pass4.c: Modified to use the icount + abstraction. + +Fri Mar 7 08:28:55 1997 Theodore Ts'o + + * dirinfo.c (dir_info_iter): New function which allows iteration + over the directories in the dir_info map. + + * pass3.c (pass3, check_directory): Speed up pass 3 by iterating + over all directories in the dir_info structure, instead of + iterating all inodes looking for directories, and then + looking up the directories using get_dir_info(). + +Sat Feb 1 11:33:43 1997 Theodore Ts'o + + * pass1.c (pass1, process_block): + * pass2.c (pass2): Use the ext2_dblist abstraction instead of + manual code to manage the directory block list information. + + * pass1.c (check_blocks), pass1b.c (pass1b), pass2.c + (deallocate_inode): Call the ext2 library routine + ext2_inode_has_valid_blocks() instead of + inode_has_valid_blocks(). + + * swapfs.c (swap_inodes): Add check so that we don't try to call + swap_inode_blocks unless the inode has valid blocks. + (Otherwise a long fast symlink might cause + swap_inode_blocks to erroneously get called.) + +Wed Jan 22 14:42:53 1997 Theodore Ts'o + + * problem.c, problem.h: Added entries for PR_2_DOT_NULL_TERM and + PR_2_DOT_DOT_NULL_TERM. + + * pass2.c (check_dot, check_dot_dot): Make sure the new . and + .. entries are null-terminated, since the 2.0 kernel + requires this (for no good reason). + +Mon Jan 20 20:05:11 1997 Theodore Ts'o + + * pass1.c (pass1): Set the EXT2_SF_SKIP_MISSING_ITABLE flag so + that we can recover from missing inode tables. + + * dirinfo.c (get_dir_info): If there are no directories in the + dir_info abstraction, don't core dump (because dir_info is + NULL). + + * e2fsck.c (main): Don't try using the backup superblocks if there + aren't any. + (check_super_block): If there are illegal inode table or + bitmaps, set the filesystem as being in error. + +Wed Jan 15 11:32:01 1997 Theodore Ts'o + + * pass2.c (check_dir_block): Added check to make sure that rec_len + is a a multiple of 4 (so that the directory entries are + 4-byte aligned). + +Sat Dec 28 12:16:32 1996 Theodore Ts'o + + * Makefile.in (uninstall): Uninstall all programs in the PROGS + line. + (PROGS): Don't build and install the extend program by + default. + + +Sat Dec 7 16:41:02 1996 Theodore Ts'o + + * pass1.c (process_inodes): Make sure the stashed_ino variable is + saved and restored as well. + (pass1): For fast sym links, skip the check_blocks + processing step altogether. + +Mon Dec 2 09:28:24 1996 Theodore Ts'o + + * problem.c, message.c: New files, to completely refurbish how + filesystem problems are reported to the user. The + diagnostic messages are now encoded out in an easily + customizable, extensible format. The messages printed out + in preen mode are all on one line, and contain the device + name. + +Fri Nov 29 20:26:08 1996 Theodore Ts'o + + * swapfs.c (swap_inodes): When swapping a filesystem, ignore + deleted files. + + * pass1.c (pass1): Ignore missing inode table errors during the + scan, and just skip past those inodes. + + * pass3.c (check_root): Remove root_ino argument, and assume that + the root inode must be EXT2_ROOT_INO. Move responsibility + of setting the parent of the root inode in the directory + inode structure to pass2(). + + * pass2.c (check_dir_block): Don't allow links to the root + directory. + + * dirinfo.c (add_dir_info): Remove last argument to add_dir_info, + since the inode is no longer used. + +Tue Oct 15 00:06:49 1996 Theodore Ts'o + + * e2fsck.c (main): If the superblock magic number is wrong, or the + block group fails a sanity check, then automatically + restart trying to use the backup superblocks. + + * pass1.c (mark_table_blocks): Make the inode tables ahead of + everything else; in the case where a bitmap block overlays + the inode table, the inode table should take precedence. + + * pass2.c (maybe_clear_entry): Make the deleted/unused error + message fit on one line, since the error can happen during + a preen pass. (We eventually need to revamp the whole + e2fsck error reporting and prompting system, but that's a + job for another day.) + +Mon Oct 14 22:29:49 1996 Theodore Ts'o + + * e2fsck.c (main): Read in the list badblocks into fs->badblocks + for the benefit of the inode scan functions. + + * pass1.c (pass1): If ext2fs_get_next_inode() returns an error + indicating that an inode is in a bad block, mark that + inode as being used, as well as in the inode "bb" map. + + * pass2.c (maybe_clear_entry): New function which generalizes the + error cases when a directory entry needs to be cleared. + (check_dir_block): If an inode is in the "bb" map, offer + to clear the directory entry, since the inode is in a bad + block. + + * pass4.c (pass4): If an inode is marked as used, but is is marked + in the "bb" map, don't process it as a disconnected inode. + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Mon Oct 7 00:45:30 1996 Theodore Ts'o * e2fsck.c (main): Print out the version number of the shared diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index f4330c38..9d0cbb13 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -12,7 +12,7 @@ LDFLAG_STATIC = @LDFLAG_STATIC@ @MCONFIG@ -PROGS= e2fsck extend @EXTRA_PROGS@ +PROGS= e2fsck @EXTRA_PROGS@ MANPAGES= e2fsck.8 LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBUUID) @@ -26,7 +26,7 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) $(PROFILED_LIBUUI .c.o: $(CC) -c $(ALL_CFLAGS) $< -o $@ -@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -pg -o profiled/$*.o -c $< +@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< # # Flags for using Checker @@ -53,12 +53,14 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) $(PROFILED_LIBUUI #MCHECK= -DMCHECK OBJS= e2fsck.o pass1.o pass1b.o pass2.o pass3.o pass4.o pass5.o \ - swapfs.o badblocks.o util.o dirinfo.o ehandler.o $(MTRACE_OBJ) + swapfs.o badblocks.o util.o dirinfo.o ehandler.o problem.o message.o \ + $(MTRACE_OBJ) PROFILED_OBJS= profiled/e2fsck.o profiled/pass1.o profiled/pass1b.o \ profiled/pass2.o profiled/pass3.o profiled/pass4.o profiled/pass5.o \ profiled/badblocks.o profiled/util.o profiled/dirinfo.o \ - profiled/ehandler.o profiled/swapfs.o + profiled/ehandler.o profiled/message.o profiled/problem.o \ + profiled/swapfs.o SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/pass1.c \ @@ -71,6 +73,7 @@ SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/util.c \ $(srcdir)/dirinfo.c \ $(srcdir)/ehandler.c \ + $(srcdir)/problem.c \ $(MTRACE_SRC) all:: profiled $(PROGS) e2fsck.static e2fsck.shared $(MANPAGES) @@ -87,8 +90,8 @@ e2fsck.static: $(OBJS) $(STATIC_DEPLIBS) e2fsck.shared: $(OBJS) $(DEPLIBS) $(LD) $(ALL_LDFLAGS) -o e2fsck.shared $(OBJS) $(LIBS) -e2fsck.profiled: $(PROFILED_OBJS) $(DEPLIBS) - $(LD) $(ALL_LDFLAGS) -pg -o e2fsck.profiled $(PROFILED_OBJS) \ +e2fsck.profiled: $(PROFILED_OBJS) $(PROFILED_DEPLIBS) + $(LD) $(ALL_LDFLAGS) -g -pg -o e2fsck.profiled $(PROFILED_OBJS) \ $(PROFILED_LIBS) extend: extend.o @@ -122,8 +125,9 @@ install: $(PROGS) $(MANPAGES) installdirs done uninstall: - $(RM) -f $(sbindir)/e2fsck - $(RM) -f $(sbindir)/flushb + for i in $(PROGS); do \ + $(RM) -f $(sbindir)/$$i; \ + done $(RM) -f $(sbindir)/fsck.ext2 for i in $(MANPAGES); do \ $(RM) -f $(man8dir)/$$i; \ @@ -143,46 +147,66 @@ distclean: clean # Makefile dependencies follow. This must be the last section in # the Makefile.in file # -e2fsck.o: $(srcdir)/e2fsck.c $(top_srcdir)/lib/et/com_err.h \ +e2fsck.o: $(srcdir)/e2fsck.c \ + $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/uuid/uuid.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/../version.h -pass1.o: $(srcdir)/pass1.c $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ - $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h -pass1b.o: $(srcdir)/pass1b.c $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ - $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h -pass2.o: $(srcdir)/pass2.c $(top_srcdir)/lib/et/com_err.h $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h \ + $(srcdir)/../version.h +pass1.o: $(srcdir)/pass1.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h -pass3.o: $(srcdir)/pass3.c $(top_srcdir)/lib/et/com_err.h $(srcdir)/e2fsck.h \ - $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h +pass1b.o: $(srcdir)/pass1b.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h \ + $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h +pass2.o: $(srcdir)/pass2.c $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/io.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ + $(srcdir)/problem.h +pass3.o: $(srcdir)/pass3.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h \ + $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h pass4.o: $(srcdir)/pass4.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h -pass5.o: $(srcdir)/pass5.c $(top_srcdir)/lib/et/com_err.h $(srcdir)/e2fsck.h \ - $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -badblocks.o: $(srcdir)/badblocks.c $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h +pass5.o: $(srcdir)/pass5.c $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/io.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h +badblocks.o: $(srcdir)/badblocks.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(top_srcdir)/lib/ext2fs/bitops.h -util.o: $(srcdir)/util.c $(srcdir)/e2fsck.h \ +util.o: $(srcdir)/util.c \ + $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(top_srcdir)/lib/ext2fs/bitops.h dirinfo.o: $(srcdir)/dirinfo.c $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ - $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h -ehandler.o: $(srcdir)/ehandler.c $(srcdir)/e2fsck.h \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/io.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h +ehandler.o: $(srcdir)/ehandler.c \ + $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ - $(top_srcdir)/lib/ext2fs/bitops.h + $(top_srcdir)/lib/ext2fs/bitops.h +problem.o: $(srcdir)/problem.c \ + $(srcdir)/e2fsck.h \ + $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(top_srcdir)/lib/ext2fs/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/problem.h diff --git a/e2fsck/dirinfo.c b/e2fsck/dirinfo.c index 6c78dd1f..5218234c 100644 --- a/e2fsck/dirinfo.c +++ b/e2fsck/dirinfo.c @@ -24,12 +24,11 @@ int get_num_dirs(ext2_filsys fs) } /* - * This subroutine is called during pass1 to stash away the block - * numbers for the directory, which we will need later. The idea is - * to avoid reading directory inodes twice. + * This subroutine is called during pass1 to create a directory info + * entry. During pass1, the passed-in parent is 0; it will get filled + * in during pass2. */ -void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent, - struct ext2_inode *inode) +void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent) { struct dir_info *dir; int i, j; @@ -61,7 +60,7 @@ void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent, * the dir_info array needs to be sorted by inode number for * get_dir_info()'s sake. */ - if (dir_info_count && dir_info[dir_info_count-1].ino > ino) { + if (dir_info_count && dir_info[dir_info_count-1].ino >= ino) { for (i = dir_info_count-1; i > 0; i--) if (dir_info[i-1].ino < ino) break; @@ -87,6 +86,8 @@ struct dir_info *get_dir_info(ino_t ino) low = 0; high = dir_info_count-1; + if (!dir_info) + return 0; if (ino == dir_info[low].ino) return &dir_info[low]; if (ino == dir_info[high].ino) @@ -118,3 +119,14 @@ void free_dir_info(ext2_filsys fs) dir_info_size = 0; dir_info_count = 0; } + +/* + * A simple interator function + */ +struct dir_info *dir_info_iter(int *control) +{ + if (*control >= dir_info_count) + return 0; + + return(dir_info + (*control)++); +} diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c index f081d668..9cebae4f 100644 --- a/e2fsck/e2fsck.c +++ b/e2fsck/e2fsck.c @@ -1,8 +1,12 @@ /* * e2fsck.c - a consistency checker for the new extended file system. * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ /* Usage: e2fsck [-dfpnsvy] device @@ -42,6 +46,7 @@ #include "et/com_err.h" #include "uuid/uuid.h" #include "e2fsck.h" +#include "problem.h" #include "../version.h" extern int isatty(int); @@ -60,6 +65,7 @@ int rwflag = 1; int swapfs = 0; int normalize_swapfs = 0; int inode_buffer_blocks = 0; +blk_t use_superblock; blk_t superblock; int blocksize = 0; int verbose = 0; @@ -236,8 +242,11 @@ static void relocate_hint(void) { static hint_issued = 0; - /* Only issue the hint once */ - if (hint_issued) + /* + * Only issue the hint once, and only if we're using the + * primary superblocks. + */ + if (hint_issued || superblock) return; printf("Note: if there is several inode or block bitmap blocks\n" @@ -258,6 +267,9 @@ static void check_super_block(ext2_filsys fs) int i; blk_t should_be; errcode_t retval; + struct problem_context pctx; + + clear_problem_context(&pctx); /* * Verify the super block constants... @@ -334,55 +346,52 @@ static void check_super_block(ext2_filsys fs) last_block = first_block + blocks_per_group; for (i = 0; i < fs->group_desc_count; i++) { + pctx.group = i; + if (i == fs->group_desc_count - 1) last_block = fs->super->s_blocks_count; if ((fs->group_desc[i].bg_block_bitmap < first_block) || (fs->group_desc[i].bg_block_bitmap >= last_block)) { relocate_hint(); - printf("Block bitmap for group %d is not in group. " - "(block %u)\n", - i, fs->group_desc[i].bg_block_bitmap); - preenhalt(fs); - if (!ask("Relocate", 1)) { - fatal_error("Block bitmap not in group"); + pctx.blk = fs->group_desc[i].bg_block_bitmap; + if (fix_problem(fs, PR_0_BB_NOT_GROUP, &pctx)) { + fs->group_desc[i].bg_block_bitmap = 0; + invalid_block_bitmap[i]++; + invalid_bitmaps++; } - fs->group_desc[i].bg_block_bitmap = 0; - invalid_block_bitmap[i]++; - invalid_bitmaps++; } if ((fs->group_desc[i].bg_inode_bitmap < first_block) || (fs->group_desc[i].bg_inode_bitmap >= last_block)) { relocate_hint(); - printf("Inode bitmap group %d not in group. " - "(block %u)\n", - i, fs->group_desc[i].bg_inode_bitmap); - preenhalt(fs); - if (!ask("Relocate", 1)) { - fatal_error("Inode bitmap not in group"); + pctx.blk = fs->group_desc[i].bg_inode_bitmap; + if (fix_problem(fs, PR_0_IB_NOT_GROUP, &pctx)) { + fs->group_desc[i].bg_inode_bitmap = 0; + invalid_inode_bitmap[i]++; + invalid_bitmaps++; } - fs->group_desc[i].bg_inode_bitmap = 0; - invalid_inode_bitmap[i]++; - invalid_bitmaps++; } if ((fs->group_desc[i].bg_inode_table < first_block) || ((fs->group_desc[i].bg_inode_table + fs->inode_blocks_per_group - 1) >= last_block)) { relocate_hint(); - printf("Inode table for group %d not in group. " - "(block %u)\n", - i, fs->group_desc[i].bg_inode_table); - printf("WARNING: SEVERE DATA LOSS POSSIBLE.\n"); - preenhalt(fs); - if (!ask("Relocate", 1)) { - fatal_error("Inode table not in group"); + pctx.blk = fs->group_desc[i].bg_inode_table; + if (fix_problem(fs, PR_0_ITABLE_NOT_GROUP, &pctx)) { + fs->group_desc[i].bg_inode_table = 0; + invalid_inode_table[i]++; + invalid_bitmaps++; } - fs->group_desc[i].bg_inode_table = 0; - invalid_inode_table[i]++; - invalid_bitmaps++; } first_block += fs->super->s_blocks_per_group; last_block += fs->super->s_blocks_per_group; } + /* + * If we have invalid bitmaps, set the error state of the + * filesystem. + */ + if (invalid_bitmaps && rwflag) { + fs->super->s_state &= ~EXT2_VALID_FS; + ext2fs_mark_super_dirty(fs); + } /* * If the UUID field isn't assigned, assign it. @@ -492,7 +501,7 @@ static void PRS(int argc, char *argv[]) /* What we do by default, anyway! */ break; case 'b': - superblock = atoi(optarg); + use_superblock = atoi(optarg); break; case 'B': blocksize = atoi(optarg); @@ -588,7 +597,9 @@ int main (int argc, char *argv[]) errcode_t retval = 0; int exit_value = FSCK_OK; int i; - ext2_filsys fs; + ext2_filsys fs = 0; + io_manager io_ptr; + struct ext2fs_sb *s; #ifdef MTRACE mtrace(); @@ -601,7 +612,7 @@ int main (int argc, char *argv[]) PRS(argc, argv); - if (!preen) + if (!preen || show_version_only) fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", E2FSPROGS_VERSION, E2FSPROGS_DATE, EXT2FS_VERSION, EXT2FS_DATE); @@ -618,27 +629,46 @@ int main (int argc, char *argv[]) if (!isatty (0) || !isatty (1)) die ("need terminal for interactive repairs"); } + superblock = use_superblock; restart: +#if 1 + io_ptr = unix_io_manager; +#else + io_ptr = test_io_manager; + test_io_backing_manager = unix_io_manager; +#endif sync_disks(); if (superblock && blocksize) { retval = ext2fs_open(filesystem_name, rwflag ? EXT2_FLAG_RW : 0, - superblock, blocksize, unix_io_manager, - &fs); + superblock, blocksize, io_ptr, &fs); } else if (superblock) { for (i=0; possible_block_sizes[i]; i++) { retval = ext2fs_open(filesystem_name, rwflag ? EXT2_FLAG_RW : 0, superblock, possible_block_sizes[i], - unix_io_manager, &fs); + io_ptr, &fs); if (!retval) break; } } else retval = ext2fs_open(filesystem_name, rwflag ? EXT2_FLAG_RW : 0, - 0, 0, unix_io_manager, &fs); + 0, 0, io_ptr, &fs); + if (!superblock && !preen && + ((retval == EXT2_ET_BAD_MAGIC) || + ((retval == 0) && ext2fs_check_desc(fs)))) { + if (!fs || (fs->group_desc_count > 1)) { + printf("%s trying backup blocks...\n", + retval ? "Couldn't find ext2 superblock," : + "Group descriptors look bad..."); + superblock = 8193; + if (fs) + ext2fs_close(fs); + goto restart; + } + } if (retval) { com_err(program_name, retval, "while trying to open %s", filesystem_name); @@ -656,16 +686,28 @@ restart: printf(corrupt_msg); fatal_error(0); } - #ifdef EXT2_CURRENT_REV if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) { com_err(program_name, EXT2_ET_REV_TOO_HIGH, "while trying to open %s", filesystem_name); + goto get_newer; + } +#endif + /* + * Check for compatibility with the feature sets. We have to + * check because we need to be more stringent than ext2fs_open + */ + s = (struct ext2fs_sb *) fs->super; + if (s->s_feature_compat || s->s_feature_incompat || + s->s_feature_ro_compat) { + com_err(program_name, EXT2_ET_UNSUPP_FEATURE, + " (%s)", filesystem_name); + get_newer: printf ("Get a newer version of e2fsck!\n"); fatal_error(0); } -#endif + /* * If the user specified a specific superblock, presumably the * master superblock has been trashed. So we mark the @@ -715,6 +757,14 @@ restart: */ ext2fs_mark_valid(fs); + retval = ext2fs_read_bb_inode(fs, &fs->badblocks); + if (retval) { + com_err(program_name, retval, + "while reading bad blocks inode"); + preenhalt(fs); + printf("This doesn't bode well, but we'll try to go on...\n"); + } + pass1(fs); free(invalid_inode_bitmap); free(invalid_block_bitmap); @@ -723,6 +773,7 @@ restart: ext2fs_close(fs); printf("Restarting e2fsck from the beginning...\n"); restart_e2fsck = 0; + superblock = use_superblock; goto restart; } pass2(fs); diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index 2e0fa777..9eb0dad5 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -47,13 +47,13 @@ /* * Inode count arrays */ -extern unsigned short * inode_count; -extern unsigned short * inode_link_info; +extern ext2_icount_t inode_count; +extern ext2_icount_t inode_link_info; /* * The directory information structure; stores directory information * collected in earlier passes, to avoid disk i/o in fetching the - * directoryt information. + * directory information. */ struct dir_info { ino_t ino; /* Inode number */ @@ -61,16 +61,6 @@ struct dir_info { ino_t parent; /* Parent according to treewalk */ }; -struct dir_block_struct { - ino_t ino; - blk_t blk; - int blockcnt; -}; - -struct dir_block_struct *dir_blocks; -int dir_block_count; -int dir_block_size; - /* * This structure is used for keeping track of how much resources have * been used for a particular pass of e2fsck. @@ -91,6 +81,7 @@ extern const char * device_name; extern ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */ extern ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */ extern ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */ +extern ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */ extern ext2fs_block_bitmap block_found_map; /* Blocks which are in use */ extern ext2fs_block_bitmap block_dup_map; /* Blocks which are used by more than once */ @@ -175,11 +166,11 @@ extern void read_bad_blocks_file(ext2_filsys fs, const char *bad_blocks_file, extern void test_disk(ext2_filsys fs); /* dirinfo.c */ -extern void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent, - struct ext2_inode *inode); +extern void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent); extern struct dir_info *get_dir_info(ino_t ino); extern void free_dir_info(ext2_filsys fs); extern int get_num_dirs(ext2_filsys fs); +extern struct dir_info *dir_info_iter(int *control); /* ehandler.c */ extern const char *ehandler_operation(const char *op); diff --git a/e2fsck/message.c b/e2fsck/message.c new file mode 100644 index 00000000..c5f25938 --- /dev/null +++ b/e2fsck/message.c @@ -0,0 +1,329 @@ +/* + * message.c --- print e2fsck messages (with compression) + * + * Copyright 1996, 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * print_e2fsck_message() prints a message to the user, using + * compression techniques and expansions of abbreviations. + * + * The following % expansions are supported: + * + * %b block number + * %B integer + * %di ->ino inode number + * %dn ->name string + * %D inode number + * %g integer + * %i inode number + * %Is -> i_size + * %Ib -> i_blocks + * %Il -> i_links_count + * %Im -> i_mode + * %IM -> i_mtime + * %IF -> i_faddr + * %If -> i_file_acl + * %Id -> i_dir_acl + * %j inode number + * %N + * %p ext2fs_get_pathname of directory + * %P ext2fs_get_pathname of ->ino with as + * the containing directory. (If dirent is NULL + * then return the pathname of directory ) + * %q ext2fs_get_pathname of directory + * %Q ext2fs_get_pathname of directory with as + * the containing directory. + * + * The following '@' expansions are supported: + * + * @b block + * @B bitmap + * @C conflicts with some other fs block + * @i inode + * @D deleted + * @d directory + * @e entry + * @E Entry '%Dn' in %p (%i) + * @F for @i %i (%Q) is + * @g group + * @l lost+found + * @L is a link + * @u unattached + * @r root inode + * @z zero-length + */ + +#include +#include +#include +#include +#include + +#include "e2fsck.h" + +#include "problem.h" + +#ifdef __GNUC__ +#define _INLINE_ __inline__ +#else +#define _INLINE_ +#endif + +/* + * This structure defines the abbreviations used by the text strings + * below. The first character in the string is the index letter. An + * abbreviation of the form '@' is expanded by looking up the index + * letter in the table below. + */ +static const char *abbrevs[] = { + "bblock", + "Bbitmap", + "Cconflicts with some other fs @b", + "iinode", + "Ddeleted", + "ddirectory", + "eentry", + "E@e '%Dn' in %p (%i)", + "Ffor @i %i (%Q) is", + "ggroup", + "llost+found", + "Lis a link", + "uunattached", + "rroot @i", + "sshould be", + "zzero-length", + "@@", + 0 + }; + +/* + * Give more user friendly names to the "special" inodes. + */ +#define num_special_inodes 7 +static const char *special_inode_name[] = +{ + "", /* 0 */ + "", /* 1 */ + "/", /* 2 */ + "", /* 3 */ + "", /* 4 */ + "", /* 5 */ + "" /* 6 */ +}; + +/* + * This function prints a pathname, using the ext2fs_get_pathname + * function + */ +static void print_pathname(ext2_filsys fs, ino_t dir, ino_t ino) +{ + errcode_t retval; + char *path; + + if (!dir && (ino < num_special_inodes)) { + fputs(special_inode_name[ino], stdout); + return; + } + + retval = ext2fs_get_pathname(fs, dir, ino, &path); + if (retval) + fputs("???", stdout); + else { + fputs(path, stdout); + free(path); + } +} + +/* + * This function handles the '@' expansion. We allow recursive + * expansion; an @ expression can contain further '@' and '%' + * expressions. + */ +static _INLINE_ void expand_at_expression(ext2_filsys fs, char ch, + struct problem_context *ctx, + int *first) +{ + const char **cpp, *str; + + /* Search for the abbreviation */ + for (cpp = abbrevs; *cpp; cpp++) { + if (ch == *cpp[0]) + break; + } + if (*cpp) { + str = (*cpp) + 1; + if (*first && islower(*str)) { + *first = 0; + fputc(toupper(*str++), stdout); + } + print_e2fsck_message(fs, str, ctx, *first); + } else + printf("@%c", ch); +} + +/* + * This function expands '%kX' expressions + */ +static _INLINE_ void expand_inode_expression(char ch, + struct problem_context *ctx) +{ + struct ext2_inode *inode; + char * time_str; + time_t t; + + if (!ctx || !ctx->inode) + goto no_inode; + + inode = ctx->inode; + + switch (ch) { + case 's': + printf("%u", inode->i_size); + break; + case 'b': + printf("%u", inode->i_blocks); + break; + case 'l': + printf("%d", inode->i_links_count); + break; + case 'm': + printf("0%o", inode->i_mode); + break; + case 'M': + t = inode->i_mtime; + time_str = ctime(&t); + printf("%.24s", time_str); + break; + case 'F': + printf("%u", inode->i_faddr); + break; + case 'f': + printf("%u", inode->i_file_acl); + break; + case 'd': + printf("%u", inode->i_dir_acl); + break; + default: + no_inode: + printf("%%I%c", ch); + break; + } +} + +/* + * This function expands '%dX' expressions + */ +static _INLINE_ void expand_dirent_expression(char ch, + struct problem_context *ctx) +{ + struct ext2_dir_entry *dirent; + int len; + + if (!ctx || !ctx->dirent) + goto no_dirent; + + dirent = ctx->dirent; + + switch (ch) { + case 'i': + printf("%u", dirent->inode); + break; + case 'n': + len = dirent->name_len; + if (len > EXT2_NAME_LEN) + len = EXT2_NAME_LEN; + if (len > dirent->rec_len) + len = dirent->rec_len; + printf("%.*s", dirent->name_len, dirent->name); + break; + default: + no_dirent: + printf("%%D%c", ch); + break; + } +} + +static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch, + struct problem_context *ctx) +{ + if (!ctx) + goto no_context; + + switch (ch) { + case '%': + fputc('%', stdout); + break; + case 'b': + printf("%u", ctx->blk); + break; + case 'B': + printf("%d", ctx->blkcount); + break; + case 'd': + printf("%lu", ctx->dir); + break; + case 'g': + printf("%d", ctx->group); + break; + case 'i': + printf("%lu", ctx->ino); + break; + case 'j': + printf("%lu", ctx->ino2); + break; + case 'N': + printf("%u", ctx->num); + break; + case 'p': + print_pathname(fs, ctx->ino, 0); + break; + case 'P': + print_pathname(fs, ctx->ino2, + ctx->dirent ? ctx->dirent->inode : 0); + break; + case 'q': + print_pathname(fs, ctx->dir, 0); + break; + case 'Q': + print_pathname(fs, ctx->dir, ctx->ino); + break; + default: + no_context: + printf("%%%c", ch); + break; + } +} + +void print_e2fsck_message(ext2_filsys fs, const char *msg, + struct problem_context *ctx, int first) +{ + const char * cp; + int i; + + for (cp = msg; *cp; cp++) { + if (cp[0] == '@') { + cp++; + expand_at_expression(fs, *cp, ctx, &first); + } else if (cp[0] == '%' && cp[1] == 'I') { + cp += 2; + expand_inode_expression(*cp, ctx); + } else if (cp[0] == '%' && cp[1] == 'D') { + cp += 2; + expand_dirent_expression(*cp, ctx); + } else if ((cp[0] == '%')) { + cp++; + expand_percent_expression(fs, *cp, ctx); + } else { + for (i=0; cp[i]; i++) + if ((cp[i] == '@') || cp[i] == '%') + break; + printf("%.*s", i, cp); + cp += i-1; + } + first = 0; + } +} diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 0c860961..2b058676 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -1,8 +1,12 @@ /* * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * * Pass 1 of e2fsck iterates over all the inodes in the filesystems, * and applies the following tests to each inode: @@ -16,6 +20,7 @@ * - A bitmap of which inodes are in use. (inode_used_map) * - A bitmap of which inodes are directories. (inode_dir_map) * - A bitmap of which inodes have bad fields. (inode_bad_map) + * - A bitmap of which inodes are in bad blocks. (inode_bb_map) * - A bitmap of which blocks are in use. (block_found_map) * - A bitmap of which blocks are in use by two inodes (block_dup_map) * - The data blocks of the directory inodes. (dir_map) @@ -37,6 +42,7 @@ #include #include "e2fsck.h" +#include "problem.h" #ifdef NO_INLINE_FUNCS #define _INLINE_ @@ -64,28 +70,31 @@ int fs_fragmented = 0; ext2fs_inode_bitmap inode_used_map = 0; /* Inodes which are in use */ ext2fs_inode_bitmap inode_bad_map = 0; /* Inodes which are bad in some way */ ext2fs_inode_bitmap inode_dir_map = 0; /* Inodes which are directories */ +ext2fs_inode_bitmap inode_bb_map = 0; /* Inodes which are in bad blocks */ ext2fs_block_bitmap block_found_map = 0; ext2fs_block_bitmap block_dup_map = 0; ext2fs_block_bitmap block_illegal_map = 0; -unsigned short * inode_link_info = NULL; +ext2_icount_t inode_link_info = 0; static int process_block(ext2_filsys fs, blk_t *blocknr, - int blockcnt, void *private); + int blockcnt, blk_t ref_blk, + int ref_offset, void *private); static int process_bad_block(ext2_filsys fs, blk_t *block_nr, - int blockcnt, void *private); -static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode, + int blockcnt, blk_t ref_blk, + int ref_offset, void *private); +static void check_blocks(ext2_filsys fs, struct problem_context *pctx, char *block_buf); static void mark_table_blocks(ext2_filsys fs); static void alloc_bad_map(ext2_filsys fs); +static void alloc_bb_map(ext2_filsys fs); static void handle_fs_bad_blocks(ext2_filsys fs); static void process_inodes(ext2_filsys fs, char *block_buf); static int process_inode_cmp(const void *a, const void *b); -static int dir_block_cmp(const void *a, const void *b); static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, void * private); -static char *describe_illegal_block(ext2_filsys fs, blk_t block); +/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */ struct process_block_struct { ino_t ino; @@ -93,9 +102,9 @@ struct process_block_struct { int num_blocks; int last_block; int num_illegal_blocks; - int fix; blk_t previous_block; struct ext2_inode *inode; + struct problem_context *pctx; }; struct process_inode_block { @@ -116,13 +125,6 @@ static struct process_inode_block *inodes_to_process; static int process_inode_count; int process_inode_size = 256; -/* - * For the directory blocks list. - */ -struct dir_block_struct *dir_blocks = 0; -int dir_block_count = 0; -int dir_block_size = 0; - /* * Free all memory allocated by pass1 in preparation for restarting * things. @@ -132,13 +134,19 @@ static void unwind_pass1(ext2_filsys fs) ext2fs_free_inode_bitmap(inode_used_map); inode_used_map = 0; ext2fs_free_inode_bitmap(inode_dir_map); inode_dir_map = 0; ext2fs_free_block_bitmap(block_found_map); block_found_map = 0; - free(inode_link_info); inode_link_info = 0; + ext2fs_free_icount(inode_link_info); inode_link_info = 0; free(inodes_to_process);inodes_to_process = 0; - free(dir_blocks); dir_blocks = 0; - dir_block_size = 0; + ext2fs_free_dblist(fs->dblist); fs->dblist = 0; + free_dir_info(fs); if (block_dup_map) { ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0; } + if (inode_bb_map) { + ext2fs_free_inode_bitmap(inode_bb_map); inode_bb_map = 0; + } + if (inode_bad_map) { + ext2fs_free_inode_bitmap(inode_bad_map); inode_bad_map = 0; + } /* Clear statistic counters */ fs_directory_count = 0; @@ -167,6 +175,7 @@ void pass1(ext2_filsys fs) errcode_t retval; struct resource_track rtrack; unsigned char frag, fsize; + struct problem_context pctx; init_resource_track(&rtrack); @@ -208,19 +217,23 @@ void pass1(ext2_filsys fs) "while allocating block_illegal_map"); fatal_error(0); } - inode_link_info = allocate_memory((fs->super->s_inodes_count + 1) * - sizeof(unsigned short), - "inode link count array"); + retval = ext2fs_create_icount(fs, 0, 0, &inode_link_info); + if (retval) { + com_err("ext2fs_create_icount", retval, + "while creating inode_link_info"); + fatal_error(0); + } inodes_to_process = allocate_memory(process_inode_size * sizeof(struct process_inode_block), "array of inodes to process"); process_inode_count = 0; - dir_block_size = get_num_dirs(fs) * 4; - dir_block_count = 0; - dir_blocks = allocate_memory(sizeof(struct dir_block_struct) * - dir_block_size, - "directory block information"); + retval = ext2fs_init_dblist(fs, 0); + if (retval) { + com_err(program_name, retval, + "while allocating directory block information"); + fatal_error(0); + } mark_table_blocks(fs); block_buf = allocate_memory(fs->blocksize * 3, "block interate buffer"); @@ -234,6 +247,7 @@ void pass1(ext2_filsys fs) com_err(program_name, retval, "while opening inode scan"); fatal_error(0); } + ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0); retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval) { com_err(program_name, retval, "while starting inode scan"); @@ -241,25 +255,36 @@ void pass1(ext2_filsys fs) } stashed_inode = &inode; ext2fs_set_inode_callback(scan, scan_callback, block_buf); + clear_problem_context(&pctx); while (ino) { stashed_ino = ino; - inode_link_info[ino] = inode.i_links_count; + if (inode.i_links_count) + retval = ext2fs_icount_store(inode_link_info, ino, + inode.i_links_count); + if (retval) { + com_err("ext2fs_icount_fetch", retval, + "while adding inode %u", ino); + fatal_error(0); + } + pctx.ino = ino; + pctx.inode = &inode; if (ino == EXT2_BAD_INO) { struct process_block_struct pb; pb.ino = EXT2_BAD_INO; pb.num_blocks = pb.last_block = 0; pb.num_illegal_blocks = 0; - pb.suppress = pb.clear = pb.is_dir = 0; + pb.suppress = 0; pb.clear = 0; pb.is_dir = 0; pb.fragmented = 0; - pb.fix = -1; pb.inode = &inode; - retval = ext2fs_block_iterate(fs, ino, 0, block_buf, - process_bad_block, &pb); + pb.pctx = &pctx; + retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, + process_bad_block, &pb); if (retval) com_err(program_name, retval, "while calling e2fsc_block_interate in pass 1"); ext2fs_mark_inode_bitmap(inode_used_map, ino); + clear_problem_context(&pctx); goto next; } if (ino == EXT2_ROOT_INO) { @@ -269,16 +294,14 @@ void pass1(ext2_filsys fs) * regnerated in pass #3. */ if (!LINUX_S_ISDIR(inode.i_mode)) { - printf("Root inode is not a directory. "); - preenhalt(fs); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_1_ROOT_NO_DIR, &pctx)) { inode.i_dtime = time(0); inode.i_links_count = 0; - inode_link_info[ino] = 0; + ext2fs_icount_store(inode_link_info, + ino, 0); e2fsck_write_inode(fs, ino, &inode, "pass1"); - } else - ext2fs_unmark_valid(fs); + } } /* * If dtime is set, offer to clear it. mke2fs @@ -290,34 +313,30 @@ void pass1(ext2_filsys fs) * as a special case. */ if (inode.i_dtime && inode.i_links_count) { - if (ask("Root inode has dtime set " - "(probably due to old mke2fs). Fix", - 1)) { + if (fix_problem(fs, PR_1_ROOT_DTIME, &pctx)) { inode.i_dtime = 0; e2fsck_write_inode(fs, ino, &inode, "pass1"); - } else - ext2fs_unmark_valid(fs); + } } } if (ino == EXT2_BOOT_LOADER_INO) { ext2fs_mark_inode_bitmap(inode_used_map, ino); - check_blocks(fs, ino, &inode, block_buf); + check_blocks(fs, &pctx, block_buf); goto next; } if ((ino != EXT2_ROOT_INO) && (ino < EXT2_FIRST_INODE(fs->super))) { ext2fs_mark_inode_bitmap(inode_used_map, ino); if (inode.i_mode != 0) { - printf("Reserved inode %lu has bad mode. ", ino); - if (ask("Clear", 1)) { + if (fix_problem(fs, + PR_1_RESERVED_BAD_MODE, &pctx)) { inode.i_mode = 0; e2fsck_write_inode(fs, ino, &inode, "pass1"); - } else - ext2fs_unmark_valid(fs); + } } - check_blocks(fs, ino, &inode, block_buf); + check_blocks(fs, &pctx, block_buf); goto next; } /* @@ -326,14 +345,12 @@ void pass1(ext2_filsys fs) */ if (!inode.i_links_count) { if (!inode.i_dtime && inode.i_mode) { - printf("Deleted inode %lu has zero dtime.\n", - ino); - if (ask("Set dtime", 1)) { + if (fix_problem(fs, + PR_1_ZERO_DTIME, &pctx)) { inode.i_dtime = time(0); e2fsck_write_inode(fs, ino, &inode, "pass1"); - } else - ext2fs_unmark_valid(fs); + } } goto next; } @@ -348,13 +365,10 @@ void pass1(ext2_filsys fs) * */ if (inode.i_dtime) { - printf("Inode %lu is in use, but has dtime set\n", - ino); - if (ask("Clear dtime", 1)) { + if (fix_problem(fs, PR_1_SET_DTIME, &pctx)) { inode.i_dtime = 0; e2fsck_write_inode(fs, ino, &inode, "pass1"); - } else - ext2fs_unmark_valid(fs); + } } ext2fs_mark_inode_bitmap(inode_used_map, ino); @@ -384,7 +398,7 @@ void pass1(ext2_filsys fs) if (LINUX_S_ISDIR(inode.i_mode)) { ext2fs_mark_inode_bitmap(inode_dir_map, ino); - add_dir_info(fs, ino, 0, &inode); + add_dir_info(fs, ino, 0); fs_directory_count++; } else if (LINUX_S_ISREG (inode.i_mode)) fs_regular_count++; @@ -394,8 +408,10 @@ void pass1(ext2_filsys fs) fs_blockdev_count++; else if (LINUX_S_ISLNK (inode.i_mode)) { fs_symlinks_count++; - if (!inode.i_blocks) + if (!inode.i_blocks) { fs_fast_symlinks_count++; + goto next; + } } else if (LINUX_S_ISFIFO (inode.i_mode)) fs_fifo_count++; @@ -419,12 +435,19 @@ void pass1(ext2_filsys fs) inodes_to_process[process_inode_count].inode = inode; process_inode_count++; } else - check_blocks(fs, ino, &inode, block_buf); + check_blocks(fs, &pctx, block_buf); if (process_inode_count >= process_inode_size) process_inodes(fs, block_buf); next: retval = ext2fs_get_next_inode(scan, &ino, &inode); + if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { + if (!inode_bb_map) + alloc_bb_map(fs); + ext2fs_mark_inode_bitmap(inode_bb_map, ino); + ext2fs_mark_inode_bitmap(inode_used_map, ino); + goto next; + } if (retval) { com_err(program_name, retval, "while doing inode scan"); @@ -435,9 +458,6 @@ void pass1(ext2_filsys fs) ext2fs_close_inode_scan(scan); ehandler_operation(0); - qsort(dir_blocks, dir_block_count, sizeof(struct dir_block_struct), - dir_block_cmp); - if (invalid_bitmaps) handle_fs_bad_blocks(fs); @@ -463,7 +483,7 @@ endit: free(block_buf); ext2fs_free_block_bitmap(block_illegal_map); block_illegal_map = 0; - + if (tflag > 1) { printf("Pass 1: "); print_resource_track(&rtrack); @@ -488,30 +508,33 @@ static void process_inodes(ext2_filsys fs, char *block_buf) { int i; struct ext2_inode *old_stashed_inode; - ino_t ino; + ino_t old_stashed_ino; const char *old_operation; char buf[80]; - + struct problem_context pctx; + #if 0 printf("begin process_inodes: "); #endif old_operation = ehandler_operation(0); old_stashed_inode = stashed_inode; + old_stashed_ino = stashed_ino; qsort(inodes_to_process, process_inode_count, sizeof(struct process_inode_block), process_inode_cmp); + clear_problem_context(&pctx); for (i=0; i < process_inode_count; i++) { - stashed_inode = &inodes_to_process[i].inode; - ino = inodes_to_process[i].ino; - stashed_ino = ino; -#if 0 - printf("%u ", ino); -#endif - sprintf(buf, "reading indirect blocks of inode %lu", ino); - ehandler_operation(buf); - check_blocks(fs, ino, stashed_inode, block_buf); + pctx.inode = stashed_inode = &inodes_to_process[i].inode; + pctx.ino = stashed_ino = inodes_to_process[i].ino; +#if 0 + printf("%u ", pctx.ino); +#endif + sprintf(buf, "reading indirect blocks of inode %lu", pctx.ino); + ehandler_operation(buf); + check_blocks(fs, &pctx, block_buf); } stashed_inode = old_stashed_inode; + stashed_ino = old_stashed_ino; process_inode_count = 0; #if 0 printf("end process inodes\n"); @@ -530,22 +553,6 @@ static int process_inode_cmp(const void *a, const void *b) ib_b->inode.i_block[EXT2_IND_BLOCK]); } -static int dir_block_cmp(const void *a, const void *b) -{ - const struct dir_block_struct *db_a = - (const struct dir_block_struct *) a; - const struct dir_block_struct *db_b = - (const struct dir_block_struct *) b; - - if (db_a->blk != db_b->blk) - return (db_a->blk - db_b->blk); - - if (db_a->ino != db_b->ino) - return (db_a->ino - db_b->ino); - - return (db_a->blockcnt - db_b->blockcnt); -} - /* * This procedure will allocate the inode bad map table */ @@ -562,6 +569,22 @@ static void alloc_bad_map(ext2_filsys fs) } } +/* + * This procedure will allocate the inode "bb" (badblock) map table + */ +static void alloc_bb_map(ext2_filsys fs) +{ + errcode_t retval; + + retval = ext2fs_allocate_inode_bitmap(fs, "inode in bad block map", + &inode_bb_map); + if (retval) { + com_err("ext2fs_allocate_inode_bitmap", retval, + "while allocating inode in bad block map"); + fatal_error(0); + } +} + /* * Marks a block as in use, setting the dup_map if it's been set * already. Called by process_block and process_bad_block. @@ -593,27 +616,30 @@ static _INLINE_ void mark_block_used(ext2_filsys fs, blk_t block) * This subroutine is called on each inode to account for all of the * blocks used by that inode. */ -static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode, +static void check_blocks(ext2_filsys fs, struct problem_context *pctx, char *block_buf) { struct process_block_struct pb; errcode_t retval; + ino_t ino = pctx->ino; + struct ext2_inode *inode = pctx->inode; - if (!inode_has_valid_blocks(inode)) + if (!ext2fs_inode_has_valid_blocks(pctx->inode)) return; pb.ino = ino; pb.num_blocks = pb.last_block = 0; pb.num_illegal_blocks = 0; - pb.suppress = pb.clear = 0; + pb.suppress = 0; pb.clear = 0; pb.fragmented = 0; pb.previous_block = 0; - pb.is_dir = LINUX_S_ISDIR(inode->i_mode); - pb.fix = -1; + pb.is_dir = LINUX_S_ISDIR(pctx->inode->i_mode); pb.inode = inode; - retval = ext2fs_block_iterate(fs, ino, - pb.is_dir ? BLOCK_FLAG_HOLE : 0, - block_buf, process_block, &pb); + pb.pctx = pctx; + retval = ext2fs_block_iterate2(fs, ino, + pb.is_dir ? BLOCK_FLAG_HOLE : 0, + block_buf, process_block, &pb); + reset_problem_latch(PR_LATCH_BLOCK); if (retval) com_err(program_name, retval, "while calling ext2fs_block_iterate in check_blocks"); @@ -629,7 +655,7 @@ static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode, fatal_error(0); } inode->i_links_count = 0; - inode_link_info[ino] = 0; + ext2fs_icount_store(inode_link_info, ino, 0); inode->i_dtime = time(0); e2fsck_write_inode(fs, ino, inode, "check_blocks"); ext2fs_unmark_inode_bitmap(inode_dir_map, ino); @@ -643,9 +669,6 @@ static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode, return; } - if (pb.fix > 0) - e2fsck_read_inode(fs, ino, inode, "check_blocks"); - pb.num_blocks *= (fs->blocksize / 512); #if 0 printf("inode %u, i_size = %lu, last_block = %lu, i_blocks=%lu, num_blocks = %lu\n", @@ -653,41 +676,38 @@ static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode, pb.num_blocks); #endif if (!pb.num_blocks && pb.is_dir) { - printf("Inode %lu is a zero length directory. ", ino); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_1_ZERO_LENGTH_DIR, pctx)) { inode->i_links_count = 0; - inode_link_info[ino] = 0; + ext2fs_icount_store(inode_link_info, ino, 0); inode->i_dtime = time(0); e2fsck_write_inode(fs, ino, inode, "check_blocks"); ext2fs_unmark_inode_bitmap(inode_dir_map, ino); ext2fs_unmark_inode_bitmap(inode_used_map, ino); fs_directory_count--; pb.is_dir = 0; - } else - ext2fs_unmark_valid(fs); + } } - if ((pb.is_dir && (inode->i_size != (pb.last_block + 1) * fs->blocksize)) || + if ((pb.is_dir && (inode->i_size != + (pb.last_block + 1) * fs->blocksize)) || (inode->i_size < pb.last_block * fs->blocksize)) { - printf ("%s %lu, incorrect size, %u (counted = %u). ", - pb.is_dir ? "Directory" : "Inode", ino, inode->i_size, - (pb.last_block+1) * fs->blocksize); - if (ask ("Set size to counted", 1)) { - inode->i_size = (pb.last_block+1) * fs->blocksize; + pctx->num = (pb.last_block+1) * fs->blocksize; + if (fix_problem(fs, PR_1_BAD_I_SIZE, pctx)) { + inode->i_size = pctx->num; e2fsck_write_inode(fs, ino, inode, "check_blocks"); - } else - ext2fs_unmark_valid(fs); + } + pctx->num = 0; } if (pb.num_blocks != inode->i_blocks) { - printf ("Inode %lu, i_blocks wrong %u (counted=%u). ", - ino, inode->i_blocks, pb.num_blocks); - if (ask ("Set i_blocks to counted", 1)) { + pctx->num = pb.num_blocks; + if (fix_problem(fs, PR_1_BAD_I_BLOCKS, pctx)) { inode->i_blocks = pb.num_blocks; e2fsck_write_inode(fs, ino, inode, "check_blocks"); - } else - ext2fs_unmark_valid(fs); + } + pctx->num = 0; } } +#if 0 /* * Helper function called by process block when an illegal block is * found. It returns a description about why the block is illegal @@ -737,6 +757,7 @@ static char *describe_illegal_block(ext2_filsys fs, blk_t block) } return(problem); } +#endif /* * This is a helper function for check_blocks(). @@ -744,14 +765,19 @@ static char *describe_illegal_block(ext2_filsys fs, blk_t block) int process_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, + blk_t ref_block, + int ref_offset, void *private) { struct process_block_struct *p; - char *problem; + struct problem_context *pctx; blk_t blk = *block_nr; int ret_code = 0; + int problem = 0; + errcode_t retval; p = (struct process_block_struct *) private; + pctx = p->pctx; if (blk == 0) { if (p->is_dir == 0) { @@ -767,8 +793,10 @@ int process_block(ext2_filsys fs, if (blockcnt < 0) return 0; if (blockcnt * fs->blocksize < p->inode->i_size) { - printf("Hole found in directory inode %lu! " - "(blkcnt=%d)\n", p->ino, blockcnt); +#if 0 + printf("Missing block (#%d) in directory inode %lu!\n", + blockcnt, p->ino); +#endif goto mark_dir; } return 0; @@ -790,43 +818,34 @@ int process_block(ext2_filsys fs, } p->previous_block = blk; - if (blk < fs->super->s_first_data_block || - blk >= fs->super->s_blocks_count || - ext2fs_test_block_bitmap(block_illegal_map, blk)) { - problem = describe_illegal_block(fs, blk); - if (preen) { - printf("Block %u of inode %lu %s\n", blk, p->ino, - problem); - preenhalt(fs); - } - if (p->fix == -1) { - printf("Remove illegal block(s) in inode %lu", p->ino); - p->fix = ask("", 1); - } + blk >= fs->super->s_blocks_count) + problem = PR_1_ILLEGAL_BLOCK_NUM; + else if (ext2fs_test_block_bitmap(block_illegal_map, blk)) + problem = PR_1_BLOCK_OVERLAPS_METADATA; + + if (problem) { p->num_illegal_blocks++; - if (!p->suppress && (p->num_illegal_blocks % 20) == 0) { - printf("Too many illegal blocks in inode %lu.\n", - p->ino); - if (ask("Clear inode", 1)) { + if (!p->suppress && (p->num_illegal_blocks % 12) == 0) { + if (fix_problem(fs, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) { p->clear = 1; return BLOCK_ABORT; } - if (ask("Supress messages", 0)) { + if (ask("Suppress messages", 0)) { p->suppress = 1; + suppress_latch_group(PR_LATCH_BLOCK, 1); } } - if (!p->suppress) - printf("Block #%d (%u) %s. %s\n", blockcnt, blk, - problem, clear_msg[p->fix]); - if (p->fix) { + pctx->blk = blk; + pctx->blkcount = blockcnt; + if (fix_problem(fs, problem, pctx)) { blk = *block_nr = 0; ret_code = BLOCK_CHANGED; goto mark_dir; - } else { - ext2fs_unmark_valid(fs); + } else return 0; - } + pctx->blk = 0; + pctx->blkcount = -1; } mark_block_used(fs, blk); @@ -835,20 +854,13 @@ int process_block(ext2_filsys fs, p->last_block = blockcnt; mark_dir: if (p->is_dir && (blockcnt >= 0)) { - if (dir_block_count >= dir_block_size) { - dir_block_size += 100; - dir_blocks = realloc(dir_blocks, - dir_block_size * - sizeof(struct dir_block_struct)); - if (dir_blocks == 0) - fatal_error("Not enough memory to " - "realloc dir_blocks"); + retval = ext2fs_add_dir_block(fs->dblist, p->ino, + blk, blockcnt); + if (retval) { + com_err(program_name, retval, + "while adding to directory block list"); + fatal_error(0); } - - dir_blocks[dir_block_count].blk = blk; - dir_blocks[dir_block_count].ino = p->ino; - dir_blocks[dir_block_count].blockcnt = blockcnt; - dir_block_count++; } return ret_code; } @@ -886,34 +898,33 @@ static int bad_primary_block(ext2_filsys fs, blk_t *block_nr) int process_bad_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, + blk_t ref_block, + int ref_offset, void *private) { struct process_block_struct *p; blk_t blk = *block_nr; int first_block; int i; - + struct problem_context *pctx; + if (!blk) return 0; + p = (struct process_block_struct *) private; + pctx = p->pctx; + + pctx->blk = blk; + pctx->blkcount = blockcnt; + if ((blk < fs->super->s_first_data_block) || (blk >= fs->super->s_blocks_count)) { - if (preen) { - printf("Illegal block %u in bad block inode\n", blk); - preenhalt(fs); - } - if (p->fix == -1) - p->fix = ask("Remove illegal block(s) in bad block inode", 1); - printf("Illegal block %u in bad block inode. %s\n", blk, - clear_msg[p->fix]); - if (p->fix) { + if (fix_problem(fs, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) { *block_nr = 0; return BLOCK_CHANGED; - } else { - ext2fs_unmark_valid(fs); + } else return 0; - } } if (blockcnt < 0) { @@ -943,6 +954,7 @@ int process_bad_block(ext2_filsys fs, first_block = fs->super->s_first_data_block; for (i = 0; i < fs->group_desc_count; i++ ) { + pctx->group = i; if (blk == first_block) { if (i == 0) { printf("The primary superblock (%u) is " @@ -973,39 +985,29 @@ int process_bad_block(ext2_filsys fs, return 0; } if (blk == fs->group_desc[i].bg_block_bitmap) { - printf("Group %d's block bitmap (%u) is bad. ", - i, blk); - if (ask("Relocate", 1)) { + if (fix_problem(fs, PR_1_BB_BAD_BLOCK, pctx)) { invalid_block_bitmap[i]++; invalid_bitmaps++; - } else - ext2fs_unmark_valid(fs); + } return 0; } if (blk == fs->group_desc[i].bg_inode_bitmap) { - printf("Group %d's inode bitmap (%u) is bad. ", - i, blk); - if (ask("Relocate", 1)) { + if (fix_problem(fs, PR_1_IB_BAD_BLOCK, pctx)) { invalid_inode_bitmap[i]++; invalid_bitmaps++; - } else - ext2fs_unmark_valid(fs); + } return 0; } if ((blk >= fs->group_desc[i].bg_inode_table) && (blk < (fs->group_desc[i].bg_inode_table + fs->inode_blocks_per_group))) { - printf("WARNING: Severe data loss possible!!!!\n"); - printf("Bad block %u in group %d's inode table. ", - blk, i); - if (ask("Relocate", 1)) { - invalid_inode_table[i]++; - invalid_bitmaps++; - } else - ext2fs_unmark_valid(fs); + /* + * If there are bad blocks in the inode table, + * the inode scan code will try to do + * something reasonable automatically. + */ return 0; } - first_block += fs->super->s_blocks_per_group; } /* * If we've gotten to this point, then the only @@ -1114,24 +1116,47 @@ static void mark_table_blocks(ext2_filsys fs) { blk_t block, b; int i,j; + struct problem_context pctx; + + clear_problem_context(&pctx); block = fs->super->s_first_data_block; for (i = 0; i < fs->group_desc_count; i++) { + pctx.group = i; + /* + * Mark the blocks used for the inode table + */ + if (fs->group_desc[i].bg_inode_table) { + for (j = 0, b = fs->group_desc[i].bg_inode_table; + j < fs->inode_blocks_per_group; + j++, b++) { + if (ext2fs_test_block_bitmap(block_found_map, + b)) { + pctx.blk = b; + if (fix_problem(fs, + PR_1_ITABLE_CONFLICT, &pctx)) { + invalid_inode_table[i]++; + invalid_bitmaps++; + } + } else { + ext2fs_mark_block_bitmap(block_found_map, + b); + ext2fs_mark_block_bitmap(block_illegal_map, + b); + } + } + } + /* * Mark block used for the block bitmap */ if (fs->group_desc[i].bg_block_bitmap) { if (ext2fs_test_block_bitmap(block_found_map, fs->group_desc[i].bg_block_bitmap)) { - printf("Group %i's block bitmap at %u " - "conflicts with some other fs block.\n", - i, fs->group_desc[i].bg_block_bitmap); - preenhalt(fs); - if (ask("Relocate", 1)) { + pctx.blk = fs->group_desc[i].bg_block_bitmap; + if (fix_problem(fs, PR_1_BB_CONFLICT, &pctx)) { invalid_block_bitmap[i]++; invalid_bitmaps++; - } else { - ext2fs_unmark_valid(fs); } } else { ext2fs_mark_block_bitmap(block_found_map, @@ -1147,16 +1172,11 @@ static void mark_table_blocks(ext2_filsys fs) if (fs->group_desc[i].bg_inode_bitmap) { if (ext2fs_test_block_bitmap(block_found_map, fs->group_desc[i].bg_inode_bitmap)) { - printf("Group %i's inode bitmap at %u " - "conflicts with some other fs block.\n", - i, fs->group_desc[i].bg_inode_bitmap); - preenhalt(fs); - if (ask("Relocate", 1)) { + pctx.blk = fs->group_desc[i].bg_inode_bitmap; + if (fix_problem(fs, PR_1_IB_CONFLICT, &pctx)) { invalid_inode_bitmap[i]++; invalid_bitmaps++; - } else { - ext2fs_unmark_valid(fs); - } + } } else { ext2fs_mark_block_bitmap(block_found_map, fs->group_desc[i].bg_inode_bitmap); @@ -1165,35 +1185,6 @@ static void mark_table_blocks(ext2_filsys fs) } } - /* - * Mark the blocks used for the inode table - */ - if (fs->group_desc[i].bg_inode_table) { - for (j = 0, b = fs->group_desc[i].bg_inode_table; - j < fs->inode_blocks_per_group; - j++, b++) { - if (ext2fs_test_block_bitmap(block_found_map, - b)) { - printf("Group %i's inode table at %u " - "conflicts with some other " - "fs block.\n", - i, b); - preenhalt(fs); - if (ask("Relocate", 1)) { - invalid_inode_table[i]++; - invalid_bitmaps++; - } else { - ext2fs_unmark_valid(fs); - } - } else { - ext2fs_mark_block_bitmap(block_found_map, - b); - ext2fs_mark_block_bitmap(block_illegal_map, - b); - } - } - } - /* * Mark this group's copy of the superblock */ diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c index b3bc8334..27ca98b8 100644 --- a/e2fsck/pass1b.c +++ b/e2fsck/pass1b.c @@ -18,8 +18,12 @@ * (so that the file gets a fresh copy of the duplicated blocks) or * simply to delete the file. * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * */ @@ -31,6 +35,8 @@ #include #include "e2fsck.h" +#include "problem.h" + /* * This is structure is allocated for each time that a block is * claimed by more than one file. So if a particular block is claimed @@ -75,16 +81,12 @@ struct dup_block { * of multiply-claimed blocks. */ struct dup_inode { - ino_t ino; - time_t mtime; - char *pathname; - int num_dupblocks; - int flags; + ino_t ino, dir; + int num_dupblocks; + struct ext2_inode inode; struct dup_inode *next; }; -#define DUP_INODE_DONT_FREE_PATHNAME 0x1 - static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private); static void delete_file(ext2_filsys fs, struct dup_inode *dp, @@ -125,7 +127,7 @@ void pass1_dupblocks(ext2_filsys fs, char *block_buf) * Time to free all of the accumulated data structures that we * don't need anymore. */ - ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0; + ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0; ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0; for (p = dup_blk; p; p = next_p) { next_p = p->next_block; @@ -136,8 +138,6 @@ void pass1_dupblocks(ext2_filsys fs, char *block_buf) } for (r = dup_ino; r; r = next_r) { next_r = r->next; - if (r->pathname && !(r->flags & DUP_INODE_DONT_FREE_PATHNAME)) - free(r->pathname); free(r); } } @@ -176,7 +176,7 @@ void pass1b(ext2_filsys fs, char *block_buf) stashed_ino = ino; if ((ino != EXT2_BAD_INO) && (!ext2fs_test_inode_bitmap(inode_used_map, ino) || - !inode_has_valid_blocks(&inode))) + !ext2fs_inode_has_valid_blocks(&inode))) goto next; pb.ino = ino; @@ -189,10 +189,9 @@ void pass1b(ext2_filsys fs, char *block_buf) dp = allocate_memory(sizeof(struct dup_inode), "duplicate inode record"); dp->ino = ino; - dp->mtime = inode.i_mtime; + dp->dir = 0; + dp->inode = inode; dp->num_dupblocks = pb.dup_blocks; - dp->pathname = 0; - dp->flags = 0; dp->next = dup_ino; dup_ino = dp; if (ino != EXT2_BAD_INO) @@ -270,113 +269,71 @@ int process_pass1b_block(ext2_filsys fs, return 0; } -/* - * Used by pass1c to name the "special" inodes. They are declared as - * writeable strings to prevent const problems. - */ -#define num_special_inodes 7 -char special_inode_name[num_special_inodes][40] = -{ - "", /* 0 */ - "", /* 1 */ - "/", /* 2 */ - "", /* 3 */ - "", /* 4 */ - "", /* 5 */ - "" /* 6 */ -}; - /* * Pass 1c: Scan directories for inodes with duplicate blocks. This * is used so that we can print pathnames when prompting the user for * what to do. */ -struct process_dir_struct { - ext2_filsys fs; - ino_t dir_ino; +struct search_dir_struct { int count; + ino_t first_inode; }; +static int search_dirent_proc(ino_t dir, int entry, + struct ext2_dir_entry *dirent, + int offset, int blocksize, + char *buf, void *private) +{ + struct search_dir_struct *sd = private; + struct dup_inode *p; + + if (!dirent->inode || (entry < DIRENT_OTHER_FILE) || + !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode)) + return 0; + + for (p = dup_ino; p; p = p->next) { + if ((p->ino >= sd->first_inode) && + (p->ino == dirent->inode)) + break; + } + + if (!p || p->dir) + return 0; + + p->dir = dir; + sd->count--; + + return(sd->count ? 0 : DIRENT_ABORT); +} + + void pass1c(ext2_filsys fs, char *block_buf) { - int i; struct dup_inode *p; - errcode_t retval; - char buf[80]; int inodes_left = dup_inode_count; - int offset, entry; - struct ext2_dir_entry *dirent; + struct search_dir_struct sd; printf("Pass 1C: Scan directories for inodes with dup blocks.\n"); /* * First check to see if any of the inodes with dup blocks is - * the bad block inode or the root inode; handle them as - * special cases. + * a special inode. (Note that the bad block inode isn't + * counted.) */ for (p = dup_ino; p; p = p->next) { - if (p->ino < num_special_inodes) { - p->pathname = special_inode_name[p->ino]; - p->flags |= DUP_INODE_DONT_FREE_PATHNAME; + if ((p->ino < EXT2_FIRST_INODE(fs->super)) && + (p->ino != EXT2_BAD_INO)) inodes_left--; - } } /* * Search through all directories to translate inodes to names * (by searching for the containing directory for that inode.) */ - for (i=0; inodes_left && i < dir_block_count; i++) { - retval = ext2fs_read_dir_block(fs, dir_blocks[i].blk, - block_buf); - entry = offset = 0; - while (offset < fs->blocksize) { - entry++; - dirent = (struct ext2_dir_entry *) - (block_buf + offset); - if (!dirent->inode || - ((dir_blocks[i].blockcnt == 0) && (entry <= 2))) - goto next; - - if (!ext2fs_test_inode_bitmap(inode_dup_map, - dirent->inode)) - goto next; - - for (p = dup_ino; p; p = p->next) { - if (p->ino == dirent->inode) - break; - } - - if (!p || p->pathname) - goto next; - - (void) ext2fs_get_pathname(fs, dir_blocks[i].ino, - p->ino, &p->pathname); - inodes_left--; - - next: - if (dirent->rec_len < 8) - break; - offset += dirent->rec_len; - } - } - - - /* - * If we can't get a name, then put in a generic one. - */ - for (p = dup_ino; p; p = p->next) { - if (!p->pathname) { - sprintf(buf, "", p->ino); - p->pathname = malloc(strlen(buf)+1); - if (!p->pathname) { - fprintf(stderr, "pass1c: couldn't malloc " - "generic pathname\n"); - fatal_error(0); - } - strcpy(p->pathname, buf); - } - } + sd.count = inodes_left; + sd.first_inode = EXT2_FIRST_INODE(fs->super); + ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf, + search_dirent_proc, &sd); } static void pass1d(ext2_filsys fs, char *block_buf) @@ -387,8 +344,8 @@ static void pass1d(ext2_filsys fs, char *block_buf) int shared_len; int i; errcode_t retval; - char *time_str; int file_ok; + struct problem_context pctx; printf("Pass 1D: Reconciling duplicate blocks\n"); read_bitmaps(fs); @@ -436,23 +393,33 @@ static void pass1d(ext2_filsys fs, char *block_buf) } } } - time_str = ctime(&p->mtime); - time_str[24] = 0; - printf("File %s (inode #%lu, mod time %s) \n", - p->pathname, p->ino, time_str); - printf(" has %d duplicate blocks, shared with %d file%s:\n", - p->num_dupblocks, shared_len, - (shared_len>1) ? "s" : ""); + + /* + * Report the inode that we are working on + */ + clear_problem_context(&pctx); + pctx.inode = &p->inode; + pctx.ino = p->ino; + pctx.dir = p->dir; + pctx.blkcount = p->num_dupblocks; + pctx.num = shared_len; + fix_problem(fs, PR_1B_DUP_FILE, &pctx); + pctx.blkcount = 0; + pctx.num = 0; + for (i = 0; i < shared_len; i++) { for (s = dup_ino; s; s = s->next) if (s->ino == shared[i]) break; if (!s) continue; - time_str = ctime(&s->mtime); - time_str[24] = 0; - printf("\t%s (inode #%lu, mod time %s)\n", - s->pathname, s->ino, time_str); + /* + * Report the inode that we are sharing with + */ + pctx.inode = &s->inode; + pctx.ino = s->ino; + pctx.dir = s->dir; + fix_problem(fs, PR_1B_DUP_FILE_LIST, &pctx); } if (file_ok) { printf("Duplicated blocks already reassigned or cloned.\n\n"); diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 91bcc088..0032c852 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -1,8 +1,12 @@ /* * pass2.c --- check directory structure * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * * Pass 2 of e2fsck iterates through all active directory inodes, and * applies to following tests to each directory entry in the directory @@ -39,32 +43,41 @@ #include "et/com_err.h" #include "e2fsck.h" +#include "problem.h" /* * Keeps track of how many times an inode is referenced. */ -unsigned short * inode_count; +ext2_icount_t inode_count = 0; static void deallocate_inode(ext2_filsys fs, ino_t ino, char* block_buf); static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino); static int check_dir_block(ext2_filsys fs, - struct dir_block_struct *dir_blocks_info, - char *buf); + struct ext2_db_entry *dir_blocks_info, + void *private); static int allocate_dir_block(ext2_filsys fs, - struct dir_block_struct *dir_blocks_info, - char *buf); + struct ext2_db_entry *dir_blocks_info, + char *buf, struct problem_context *pctx); static int update_dir_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *private); +struct check_dir_struct { + char *buf; + struct problem_context pctx; +}; + void pass2(ext2_filsys fs) { - int i; char *buf; struct resource_track rtrack; - + struct dir_info *dir; + errcode_t retval; + ino_t size; + struct check_dir_struct cd; + init_resource_track(&rtrack); #ifdef MTRACE @@ -73,17 +86,33 @@ void pass2(ext2_filsys fs) if (!preen) printf("Pass 2: Checking directory structure\n"); - inode_count = allocate_memory((fs->super->s_inodes_count + 1) * - sizeof(unsigned short), - "buffer for inode count"); - + size = ext2fs_get_icount_size(inode_link_info) + 10; + retval = ext2fs_create_icount(fs, EXT2_ICOUNT_OPT_INCREMENT, + size, &inode_count); + if (retval) { + com_err("ext2fs_create_icount", retval, + "while creating inode_count"); + fatal_error(0); + } buf = allocate_memory(fs->blocksize, "directory scan buffer"); - for (i=0; i < dir_block_count; i++) - check_dir_block(fs, &dir_blocks[i], buf); - + /* + * Set up the parent pointer for the root directory, if + * present. (If the root directory is not present, we will + * create it in pass 3.) + */ + dir = get_dir_info(EXT2_ROOT_INO); + if (dir) + dir->parent = EXT2_ROOT_INO; + + cd.buf = buf; + clear_problem_context(&cd.pctx); + + retval = ext2fs_dblist_iterate(fs->dblist, check_dir_block, &cd); + free(buf); - free(dir_blocks); + ext2fs_free_dblist(fs->dblist); + if (inode_bad_map) { ext2fs_free_inode_bitmap(inode_bad_map); inode_bad_map = 0; @@ -100,54 +129,39 @@ void pass2(ext2_filsys fs) */ static int check_dot(ext2_filsys fs, struct ext2_dir_entry *dirent, - ino_t ino) + ino_t ino, struct problem_context *pctx) { struct ext2_dir_entry *nextdir; int status = 0; int created = 0; int new_len; - const char *question = 0; + int problem = 0; - if (!dirent->inode) { - printf("Missing '.' in directory inode %lu.\n", ino); - question = "Fix"; - } else if ((dirent->name_len != 1) || - strncmp(dirent->name, ".", dirent->name_len)) { - char *name = malloc(dirent->name_len + 1); - if (!name) - fatal_error("Couldn't allocate . name"); - strncpy(name, dirent->name, dirent->name_len); - name[dirent->name_len] = '\0'; - printf("First entry in directory inode %lu contains '%s' " - "(inode=%u)\n", ino, name, dirent->inode); - printf("instead of '.'.\n"); - free(name); - question = "Change to be '.'"; - } - if (question) { - if (dirent->rec_len < 12) - fatal_error("Cannot fix, insufficient space to add '.'"); - preenhalt(fs); - if (ask(question, 1)) { + if (!dirent->inode) + problem = PR_2_MISSING_DOT; + else if ((dirent->name_len != 1) || + (dirent->name[0] != '.')) + problem = PR_2_1ST_NOT_DOT; + else if (dirent->name[1] != '\0') + problem = PR_2_DOT_NULL_TERM; + + if (problem) { + if (fix_problem(fs, problem, pctx)) { + if (dirent->rec_len < 12) + dirent->rec_len = 12; dirent->inode = ino; dirent->name_len = 1; dirent->name[0] = '.'; + dirent->name[1] = '\0'; status = 1; created = 1; - } else { - ext2fs_unmark_valid(fs); - return 0; } } if (dirent->inode != ino) { - printf("Bad inode number for '.' in directory inode %lu.\n", - ino); - preenhalt(fs); - if (ask("Fix", 1)) { + if (fix_problem(fs, PR_2_BAD_INODE_DOT, pctx)) { dirent->inode = ino; status = 1; - } else - ext2fs_unmark_valid(fs); + } } if (dirent->rec_len > 12) { new_len = dirent->rec_len - 12; @@ -175,32 +189,23 @@ static int check_dot(ext2_filsys fs, */ static int check_dotdot(ext2_filsys fs, struct ext2_dir_entry *dirent, - struct dir_info *dir) + struct dir_info *dir, struct problem_context *pctx) { - ino_t ino = dir->ino; - const char *question = 0; + int problem = 0; - if (!dirent->inode) { - printf("Missing '..' in directory inode %lu.\n", ino); - question = "Fix"; - } else if ((dirent->name_len != 2) || - strncmp(dirent->name, "..", dirent->name_len)) { - char *name = malloc(dirent->name_len + 1); - if (!name) - fatal_error("Couldn't allocate bad .. name"); - strncpy(name, dirent->name, dirent->name_len); - name[dirent->name_len] = '\0'; - printf("Second entry in directory inode %lu contains '%s' " - "(inode=%u)\n", ino, name, dirent->inode); - printf("instead of '..'.\n"); - free(name); - question = "Change to be '..'"; - } - if (question) { - if (dirent->rec_len < 12) - fatal_error("Cannot fix, insufficient space to add '..'"); - preenhalt(fs); - if (ask(question, 1)) { + if (!dirent->inode) + problem = PR_2_MISSING_DOT_DOT; + else if ((dirent->name_len != 2) || + (dirent->name[0] != '.') || + (dirent->name[1] != '.')) + problem = PR_2_2ND_NOT_DOT_DOT; + else if (dirent->name[2] != '\0') + problem = PR_2_DOT_DOT_NULL_TERM; + + if (problem) { + if (fix_problem(fs, problem, pctx)) { + if (dirent->rec_len < 12) + dirent->rec_len = 12; /* * Note: we don't have the parent inode just * yet, so we will fill it in with the root @@ -210,74 +215,57 @@ static int check_dotdot(ext2_filsys fs, dirent->name_len = 2; dirent->name[0] = '.'; dirent->name[1] = '.'; + dirent->name[2] = '\0'; return 1; - } else - ext2fs_unmark_valid(fs); + } return 0; } dir->dotdot = dirent->inode; return 0; } -static char unknown_pathname[] = "???"; - /* * Check to make sure a directory entry doesn't contain any illegal * characters. */ static int check_name(ext2_filsys fs, struct ext2_dir_entry *dirent, - ino_t dir_ino, - char *name) + ino_t dir_ino, struct problem_context *pctx) { int i; int fixup = -1; - char *pathname; int ret = 0; - errcode_t retval; for ( i = 0; i < dirent->name_len; i++) { if (dirent->name[i] == '/' || dirent->name[i] == '\0') { if (fixup < 0) { - retval = ext2fs_get_pathname(fs, dir_ino, - 0, &pathname); - if (retval) { - com_err(program_name, retval, "while getting pathname in check_name"); - pathname = unknown_pathname; - } - printf ("Bad file name '%s' (contains '/' or " - " null) in directory '%s' (%lu)\n", - name, pathname, dir_ino); - if (pathname != unknown_pathname) - free(pathname); - preenhalt(fs); - fixup = ask("Replace '/' or null by '.'", 1); + fixup = fix_problem(fs, PR_2_BAD_NAME, pctx); } if (fixup) { dirent->name[i] = '.'; ret = 1; - } else - ext2fs_unmark_valid(fs); + } } } return ret; } static int check_dir_block(ext2_filsys fs, - struct dir_block_struct *db, - char *buf) + struct ext2_db_entry *db, + void *private) { struct dir_info *subdir, *dir; struct ext2_dir_entry *dirent; int offset = 0; int dir_modified = 0; errcode_t retval; - char *path1, *path2; - int dot_state, name_len; + int dot_state; blk_t block_nr = db->blk; ino_t ino = db->ino; - char name[EXT2_NAME_LEN+1]; - + __u16 links; + struct check_dir_struct *cd = private; + char *buf = cd->buf; + /* * Make sure the inode is still in use (could have been * deleted in the duplicate/bad blocks pass. @@ -285,8 +273,15 @@ static int check_dir_block(ext2_filsys fs, if (!(ext2fs_test_inode_bitmap(inode_used_map, ino))) return 0; + cd->pctx.ino = ino; + cd->pctx.blk = block_nr; + cd->pctx.blkcount = db->blockcnt; + cd->pctx.ino2 = 0; + cd->pctx.dirent = 0; + cd->pctx.num = 0; + if (db->blk == 0) { - if (allocate_dir_block(fs, db, buf)) + if (allocate_dir_block(fs, db, buf, &cd->pctx)) return 0; block_nr = db->blk; } @@ -310,40 +305,30 @@ static int check_dir_block(ext2_filsys fs, do { dot_state++; dirent = (struct ext2_dir_entry *) (buf + offset); + cd->pctx.dirent = dirent; + cd->pctx.num = offset; if (((offset + dirent->rec_len) > fs->blocksize) || (dirent->rec_len < 8) || + ((dirent->rec_len % 4) != 0) || ((dirent->name_len+8) > dirent->rec_len)) { - printf("Directory inode %lu, block %d, offset %d: directory corrupted\n", - ino, db->blockcnt, offset); - preenhalt(fs); - if (ask("Salvage", 1)) { + if (fix_problem(fs, PR_2_DIR_CORRUPTED, &cd->pctx)) { dirent->rec_len = fs->blocksize - offset; dirent->name_len = 0; dirent->inode = 0; dir_modified++; - } else { - ext2fs_unmark_valid(fs); + } else return DIRENT_ABORT; - } } - name_len = dirent->name_len; if (dirent->name_len > EXT2_NAME_LEN) { - printf("Directory inode %lu, block %d, offset %d: filename too long\n", - ino, db->blockcnt, offset); - preenhalt(fs); - if (ask("Truncate filename", 1)) { + if (fix_problem(fs, PR_2_FILENAME_LONG, &cd->pctx)) { dirent->name_len = EXT2_NAME_LEN; dir_modified++; } - name_len = EXT2_NAME_LEN; } - strncpy(name, dirent->name, name_len); - name[name_len] = '\0'; - if (dot_state == 1) { - if (check_dot(fs, dirent, ino)) + if (check_dot(fs, dirent, ino, &cd->pctx)) dir_modified++; } else if (dot_state == 2) { dir = get_dir_info(ino); @@ -352,30 +337,19 @@ static int check_dir_block(ext2_filsys fs, ino); fatal_error(0); } - if (check_dotdot(fs, dirent, dir)) + if (check_dotdot(fs, dirent, dir, &cd->pctx)) dir_modified++; } else if (dirent->inode == ino) { - retval = ext2fs_get_pathname(fs, ino, 0, &path1); - if (retval) - path1 = unknown_pathname; - printf("Entry '%s' in %s (%lu) is a link to '.' ", - name, path1, ino); - if (path1 != unknown_pathname) - free(path1); - preenhalt(fs); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_2_LINK_DOT, &cd->pctx)) { dirent->inode = 0; dir_modified++; + goto next; } } if (!dirent->inode) goto next; -#if 0 - printf("Entry '%s', name_len %d, rec_len %d, inode %lu... ", - name, dirent->name_len, dirent->rec_len, dirent->inode); -#endif - if (check_name(fs, dirent, ino, name)) + if (check_name(fs, dirent, ino, &cd->pctx)) dir_modified++; /* @@ -384,40 +358,36 @@ static int check_dir_block(ext2_filsys fs, if (((dirent->inode != EXT2_ROOT_INO) && (dirent->inode < EXT2_FIRST_INODE(fs->super))) || (dirent->inode > fs->super->s_inodes_count)) { - retval = ext2fs_get_pathname(fs, ino, 0, &path1); - if (retval) - path1 = unknown_pathname; - printf("Entry '%s' in %s (%lu) has bad inode #: %u.\n", - name, path1, ino, dirent->inode); - if (path1 != unknown_pathname) - free(path1); - preenhalt(fs); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_2_BAD_INO, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; - } else - ext2fs_unmark_valid(fs); + } } /* - * If the inode is unusued, offer to clear it. + * If the inode is unused, offer to clear it. */ if (!(ext2fs_test_inode_bitmap(inode_used_map, dirent->inode))) { - retval = ext2fs_get_pathname(fs, ino, 0, &path1); - if (retval) - path1 = unknown_pathname; - printf("Entry '%s' in %s (%lu) has deleted/unused inode %u.\n", - name, path1, ino, dirent->inode); - if (path1 != unknown_pathname) - free(path1); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_2_UNUSED_INODE, &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; - } else - ext2fs_unmark_valid(fs); + } + } + + /* + * If the inode is in a bad block, offer to clear it. + */ + if (inode_bb_map && + (ext2fs_test_inode_bitmap(inode_bb_map, + dirent->inode))) { + if (fix_problem(fs, PR_2_BB_INODE, &cd->pctx)) { + dirent->inode = 0; + dir_modified++; + goto next; + } } /* @@ -436,6 +406,20 @@ static int check_dir_block(ext2_filsys fs, } } + /* + * Don't allow links to the root directory. We check + * this specially to make sure we catch this error + * case even if the root directory hasn't been created + * yet. + */ + if ((dot_state > 2) && (dirent->inode == EXT2_ROOT_INO)) { + if (fix_problem(fs, PR_2_LINK_ROOT, &cd->pctx)) { + dirent->inode = 0; + dir_modified++; + goto next; + } + } + /* * If this is a directory, then mark its parent in its * dir_info structure. If the parent field is already @@ -453,34 +437,20 @@ static int check_dir_block(ext2_filsys fs, fatal_error(0); } if (subdir->parent) { - retval = ext2fs_get_pathname(fs, ino, - 0, &path1); - if (retval) - path1 = unknown_pathname; - retval = ext2fs_get_pathname(fs, - subdir->parent, - dirent->inode, - &path2); - if (retval) - path2 = unknown_pathname; - printf("Entry '%s' in %s (%lu) is a link to directory %s (%u).\n", - name, path1, ino, path2, - dirent->inode); - if (path1 != unknown_pathname) - free(path1); - if (path2 != unknown_pathname) - free(path2); - if (ask("Clear", 1)) { + cd->pctx.ino2 = subdir->parent; + if (fix_problem(fs, PR_2_LINK_DIR, + &cd->pctx)) { dirent->inode = 0; dir_modified++; goto next; - } else - ext2fs_unmark_valid(fs); - } - subdir->parent = ino; + } + cd->pctx.ino2 = 0; + } else + subdir->parent = ino; } - if (inode_count[dirent->inode]++ > 0) + ext2fs_icount_increment(inode_count, dirent->inode, &links); + if (links > 1) fs_links_count++; fs_total_count++; next: @@ -530,7 +500,7 @@ static void deallocate_inode(ext2_filsys fs, ino_t ino, errcode_t retval; struct ext2_inode inode; - inode_link_info[ino] = 0; + ext2fs_icount_store(inode_link_info, ino, 0); e2fsck_read_inode(fs, ino, &inode, "deallocate_inode"); inode.i_links_count = 0; inode.i_dtime = time(0); @@ -547,7 +517,7 @@ static void deallocate_inode(ext2_filsys fs, ino_t ino, ext2fs_unmark_inode_bitmap(fs->inode_map, ino); ext2fs_mark_ib_dirty(fs); - if (!inode_has_valid_blocks(&inode)) + if (!ext2fs_inode_has_valid_blocks(&inode)) return; ext2fs_mark_bb_dirty(fs); @@ -559,83 +529,34 @@ static void deallocate_inode(ext2_filsys fs, ino_t ino, ino); } -/* - * These two subroutines are used by process_bad_inode; it is used to - * make sure that certain reserved fields are really zero. If not, - * prompt the user if he/she wants us to zeroize them. - */ -static void check_for_zero_u32(ext2_filsys fs, ino_t ino, char *pathname, - const char *name, __u32 *val, - int *modified) -{ - char prompt[80]; - - if (*val) { - printf("%s for inode %lu (%s) is %u, should be zero.\n", - name, ino, pathname, *val); - preenhalt(fs); - sprintf(prompt, "Clear %s", name); - if (ask(prompt, 1)) { - *val = 0; - *modified = 1; - } else - ext2fs_unmark_valid(fs); - } -} - -static void check_for_zero_u8(ext2_filsys fs, ino_t ino, char *pathname, - const char *name, __u8 *val, - int *modified) -{ - char prompt[80]; - - if (*val) { - printf("%s for inode %lu (%s) is %d, should be zero.\n", - name, ino, pathname, *val); - preenhalt(fs); - sprintf(prompt, "Clear %s", name); - if (ask(prompt, 1)) { - *val = 0; - *modified = 1; - } else - ext2fs_unmark_valid(fs); - } -} - - - static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino) { struct ext2_inode inode; - errcode_t retval; int inode_modified = 0; - char *pathname; unsigned char *frag, *fsize; + struct problem_context pctx; e2fsck_read_inode(fs, ino, &inode, "process_bad_inode"); - retval = ext2fs_get_pathname(fs, dir, ino, &pathname); - if (retval) { - com_err("process_bad_inode", retval, - "while getting pathname for inode %d", - ino); - return 0; - } + + clear_problem_context(&pctx); + pctx.ino = ino; + pctx.dir = dir; + pctx.inode = &inode; + if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && !(LINUX_S_ISSOCK(inode.i_mode))) { - printf("Inode %lu (%s) has a bad mode (0%o).\n", - ino, pathname, inode.i_mode); - preenhalt(fs); - if (ask("Clear", 1)) { + if (fix_problem(fs, PR_2_BAD_MODE, &pctx)) { deallocate_inode(fs, ino, 0); - free(pathname); return 1; - } else - ext2fs_unmark_valid(fs); + } + } + if (inode.i_faddr && + fix_problem(fs, PR_2_FADDR_ZERO, &pctx)) { + inode.i_faddr = 0; + inode_modified++; } - check_for_zero_u32(fs, ino, pathname, "i_faddr", &inode.i_faddr, - &inode_modified); switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: @@ -653,18 +574,33 @@ static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino) default: frag = fsize = 0; } - if (frag) - check_for_zero_u8(fs, ino, pathname, "i_frag", frag, - &inode_modified); - if (fsize) - check_for_zero_u8(fs, ino, pathname, "i_fsize", fsize, - &inode_modified); + if (frag && *frag) { + pctx.num = *frag; + if (fix_problem(fs, PR_2_FRAG_ZERO, &pctx)) { + *frag = 0; + inode_modified++; + } + pctx.num = 0; + } + if (fsize && *fsize) { + pctx.num = *fsize; + if (fix_problem(fs, PR_2_FSIZE_ZERO, &pctx)) { + *fsize = 0; + inode_modified++; + } + pctx.num = 0; + } - check_for_zero_u32(fs, ino, pathname, "i_file_acl", &inode.i_file_acl, - &inode_modified); - check_for_zero_u32(fs, ino, pathname, "i_dir_acl", &inode.i_dir_acl, - &inode_modified); - free(pathname); + if (inode.i_file_acl && + fix_problem(fs, PR_2_FILE_ACL_ZERO, &pctx)) { + inode.i_file_acl = 0; + inode_modified++; + } + if (inode.i_dir_acl && + fix_problem(fs, PR_2_DIR_ACL_ZERO, &pctx)) { + inode.i_dir_acl = 0; + inode_modified++; + } if (inode_modified) e2fsck_write_inode(fs, ino, &inode, "process_bad_inode"); return 0; @@ -678,17 +614,15 @@ static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino) * that was zeroed out and now needs to be replaced. */ static int allocate_dir_block(ext2_filsys fs, - struct dir_block_struct *db, - char *buf) + struct ext2_db_entry *db, + char *buf, struct problem_context *pctx) { blk_t blk; char *block; struct ext2_inode inode; errcode_t retval; - printf("Directory inode %lu has a hole at block #%d\n", - db->ino, db->blockcnt); - if (ask("Allocate block", 1) == 0) + if (fix_problem(fs, PR_2_DIRECTORY_HOLE, pctx) == 0) return 1; /* @@ -765,7 +699,7 @@ static int update_dir_block(ext2_filsys fs, int blockcnt, void *private) { - struct dir_block_struct *db = private; + struct ext2_db_entry *db = private; if (db->blockcnt == blockcnt) { *block_nr = db->blk; diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c index a33a84d4..2f97aa4c 100644 --- a/e2fsck/pass3.c +++ b/e2fsck/pass3.c @@ -1,8 +1,12 @@ /* * pass3.c -- pass #3 of e2fsck: Check for directory connectivity * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * * Pass #3 assures that all directories are connected to the * filesystem tree, using the following algorithm: @@ -36,9 +40,11 @@ #include "et/com_err.h" #include "e2fsck.h" +#include "problem.h" -static void check_root(ext2_filsys fs, ino_t root_ino); -static void check_directory(ext2_filsys fs, ino_t dir); +static void check_root(ext2_filsys fs); +static void check_directory(ext2_filsys fs, struct dir_info *dir, + struct problem_context *pctx); static ino_t get_lost_and_found(ext2_filsys fs); static void fix_dotdot(ext2_filsys fs, struct dir_info *dir, ino_t parent); static errcode_t adjust_inode_count(ext2_filsys fs, ino_t ino, int adj); @@ -55,6 +61,8 @@ void pass3(ext2_filsys fs) int i; errcode_t retval; struct resource_track rtrack; + struct problem_context pctx; + struct dir_info *dir; init_resource_track(&rtrack); @@ -88,14 +96,16 @@ void pass3(ext2_filsys fs) print_resource_track(&global_rtrack); } - check_root(fs, EXT2_ROOT_INO); + check_root(fs); ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO); - for (i=1; i <= fs->super->s_inodes_count; i++) { - if (ext2fs_test_inode_bitmap(inode_dir_map, i)) - check_directory(fs, i); + clear_problem_context(&pctx); + for (i=0; (dir = dir_info_iter(&i)) != 0;) { + if (ext2fs_test_inode_bitmap(inode_dir_map, dir->ino)) + check_directory(fs, dir, &pctx); } + free_dir_info(fs); ext2fs_free_inode_bitmap(inode_loop_detect); ext2fs_free_inode_bitmap(inode_done_map); @@ -109,39 +119,26 @@ void pass3(ext2_filsys fs) * This makes sure the root inode is present; if not, we ask if the * user wants us to create it. Not creating it is a fatal error. */ -void check_root(ext2_filsys fs, ino_t root_ino) +static void check_root(ext2_filsys fs) { blk_t blk; errcode_t retval; struct ext2_inode inode; char * block; - struct dir_info *dir; - if (ext2fs_test_inode_bitmap(inode_used_map, root_ino)) { + if (ext2fs_test_inode_bitmap(inode_used_map, EXT2_ROOT_INO)) { /* * If the root inode is a directory, die here. The * user must have answered 'no' in pass1 when we * offered to clear it. */ - if (!(ext2fs_test_inode_bitmap(inode_dir_map, root_ino))) + if (!(ext2fs_test_inode_bitmap(inode_dir_map, EXT2_ROOT_INO))) fatal_error("Root inode not directory"); - - /* - * Set up the parent pointer for the root; this isn't - * done anywhere else, so we do it here. - */ - dir = get_dir_info(root_ino); - dir->parent = root_ino; - return; } - printf("Root inode not allocated. "); - preenhalt(fs); - if (!ask("Reallocate", 1)) { - ext2fs_unmark_valid(fs); + if (!fix_problem(fs, PR_3_NO_ROOT_INODE, 0)) fatal_error("Cannot proceed without a root inode."); - } read_bitmaps(fs); @@ -201,9 +198,9 @@ void check_root(ext2_filsys fs, ino_t root_ino) /* * Miscellaneous bookkeeping... */ - add_dir_info(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, &inode); - inode_count[EXT2_ROOT_INO] = 2; - inode_link_info[EXT2_ROOT_INO] = 2; + add_dir_info(fs, EXT2_ROOT_INO, EXT2_ROOT_INO); + ext2fs_icount_store(inode_count, EXT2_ROOT_INO, 2); + ext2fs_icount_store(inode_link_info, EXT2_ROOT_INO, 2); ext2fs_mark_inode_bitmap(inode_used_map, EXT2_ROOT_INO); ext2fs_mark_inode_bitmap(inode_dir_map, EXT2_ROOT_INO); @@ -219,23 +216,12 @@ void check_root(ext2_filsys fs, ino_t root_ino) * a loop, we treat that as a disconnected directory and offer to * reparent it to lost+found. */ -static void check_directory(ext2_filsys fs, ino_t ino) +static void check_directory(ext2_filsys fs, struct dir_info *dir, + struct problem_context *pctx) { - struct dir_info *dir; - struct dir_info *p; - errcode_t retval; - char *path1, *path2, *path3; - static char unknown[] = "???"; - - dir = get_dir_info(ino); - if (!dir) { - printf("Internal error: couldn't find dir_info for %lu\n", - ino); - fatal_error(0); - } + struct dir_info *p = dir; ext2fs_clear_inode_bitmap(inode_loop_detect); - p = dir; while (p) { /* * If we find a parent which we've already checked, @@ -271,24 +257,15 @@ static void check_directory(ext2_filsys fs, ino_t ino) * If we've reached here, we've hit a detached directory * inode; offer to reconnect it to lost+found. */ - retval = ext2fs_get_pathname(fs, p->ino, 0, &path1); - if (retval) - path1 = unknown; - - printf("Unconnected directory inode %lu (%s)\n", p->ino, path1); - if (path1 != unknown) - free(path1); - preenhalt(fs); - if (ask("Connect to /lost+found", 1)) { + pctx->ino = p->ino; + if (fix_problem(fs, PR_3_UNCONNECTED_DIR, pctx)) { if (reconnect_file(fs, p->ino)) ext2fs_unmark_valid(fs); else { p->parent = lost_and_found; fix_dotdot(fs, p, lost_and_found); } - - } else - ext2fs_unmark_valid(fs); + } /* * Make sure that .. and the parent directory are the same; @@ -296,30 +273,11 @@ static void check_directory(ext2_filsys fs, ino_t ino) */ check_dot_dot: if (dir->parent != dir->dotdot) { - retval = ext2fs_get_pathname(fs, dir->parent, ino, - &path1); - if (retval) - path1 = unknown; - retval = ext2fs_get_pathname(fs, dir->dotdot, 0, &path2); - if (retval) - path2 = unknown; - retval = ext2fs_get_pathname(fs, dir->parent, 0, &path3); - if (retval) - path3 = unknown; - - printf("'..' in %s (%lu) is %s (%lu), should be %s (%lu).\n", - path1, ino, path2, dir->dotdot, - path3, dir->parent); - if (path1 != unknown) - free(path1); - if (path2 != unknown) - free(path2); - if (path3 != unknown) - free(path3); - if (ask("Fix", 1)) + pctx->ino = dir->ino; + pctx->ino2 = dir->dotdot; + pctx->dir = dir->parent; + if (fix_problem(fs, PR_3_BAD_DOT_DOT, pctx)) fix_dotdot(fs, dir, dir->parent); - else - ext2fs_unmark_valid(fs); } } @@ -334,21 +292,17 @@ ino_t get_lost_and_found(ext2_filsys fs) errcode_t retval; struct ext2_inode inode; char * block; - const char * name = "lost+found"; + const char name[] = "lost+found"; - retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino); + retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, + sizeof(name)-1, 0, &ino); if (!retval) return ino; if (retval != ENOENT) printf("Error while trying to find /lost+found: %s", error_message(retval)); - else - printf("/lost+found not found. "); - preenhalt(fs); - if (!ask("Create", 1)) { - ext2fs_unmark_valid(fs); + if (!fix_problem(fs, PR_3_NO_LF_DIR, 0)) return 0; - } /* * Read the inode and block bitmaps in; we'll be messing with @@ -434,10 +388,10 @@ ino_t get_lost_and_found(ext2_filsys fs) /* * Miscellaneous bookkeeping that needs to be kept straight. */ - add_dir_info(fs, ino, EXT2_ROOT_INO, &inode); + add_dir_info(fs, ino, EXT2_ROOT_INO); adjust_inode_count(fs, EXT2_ROOT_INO, +1); - inode_count[ino] = 2; - inode_link_info[ino] = 2; + ext2fs_icount_store(inode_count, ino, 2); + ext2fs_icount_store(inode_link_info, ino, 2); #if 0 printf("/lost+found created; inode #%lu\n", ino); #endif @@ -468,7 +422,7 @@ int reconnect_file(ext2_filsys fs, ino_t inode) sprintf(name, "#%lu", inode); retval = ext2fs_link(fs, lost_and_found, name, inode, 0); if (retval == EXT2_ET_DIR_NO_SPACE) { - if (!ask("No room in /lost+found; expand /lost+found", 1)) + if (!fix_problem(fs, PR_3_EXPAND_LF_DIR, 0)) return 1; retval = expand_directory(fs, lost_and_found); if (retval) { @@ -510,8 +464,14 @@ static errcode_t adjust_inode_count(ext2_filsys fs, ino_t ino, int adj) #endif inode.i_links_count += adj; - inode_count[ino] += adj; - inode_link_info[ino] += adj; + if (adj == 1) { + ext2fs_icount_increment(inode_count, ino, 0); + ext2fs_icount_increment(inode_link_info, ino, 0); + } else { + ext2fs_icount_decrement(inode_count, ino, 0); + ext2fs_icount_decrement(inode_link_info, ino, 0); + } + retval = ext2fs_write_inode(fs, ino, &inode); if (retval) diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c index 9733643a..627b5548 100644 --- a/e2fsck/pass4.c +++ b/e2fsck/pass4.c @@ -1,12 +1,19 @@ /* * pass4.c -- pass #4 of e2fsck: Check reference counts * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. - * + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * Pass 4 frees the following data structures: + * - A bitmap of which inodes are in bad blocks. (inode_bb_map) */ #include "e2fsck.h" +#include "problem.h" /* * This routine is called when an inode is not connected to the @@ -15,19 +22,23 @@ * This subroutine returns 1 then the caller shouldn't bother with the * rest of the pass 4 tests. */ -int disconnect_inode(ext2_filsys fs, ino_t i) +static int disconnect_inode(ext2_filsys fs, ino_t i) { struct ext2_inode inode; + struct problem_context pctx; e2fsck_read_inode(fs, i, &inode, "pass4: disconnect_inode"); + clear_problem_context(&pctx); + pctx.ino = i; + pctx.inode = &inode; + if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) || LINUX_S_ISDIR(inode.i_mode))) { /* * This is a zero-length file; prompt to delete it... */ - printf("Unattached zero-length inode %lu\n", i); - if (ask("Clear", 1)) { - inode_link_info[i] = 0; + if (fix_problem(fs, PR_4_ZERO_LEN_INODE, &pctx)) { + ext2fs_icount_store(inode_link_info, i, 0); inode.i_links_count = 0; inode.i_dtime = time(0); e2fsck_write_inode(fs, i, &inode, @@ -47,9 +58,7 @@ int disconnect_inode(ext2_filsys fs, ino_t i) /* * Prompt to reconnect. */ - printf("Unattached inode %lu\n", i); - preenhalt(fs); - if (ask("Connect to /lost+found", 1)) { + if (fix_problem(fs, PR_4_UNATTACHED_INODE, &pctx)) { if (reconnect_file(fs, i)) ext2fs_unmark_valid(fs); } else { @@ -70,6 +79,8 @@ void pass4(ext2_filsys fs) ino_t i; struct ext2_inode inode; struct resource_track rtrack; + struct problem_context pctx; + __u16 link_count, link_counted; init_resource_track(&rtrack); @@ -79,39 +90,47 @@ void pass4(ext2_filsys fs) if (!preen) printf("Pass 4: Checking reference counts\n"); + clear_problem_context(&pctx); for (i=1; i <= fs->super->s_inodes_count; i++) { if (i == EXT2_BAD_INO || (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super))) continue; - if (!(ext2fs_test_inode_bitmap(inode_used_map, i))) + if (!(ext2fs_test_inode_bitmap(inode_used_map, i)) || + (inode_bb_map && + ext2fs_test_inode_bitmap(inode_bb_map, i))) continue; - if (inode_count[i] == 0) { + ext2fs_icount_fetch(inode_link_info, i, &link_count); + ext2fs_icount_fetch(inode_count, i, &link_counted); + if (link_counted == 0) { if (disconnect_inode(fs, i)) continue; + ext2fs_icount_fetch(inode_link_info, i, &link_count); + ext2fs_icount_fetch(inode_count, i, &link_counted); } - if (inode_count[i] != inode_link_info[i]) { + if (link_counted != link_count) { e2fsck_read_inode(fs, i, &inode, "pass4"); - if (inode_link_info[i] != inode.i_links_count) { + pctx.ino = i; + pctx.inode = &inode; + if (link_count != inode.i_links_count) { printf("WARNING: PROGRAMMING BUG IN E2FSCK!\n"); printf("\tOR SOME BONEHEAD (YOU) IS CHECKING " "A MOUNTED (LIVE) FILESYSTEM.\n"); printf("inode_link_info[%ld] is %u, " "inode.i_links_count is %d. " "They should be the same!\n", - i, inode_link_info[i], + i, link_count, inode.i_links_count); } - printf("Inode %lu has ref count %d, expecting %d.\n", - i, inode.i_links_count, inode_count[i]); - if (ask("Set i_nlinks to count", 1)) { - inode.i_links_count = inode_count[i]; + pctx.num = link_counted; + if (fix_problem(fs, PR_4_BAD_REF_COUNT, &pctx)) { + inode.i_links_count = link_counted; e2fsck_write_inode(fs, i, &inode, "pass4"); - } else - ext2fs_unmark_valid(fs); + } } } - free(inode_link_info); inode_link_info = 0; - free(inode_count); inode_count = 0; + ext2fs_free_icount(inode_link_info); inode_link_info = 0; + ext2fs_free_icount(inode_count); inode_count = 0; + ext2fs_free_inode_bitmap(inode_bb_map); if (tflag > 1) { printf("Pass 4: "); print_resource_track(&rtrack); diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c index 744aee20..def492a9 100644 --- a/e2fsck/pass5.c +++ b/e2fsck/pass5.c @@ -1,8 +1,12 @@ /* * pass5.c --- check block and inode bitmaps against on-disk bitmaps * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * */ diff --git a/e2fsck/problem.c b/e2fsck/problem.c new file mode 100644 index 00000000..14908ad5 --- /dev/null +++ b/e2fsck/problem.c @@ -0,0 +1,462 @@ +/* + * problem.c --- report filesystem problems to the user + * + * Copyright 1996, 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include +#include + +#include "e2fsck.h" + +#include "problem.h" + +#define PROMPT_FIX 0 +#define PROMPT_CLEAR 1 +#define PROMPT_RELOCATE 2 +#define PROMPT_ALLOCATE 3 +#define PROMPT_EXPAND 4 +#define PROMPT_CONNECT 5 +#define PROMPT_CREATE 6 +#define PROMPT_SALVAGE 7 +#define PROMPT_TRUNCATE 8 +#define PROMPT_CLEAR_INODE 9 + +/* + * These are the prompts which are used to ask the user if they want + * to fix a problem. + */ +static const char *prompt[] = { + "Fix", /* 0 */ + "Clear", /* 1 */ + "Relocate", /* 2 */ + "Allocate", /* 3 */ + "Expand", /* 4 */ + "Connect to /lost+found", /* 5 */ + "Create", /* 6 */ + "Salvage", /* 7 */ + "Truncate", /* 8 */ + "Clear inode" /* 9 */ + }; + +/* + * These messages are printed when we are preen mode and we will be + * automatically fixing the problem. + */ +static const char *preen_msg[] = { + "FIXED", /* 0 */ + "CLEARED", /* 1 */ + "RELOCATED", /* 2 */ + "ALLOCATED", /* 3 */ + "EXPANDED", /* 4 */ + "RECONNECTED", /* 5 */ + "CREATED", /* 6 */ + "SALVAGED", /* 7 */ + "TRUNCATED", /* 8 */ + "INODE CLEARED" /* 9 */ +}; + +static struct e2fsck_problem problem_table[] = { + + /* Pre-Pass 1 errors */ + + /* Block bitmap not in group */ + { PR_0_BB_NOT_GROUP, "@b @B for @g %g is not in @g. (@b %b)\n", + PROMPT_RELOCATE, 0 }, + + /* Inode bitmap not in group */ + { PR_0_IB_NOT_GROUP, "@i @B for @g %g is not in @g. (@b %b)\n", + PROMPT_RELOCATE, 0 }, + + /* Inode table not in group */ + { PR_0_ITABLE_NOT_GROUP, + "@i table for @g %g is not in @g. (@b %b)\n" + "WARNING: SEVERE DATA LOSS POSSIBLE.\n", + PROMPT_RELOCATE, 0 }, + + /* Pass 1 errors */ + + /* Root directory is not an inode */ + { PR_1_ROOT_NO_DIR, "@r is not a @d. ", + PROMPT_CLEAR, 0 }, + + /* Root directory has dtime set */ + { PR_1_ROOT_DTIME, + "@r has dtime set (probably due to old mke2fs). ", + PROMPT_FIX, PR_PREEN_OK }, + + /* Reserved inode has bad mode */ + { PR_1_RESERVED_BAD_MODE, + "Reserved @i %i has bad mode. ", + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Deleted inode has zero dtime */ + { PR_1_ZERO_DTIME, + "@D @i %i has zero dtime. ", + PROMPT_FIX, PR_PREEN_OK }, + + /* Inode in use, but dtime set */ + { PR_1_SET_DTIME, + "@i %i is in use, but has dtime set. ", + PROMPT_FIX, PR_PREEN_OK }, + + /* Zero-length directory */ + { PR_1_ZERO_LENGTH_DIR, + "@i %i is a @z @d. ", + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Block bitmap conflicts with some other fs block */ + { PR_1_BB_CONFLICT, + "@g %N's @b @B at %b @C.\n", + PROMPT_RELOCATE, 0 }, + + /* Inode bitmap conflicts with some other fs block */ + { PR_1_IB_CONFLICT, + "@g %N's @i @B at %b @C.\n", + PROMPT_RELOCATE, 0 }, + + /* Inode table conflicts with some other fs block */ + { PR_1_ITABLE_CONFLICT, + "@g %g's @i table at %b @C.\n", + PROMPT_RELOCATE, 0 }, + + /* Block bitmap is on a bad block */ + { PR_1_BB_BAD_BLOCK, + "@g %g's @b @B (%b) is bad. ", + PROMPT_RELOCATE, 0 }, + + /* Inode bitmap is on a bad block */ + { PR_1_IB_BAD_BLOCK, + "@g %g's @i @B (%b) is bad. ", + PROMPT_RELOCATE, 0 }, + + /* Inode has incorrect i_size */ + { PR_1_BAD_I_SIZE, + "@i %i, i_size is %Is, @s %N. ", + PROMPT_FIX, PR_PREEN_OK }, + + /* Inode has incorrect i_blocks */ + { PR_1_BAD_I_BLOCKS, + "@i %i, i_blocks is %Ib, @s %N. ", + PROMPT_FIX, PR_PREEN_OK }, + + /* Illegal block number in inode */ + { PR_1_ILLEGAL_BLOCK_NUM, + "Illegal @b #%B (%b) in @i %i. ", + PROMPT_CLEAR, PR_LATCH_BLOCK }, + + /* Block number overlaps fs metadata */ + { PR_1_BLOCK_OVERLAPS_METADATA, + "@b #%B (%b) overlaps filesystem metadata in @i %i. ", + PROMPT_CLEAR, PR_LATCH_BLOCK }, + + /* Inode has illegal blocks (latch question) */ + { PR_1_INODE_BLOCK_LATCH, + "@i %i has illegal @b(s). ", + PROMPT_CLEAR, 0 }, + + /* Too many bad blocks in inode */ + { PR_1_TOO_MANY_BAD_BLOCKS, + "Too many illegal @bs in @i %i.\n", + PROMPT_CLEAR_INODE, PR_NO_OK }, + + /* Illegal block number in bad block inode */ + { PR_1_BB_ILLEGAL_BLOCK_NUM, + "Illegal @b #%B (%b) in bad @b @i. ", + PROMPT_CLEAR, PR_LATCH_BBLOCK }, + + /* Bad block inode has illegal blocks (latch question) */ + { PR_1_INODE_BBLOCK_LATCH, + "Bad @b @i has illegal @b(s). ", + PROMPT_CLEAR, 0 }, + + /* Pass 1b errors */ + + /* File has duplicate blocks */ + { PR_1B_DUP_FILE, + "File %Q (@i #%i, mod time %IM) \n" + " has %B duplicate @b(s), shared with %N file(s):\n", + PROMPT_FIX, PR_MSG_ONLY }, + + /* List of files sharing duplicate blocks */ + { PR_1B_DUP_FILE_LIST, + "\t%Q (@i #%i, mod time %IM)\n", + PROMPT_FIX, PR_MSG_ONLY }, + + + /* Pass 2 errors */ + + /* Bad inode number for '.' */ + { PR_2_BAD_INODE_DOT, + "Bad @i number for '.' in @d @i %i.\n", + PROMPT_FIX, 0 }, + + /* Directory entry has bad inode number */ + { PR_2_BAD_INO, + "@E has bad @i #: %Di.\n", + PROMPT_CLEAR, 0 }, + + /* Directory entry has deleted or unused inode */ + { PR_2_UNUSED_INODE, + "@E has @D/unused @i %Di. ", + PROMPT_CLEAR, PR_PREEN_OK }, + + /* Directry entry is link to '.' */ + { PR_2_LINK_DOT, + "@E @L to '.' ", + PROMPT_CLEAR, 0 }, + + /* Directory entry points to inode now located in a bad block */ + { PR_2_BB_INODE, + "@E points to @i (%Di) located in a bad @b.\n", + PROMPT_CLEAR, 0 }, + + /* Directory entry contains a link to a directory */ + { PR_2_LINK_DIR, + "@E @L to @d %P (%Di).\n", + PROMPT_CLEAR, 0 }, + + /* Directory entry contains a link to the root directry */ + { PR_2_LINK_ROOT, + "@E @L to the @r.\n", + PROMPT_CLEAR, 0 }, + + /* Directory entry has illegal characters in its name */ + { PR_2_BAD_NAME, + "@E has illegal characters in its name.\n", + PROMPT_FIX, 0 }, + + /* Missing '.' in directory inode */ + { PR_2_MISSING_DOT, + "Missing '.' in @d @i %i.\n", + PROMPT_FIX, 0 }, + + /* Missing '..' in directory inode */ + { PR_2_MISSING_DOT_DOT, + "Missing '..' in @d @i %i.\n", + PROMPT_FIX, 0 }, + + /* First entry in directory inode doesn't contain '.' */ + { PR_2_1ST_NOT_DOT, + "First @e '%Dn' (inode=%Di) in @d @i %i (%p) @s '.'\n", + PROMPT_FIX, 0 }, + + /* Second entry in directory inode doesn't contain '..' */ + { PR_2_2ND_NOT_DOT_DOT, + "Second @e '%Dn' (inode=%Di) in @d @i %i @s '..'\n", + PROMPT_FIX, 0 }, + + /* i_faddr should be zero */ + { PR_2_FADDR_ZERO, + "i_faddr @F %IF, @s zero.\n", + PROMPT_CLEAR, 0 }, + + /* i_file_acl should be zero */ + { PR_2_FILE_ACL_ZERO, + "i_file_acl @F %If, @s zero.\n", + PROMPT_CLEAR, 0 }, + + /* i_dir_acl should be zero */ + { PR_2_DIR_ACL_ZERO, + "i_dir_acl @F %Id, @s zero.\n", + PROMPT_CLEAR, 0 }, + + /* i_frag should be zero */ + { PR_2_FRAG_ZERO, + "i_frag @F %N, @s zero.\n", + PROMPT_CLEAR, 0 }, + + /* i_fsize should be zero */ + { PR_2_FSIZE_ZERO, + "i_fsize @F %N, @s zero.\n", + PROMPT_CLEAR, 0 }, + + /* inode has bad mode */ + { PR_2_BAD_MODE, + "@i %i (%Q) has a bad mode (%Im).\n", + PROMPT_CLEAR, 0 }, + + /* directory corrupted */ + { PR_2_DIR_CORRUPTED, + "@d @i %i, @b %B, offset %N: @d corrupted\n", + PROMPT_SALVAGE, 0 }, + + /* filename too long */ + { PR_2_FILENAME_LONG, + "@d @i %i, @b %B, offset %N: filename too long\n", + PROMPT_TRUNCATE, 0 }, + + /* Directory inode has a missing block (hole) */ + { PR_2_DIRECTORY_HOLE, + "@d @i %i has an unallocated @b #%B. ", + PROMPT_ALLOCATE, 0 }, + + /* '.' is not NULL terminated */ + { PR_2_DOT_NULL_TERM, + "'.' directory entry in @d @i %i is not NULL terminated\n", + PROMPT_FIX, 0 }, + + /* '..' is not NULL terminated */ + { PR_2_DOT_DOT_NULL_TERM, + "'..' directory entry in @d @i %i is not NULL terminated\n", + PROMPT_FIX, 0 }, + + /* Pass 3 errors */ + + /* Root inode not allocated */ + { PR_3_NO_ROOT_INODE, + "@r not allocated. ", + PROMPT_ALLOCATE, 0 }, + + /* No room in lost+found */ + { PR_3_EXPAND_LF_DIR, + "No room in @l @d. ", + PROMPT_EXPAND, 0 }, + + /* Unconnected directory inode */ + { PR_3_UNCONNECTED_DIR, + "Unconnected @d @i %i (%p)\n", + PROMPT_CONNECT, 0 }, + + /* /lost+found not found */ + { PR_3_NO_LF_DIR, + "/@l not found. ", + PROMPT_CREATE, 0 }, + + /* .. entry is incorrect */ + { PR_3_BAD_DOT_DOT, + "'..' in %Q (%i) is %P (%j), @s %q (%d).\n", + PROMPT_FIX, 0 }, + + /* Pass 4 errors */ + + /* Unattached zero-length inode */ + { PR_4_ZERO_LEN_INODE, + "@u @z @i %i. ", + PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK }, + + /* Unattached inode */ + { PR_4_UNATTACHED_INODE, + "@u @i %i\n", + PROMPT_CONNECT, 0 }, + + /* Inode ref count wrong */ + { PR_4_BAD_REF_COUNT, + "@i %i ref count is %Il, @s %N. ", + PROMPT_FIX, PR_PREEN_OK }, + + { 0 } +}; + +/* + * This is the latch flags register. It allows several problems to be + * "latched" together. This means that the user has to answer but one + * question for the set of problems, and all of the associated + * problems will be either fixed or not fixed. + */ +char pr_latch[7]; /* Latch flags register */ +char pr_suppress[7]; /* Latch groups which are suppressed */ +int latch_question[7] = { + PR_1_INODE_BLOCK_LATCH, + PR_1_INODE_BBLOCK_LATCH +}; + +static struct e2fsck_problem *find_problem(int code) +{ + int i; + + for (i=0; problem_table[i].e2p_code; i++) { + if (problem_table[i].e2p_code == code) + return &problem_table[i]; + } + return 0; +} + +void reset_problem_latch(int mask) +{ + pr_latch[PR_LATCH(mask)] = 0; + pr_suppress[PR_LATCH(mask)] = 0; +} + +void suppress_latch_group(int mask, int value) +{ + pr_suppress[PR_LATCH(mask)] = value; +} + +void clear_problem_context(struct problem_context *ctx) +{ + memset(ctx, 0, sizeof(struct problem_context)); + ctx->blkcount = -1; + ctx->group = -1; +} + +int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx) +{ + struct e2fsck_problem *ptr; + int def_yn, answer; + int latch; + int print_answer = 0; + int suppress = 0; + + ptr = find_problem(code); + if (!ptr) { + printf("Unhandled error code (%d)!\n", code); + return 0; + } + def_yn = (ptr->flags & PR_NO_DEFAULT) ? 0 : 1; + + /* + * Do special latch processing. This is where we ask the + * latch question, if it exists + */ + if (ptr->flags & PR_LATCH_MASK) { + latch = PR_LATCH(ptr->flags); + if (latch_question[latch] && !pr_latch[latch]) + pr_latch[latch] = fix_problem(fs, + latch_question[latch], + ctx) + 1; + if (pr_suppress[latch]) + suppress++; + } + + if (!suppress) { + if (preen) + printf("%s: ", device_name); + print_e2fsck_message(fs, ptr->e2p_description, ctx, 1); + } + if (!(ptr->flags & PR_PREEN_OK)) + preenhalt(fs); + + if (ptr->flags & PR_MSG_ONLY) + return 1; + + if (preen) { + answer = def_yn; + print_answer = 1; + } else if (ptr->flags & PR_LATCH_MASK) { + latch = PR_LATCH(ptr->flags); + if (!pr_latch[latch]) + pr_latch[latch] = + ask(prompt[(int) ptr->prompt], def_yn) + 1; + else + print_answer = 1; + answer = pr_latch[latch] - 1; + } else + answer = ask(prompt[(int) ptr->prompt], def_yn); + if (!answer && !(ptr->flags & PR_NO_OK)) + ext2fs_unmark_valid(fs); + + if (print_answer) + printf("%s.\n", + answer ? preen_msg[(int) ptr->prompt] : "IGNORED"); + + return answer; +} diff --git a/e2fsck/problem.h b/e2fsck/problem.h new file mode 100644 index 00000000..91da479a --- /dev/null +++ b/e2fsck/problem.h @@ -0,0 +1,249 @@ +/* + * problem.h --- e2fsck problem error codes + * + * Copyright 1996 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct problem_context { + ino_t ino, ino2, dir; + struct ext2_inode *inode; + struct ext2_dir_entry *dirent; + blk_t blk; + int blkcount, group; + __u32 num; +}; + +struct e2fsck_problem { + int e2p_code; + const char * e2p_description; + char prompt; + short flags; +}; + +#define PR_PREEN_OK 0x0001 /* Don't need to do preenhalt */ +#define PR_NO_OK 0x0002 /* If user answers no, don't make fs invalid */ +#define PR_NO_DEFAULT 0x0004 /* Default to no */ +#define PR_MSG_ONLY 0x0008 /* Print message only */ + +/* + * We define a set of "latch groups"; these are problems which are + * handled as a set. The user answers once for a particular latch + * group. + */ +#define PR_LATCH_MASK 0x0070 /* Latch mask */ +#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */ +#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */ + +#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1) + +/* + * Pre-Pass 1 errors + */ + +/* Block bitmap not in group */ +#define PR_0_BB_NOT_GROUP 0x000001 + +/* Inode bitmap not in group */ +#define PR_0_IB_NOT_GROUP 0x000002 + +/* Inode table not in group */ +#define PR_0_ITABLE_NOT_GROUP 0x000003 + +/* + * Pass 1 errors + */ + +/* Root directory is not an inode */ +#define PR_1_ROOT_NO_DIR 0x010001 + +/* Root directory has dtime set */ +#define PR_1_ROOT_DTIME 0x010002 + +/* Reserved inode has bad mode */ +#define PR_1_RESERVED_BAD_MODE 0x010003 + +/* Deleted inode has zero dtime */ +#define PR_1_ZERO_DTIME 0x010004 + +/* Inode in use, but dtime set */ +#define PR_1_SET_DTIME 0x010005 + +/* Zero-length directory */ +#define PR_1_ZERO_LENGTH_DIR 0x010006 + +/* Block bitmap conflicts with some other fs block */ +#define PR_1_BB_CONFLICT 0x010007 + +/* Inode bitmap conflicts with some other fs block */ +#define PR_1_IB_CONFLICT 0x010008 + +/* Inode table conflicts with some other fs block */ +#define PR_1_ITABLE_CONFLICT 0x010009 + +/* Block bitmap is on a bad block */ +#define PR_1_BB_BAD_BLOCK 0x01000A + +/* Inode bitmap is on a bad block */ +#define PR_1_IB_BAD_BLOCK 0x01000B + +/* Inode has incorrect i_size */ +#define PR_1_BAD_I_SIZE 0x01000C + +/* Inode has incorrect i_blocks */ +#define PR_1_BAD_I_BLOCKS 0x01000D + +/* Illegal block number in inode */ +#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E + +/* Block number overlaps fs metadata */ +#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F + +/* Inode has illegal blocks (latch question) */ +#define PR_1_INODE_BLOCK_LATCH 0x010010 + +/* Too many bad blocks in inode */ +#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011 + +/* Illegal block number in bad block inode */ +#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012 + +/* Bad block inode has illegal blocks (latch question) */ +#define PR_1_INODE_BBLOCK_LATCH 0x010013 + +/* + * Pass 1b errors + */ + +/* File has duplicate blocks */ +#define PR_1B_DUP_FILE 0x011001 + +/* List of files sharing duplicate blocks */ +#define PR_1B_DUP_FILE_LIST 0x011002 + +/* + * Pass 2 errors + */ + +/* Bad inode number for '.' */ +#define PR_2_BAD_INODE_DOT 0x020001 + +/* Directory entry has bad inode number */ +#define PR_2_BAD_INO 0x020002 + +/* Directory entry has deleted or unused inode */ +#define PR_2_UNUSED_INODE 0x020003 + +/* Directry entry is link to '.' */ +#define PR_2_LINK_DOT 0x020004 + +/* Directory entry points to inode now located in a bad block */ +#define PR_2_BB_INODE 0x020005 + +/* Directory entry contains a link to a directory */ +#define PR_2_LINK_DIR 0x020006 + +/* Directory entry contains a link to the root directry */ +#define PR_2_LINK_ROOT 0x020007 + +/* Directory entry has illegal characters in its name */ +#define PR_2_BAD_NAME 0x020008 + +/* Missing '.' in directory inode */ +#define PR_2_MISSING_DOT 0x020009 + +/* Missing '..' in directory inode */ +#define PR_2_MISSING_DOT_DOT 0x02000A + +/* First entry in directory inode doesn't contain '.' */ +#define PR_2_1ST_NOT_DOT 0x02000B + +/* Second entry in directory inode doesn't contain '..' */ +#define PR_2_2ND_NOT_DOT_DOT 0x02000C + +/* i_faddr should be zero */ +#define PR_2_FADDR_ZERO 0x02000D + +/* i_file_acl should be zero */ +#define PR_2_FILE_ACL_ZERO 0x02000E + +/* i_dir_acl should be zero */ +#define PR_2_DIR_ACL_ZERO 0x02000F + +/* i_frag should be zero */ +#define PR_2_FRAG_ZERO 0x020010 + +/* i_fsize should be zero */ +#define PR_2_FSIZE_ZERO 0x020011 + +/* inode has bad mode */ +#define PR_2_BAD_MODE 0x020012 + +/* directory corrupted */ +#define PR_2_DIR_CORRUPTED 0x020013 + +/* filename too long */ +#define PR_2_FILENAME_LONG 0x020014 + +/* Directory inode has a missing block (hole) */ +#define PR_2_DIRECTORY_HOLE 0x020015 + +/* '.' is not NULL terminated */ +#define PR_2_DOT_NULL_TERM 0x020016 + +/* '..' is not NULL terminated */ +#define PR_2_DOT_DOT_NULL_TERM 0x020017 + +/* + * Pass 3 errors + */ + +/* Root inode not allocated */ +#define PR_3_NO_ROOT_INODE 0x030001 + +/* No room in lost+found */ +#define PR_3_EXPAND_LF_DIR 0x030002 + +/* Unconnected directory inode */ +#define PR_3_UNCONNECTED_DIR 0x030003 + +/* /lost+found not found */ +#define PR_3_NO_LF_DIR 0x030004 + +/* .. entry is incorrect */ +#define PR_3_BAD_DOT_DOT 0x030005 + + +/* + * Pass 4 errors + */ + +/* Unattached zero-length inode */ +#define PR_4_ZERO_LEN_INODE 0x040001 + +/* Unattached inode */ +#define PR_4_UNATTACHED_INODE 0x040002 + +/* Inode ref count wrong */ +#define PR_4_BAD_REF_COUNT 0x040003 + +/* + * Pass 5 errors + */ + +/* + * Function declarations + */ +int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx); +void reset_problem_latch(int mask); +void suppress_latch_group(int mask, int value); +void clear_problem_context(struct problem_context *ctx); + +/* message.c */ +void print_e2fsck_message(ext2_filsys fs, const char *msg, + struct problem_context *ctx, int first); + diff --git a/e2fsck/swapfs.c b/e2fsck/swapfs.c index 96f4ae3a..a317cc78 100644 --- a/e2fsck/swapfs.c +++ b/e2fsck/swapfs.c @@ -1,5 +1,13 @@ /* * swapfs.c --- byte-swap an ext2 filesystem + * + * Copyright 1996, 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * */ #ifdef HAVE_ERRNO_H @@ -125,21 +133,29 @@ static void swap_inodes(ext2_filsys fs) fatal_error(0); } inode = (struct ext2_inode *) buf; - for (i=0; i < fs->super->s_inodes_per_group; i++, ino++) { - if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) - ext2fs_swap_inode(fs, inode, inode, 0); + for (i=0; i < fs->super->s_inodes_per_group; + i++, ino++, inode++) { stashed_ino = ino; stashed_inode = inode; - if (inode->i_block[EXT2_IND_BLOCK] || - inode->i_block[EXT2_DIND_BLOCK] || - inode->i_block[EXT2_TIND_BLOCK] || - LINUX_S_ISDIR(inode->i_mode)) + if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) + ext2fs_swap_inode(fs, inode, inode, 0); + + /* + * Skip deleted files. + */ + if (inode->i_links_count == 0) + continue; + + if (LINUX_S_ISDIR(inode->i_mode) || + ((inode->i_block[EXT2_IND_BLOCK] || + inode->i_block[EXT2_DIND_BLOCK] || + inode->i_block[EXT2_TIND_BLOCK]) && + ext2fs_inode_has_valid_blocks(inode))) swap_inode_blocks(fs, ino, block_buf, inode); if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) ext2fs_swap_inode(fs, inode, inode, 1); - inode++; } retval = io_channel_write_blk(fs->io, fs->group_desc[group].bg_inode_table, diff --git a/e2fsck/util.c b/e2fsck/util.c index b099e665..24f77d26 100644 --- a/e2fsck/util.c +++ b/e2fsck/util.c @@ -1,8 +1,12 @@ /* * util.c --- miscellaneous utilities * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -17,7 +21,6 @@ #include const char * fix_msg[2] = { "IGNORED", "FIXED" }; -const char * clear_msg[2] = { "IGNORED", "CLEARED" }; void fatal_error (const char *msg) { @@ -189,8 +192,14 @@ void init_resource_track(struct resource_track *track) #endif } -static __inline__ float timeval_subtract(struct timeval *tv1, - struct timeval *tv2) +#ifdef __GNUC__ +#define _INLINE_ __inline__ +#else +#define _INLINE_ +#endif + +static _INLINE_ float timeval_subtract(struct timeval *tv1, + struct timeval *tv2) { return ((tv1->tv_sec - tv2->tv_sec) + ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000); @@ -245,31 +254,6 @@ extern void e2fsck_write_inode(ext2_filsys fs, unsigned long ino, } } -/* - * This function returns 1 if the inode's block entries actually - * contain block entries. - */ -int inode_has_valid_blocks(struct ext2_inode *inode) -{ - /* - * Only directories, regular files, and some symbolic links - * have valid block entries. - */ - if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && - !LINUX_S_ISLNK(inode->i_mode)) - return 0; - - /* - * If the symbolic link is a "fast symlink", then the symlink - * target is stored in the block entries. - */ - if (LINUX_S_ISLNK (inode->i_mode) && inode->i_blocks == 0 && - inode->i_size < EXT2_N_BLOCKS * sizeof (unsigned long)) - return 0; - - return 1; -} - #ifdef MTRACE void mtrace_print(char *mesg) { diff --git a/e2fsprogs-1.07.lsm b/e2fsprogs-1.07.lsm new file mode 100644 index 00000000..1633e7d0 --- /dev/null +++ b/e2fsprogs-1.07.lsm @@ -0,0 +1,20 @@ +Begin3 +Title: EXT2 Filesystem utilities +Version: 1.07 +Entered-date: 14Mar97 +Description: The filesystem utilities for the EXT2 filesystem, including + e2fsck, mke2fs, dumpe2fs, fsck, and others. +Keywords: utilities, fsck, filesystem, Ext2fs +Author: tytso@mit.edu (Theodore Tso) +Maintained-by: tytso@mit.edu (Theodore Tso) +Primary-site: tsx-11.mit.edu /pub/linux/packages/ext2fs + 620kB e2fsprogs-1.07.tar.gz + 222kB e2fsprogs-1.07-elfbin.tar.gz + 199kB e2fsprogs-1.07-0.i386.rpm + 68kB e2fsprogs-devel-1.07-0.i386.rpm + 622kB e2fsprogs-1.07-0.src.rpm + 1kB e2fsprogs-1.07.lsm +Alternate-site: +Platforms: linux 1.2.x/1.3.x/2.0.x/2.1.x +Copying-policy: GPL +End diff --git a/e2fsprogs-1.06.spec b/e2fsprogs-1.07.spec similarity index 55% rename from e2fsprogs-1.06.spec rename to e2fsprogs-1.07.spec index 31d520de..87bced10 100644 --- a/e2fsprogs-1.06.spec +++ b/e2fsprogs-1.07.spec @@ -1,43 +1,47 @@ -Description: Tools for the second extended (ext2) filesystem +Summary: Tools for the second extended (ext2) filesystem Name: e2fsprogs -Version: 1.06 +Version: 1.07 Release: 0 Copyright: GPL Group: Utilities/System -Source: tsx-11.mit.edu:/pub/linux/packages/ext2fs/e2fsprogs-1.06.tar.gz +Source: tsx-11.mit.edu:/pub/linux/packages/ext2fs/e2fsprogs-1.07.tar.gz +BuildRoot: /tmp/e2fsprogs-root + +%description +This package includes a number of utilities for creating, checking, +and repairing ext2 filesystems. %package devel -Description: e2fs static libs and headers +Summary: e2fs static libs and headers Group: Development/Libraries +%description devel +Libraries and header files needed to develop ext2 filesystem-specific +programs. + %prep %setup %build +CFLAGS="$RPM_OPT_FLAGS" ./configure --enable-elf-shlibs -%ifarch i386 -CFLAGS="$RPM_OPT_FLAGS" ./configure --enable-profile --enable-elf-shlibs -%endif - -%ifarch axp -CFLAGS="$RPM_OPT_FLAGS" ./configure --enable-profile -%endif - -#make -make libs progs +make libs progs docs %install -rm -rf /usr/include/ss /usr/include/et /usr/include/ext2fs export PATH=/sbin:$PATH -make install -make install-libs +make install DESTDIR="$RPM_BUILD_ROOT" +make install-libs DESTDIR="$RPM_BUILD_ROOT" -mv /usr/sbin/debugfs /sbin/debugfs +mv $RPM_BUILD_ROOT/usr/sbin/debugfs $RPM_BUILD_ROOT/sbin/debugfs + +%clean +rm -rf $RPM_BUILD_ROOT -%ifarch i386 %post /sbin/ldconfig -%endif + +%postun +/sbin/ldconfig %files /sbin/e2fsck @@ -53,13 +57,11 @@ mv /usr/sbin/debugfs /sbin/debugfs /usr/sbin/mklost+found /sbin/mkfs.ext2 -%ifarch i386 /lib/libe2p.so.2.2 -/lib/libext2fs.so.2.1 +/lib/libext2fs.so.2.2 /lib/libss.so.2.0 /lib/libcom_err.so.2.0 -/lib/libuuid.so.1.0 -%endif +/lib/libuuid.so.1.1 /usr/bin/chattr /usr/bin/lsattr @@ -73,28 +75,19 @@ mv /usr/sbin/debugfs /sbin/debugfs /usr/man/man1/lsattr.1 %files devel +/usr/info/libext2fs.info* /usr/lib/libe2p.a /usr/lib/libext2fs.a /usr/lib/libss.a /usr/lib/libcom_err.a -/usr/lib/libe2p_p.a -/usr/lib/libext2fs_p.a -/usr/lib/libss_p.a -/usr/lib/libcom_err_p.a /usr/lib/libuuid.a -/usr/lib/libuuid_p.a /usr/include/ss /usr/include/ext2fs /usr/include/et /usr/include/uuid - -%ifarch i386 /usr/lib/libe2p.so /usr/lib/libext2fs.so /usr/lib/libss.so /usr/lib/libcom_err.so /usr/lib/libuuid.so -%post -rm -f /lib/libe2p.so /lib/libext2fs.so /lib/libss.so /lib/libcom_err.so -%endif diff --git a/include/linux/ChangeLog b/include/linux/ChangeLog index eeaa07bf..7656dfdf 100644 --- a/include/linux/ChangeLog +++ b/include/linux/ChangeLog @@ -1,3 +1,11 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Thu Sep 12 15:23:07 1996 Theodore Ts'o * Release of E2fsprogs version 1.05 diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index a443aa27..649f7dec 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -38,19 +38,6 @@ #define EXT2FS_DATE "95/08/09" #define EXT2FS_VERSION "0.5b" -/* - * Debug code - */ -#ifdef EXT2FS_DEBUG -# define ext2_debug(f, a...) { \ - printk ("EXT2-fs DEBUG (%s, %d): %s:", \ - __FILE__, __LINE__, __FUNCTION__); \ - printk (f, ## a); \ - } -#else -# define ext2_debug(f, a...) /**/ -#endif - /* * Special inodes numbers */ diff --git a/install-utils/ChangeLog b/install-utils/ChangeLog index d17f54f6..aeff14f8 100644 --- a/install-utils/ChangeLog +++ b/install-utils/ChangeLog @@ -1,3 +1,11 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Thu Sep 12 15:23:07 1996 Theodore Ts'o * Release of E2fsprogs version 1.05 diff --git a/lib/ChangeLog b/lib/ChangeLog index 2c53f254..aa723a3a 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,16 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Sat Mar 8 13:23:43 1997 Theodore Ts'o + + * Makefile.elf-lib (image): Allow Makefile.in files to specify + a list of libraries which the shared library depends upon. + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Thu Sep 12 15:23:07 1996 Theodore Ts'o * Release of E2fsprogs version 1.05 diff --git a/lib/Makefile.elf-lib b/lib/Makefile.elf-lib index ff19bc91..4804d486 100644 --- a/lib/Makefile.elf-lib +++ b/lib/Makefile.elf-lib @@ -9,7 +9,7 @@ # ELF_IMAGE = libce # ELF_MYDIR = et # ELF_INSTALL_DIR = $(SHLIBDIR) -# +# ELF_OTHER_LIBS = -lc all:: elfshared image @@ -24,7 +24,8 @@ ELF_SONAME = $(ELF_IMAGE).so.$(ELF_SO_VERSION) image: $(ELF_LIB) $(ELF_LIB): $(OBJS) - (cd elfshared; $(CC) --shared -o $(ELF_LIB) -Wl,-soname,$(ELF_SONAME) $(OBJS)) + (cd elfshared; $(CC) --shared -o $(ELF_LIB) -Wl,-soname,$(ELF_SONAME) \ + $(OBJS) $(ELF_OTHER_LIBS)) $(MV) elfshared/$(ELF_LIB) . $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME) $(LN) $(ELF_LIB) ../$(ELF_LIB) diff --git a/lib/e2p/ChangeLog b/lib/e2p/ChangeLog index a9a8f41a..9b18c363 100644 --- a/lib/e2p/ChangeLog +++ b/lib/e2p/ChangeLog @@ -1,3 +1,21 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Thu Jan 2 00:06:29 1997 Theodore Ts'o + + * uuid.c: Include string.h, since we use memcpy(). + +Wed Jan 1 23:47:22 1997 Theodore Ts'o + + * ls.c (list_super): Copy times from the superblock to a time_t + variable. (The alpha has different sizes for time_t and + the time in the superblock.) + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Sun Sep 22 16:20:12 1996 Theodore Ts'o * pf.c: Add support for the 'A' (no atime) flag. diff --git a/lib/e2p/Makefile.in b/lib/e2p/Makefile.in index 4c433dcd..f78c98de 100644 --- a/lib/e2p/Makefile.in +++ b/lib/e2p/Makefile.in @@ -45,6 +45,7 @@ ELF_SO_VERSION = 2 ELF_IMAGE = libe2p ELF_MYDIR = e2p ELF_INSTALL_DIR = $(libdir) +ELF_OTHER_LIBS = -lc BSDLIB_VERSION = 2.1 BSDLIB_IMAGE = libe2p @@ -60,7 +61,7 @@ BSDLIB_INSTALL_DIR = $(libdir) .c.o: $(CC) $(ALL_CFLAGS) -c $< -o $@ -@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -pg -o profiled/$*.o -c $< +@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @DLL_CMT@ (export JUMP_DIR=`pwd`/jump; $(CC) -B$(JUMP_PREFIX) $(ALL_CFLAGS) \ @DLL_CMT@ -o jump/$*.o -c $<) diff --git a/lib/e2p/dll/jump.funcs b/lib/e2p/dll/jump.funcs index 61a69fb6..6173ddc2 100644 --- a/lib/e2p/dll/jump.funcs +++ b/lib/e2p/dll/jump.funcs @@ -1,13 +1,15 @@ -00000000 T _fgetflags libe2p jump/fgetflags -00000000 T _fsetflags libe2p jump/fsetflags -00000000 T _fgetversion libe2p jump/fgetversion -00000000 T _fsetversion libe2p jump/fsetversion -00000000 T _getflags libe2p jump/getflags -00000000 T _getversion libe2p jump/getversion -00000000 T _iterate_on_dir libe2p jump/iod -00000000 T _list_super libe2p jump/ls -00000000 T _print_fs_errors libe2p jump/pe -00000000 T _print_flags libe2p jump/pf -00000000 T _print_fs_state libe2p jump/ps -00000000 T _setflags libe2p jump/setflags -00000000 T _setversion libe2p jump/setversion +00000000 T _fgetflags libe2p fgetflags +00000000 T _fsetflags libe2p fsetflags +00000000 T _fgetversion libe2p fgetversion +00000000 T _fsetversion libe2p fsetversion +00000000 T _getflags libe2p getflags +00000000 T _getversion libe2p getversion +00000000 T _iterate_on_dir libe2p iod +00000000 T _list_super libe2p ls +00000000 T _print_fs_errors libe2p pe +00000000 T _print_flags libe2p pf +00000000 T _print_fs_state libe2p ps +00000000 T _setflags libe2p setflags +00000000 T _setversion libe2p setversion +00000000 T _e2p_is_null_uuid libe2p uuid +00000000 T _e2p_uuid_to_str libe2p uuid diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c index 55e0bbe6..a1320472 100644 --- a/lib/e2p/ls.c +++ b/lib/e2p/ls.c @@ -5,6 +5,8 @@ * Laboratoire MASI, Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * + * Copyright (C) 1995, 1996, 1997 Theodore Ts'o + * * This file can be redistributed under the terms of the GNU Library General * Public License */ @@ -104,13 +106,16 @@ static void print_group (unsigned short gid) #define HOUR_INT (60 * 60) #define MINUTE_INT (60) -static char *interval_string(unsigned int secs) +static const char *interval_string(unsigned int secs) { static char buf[256], tmp[80]; int hr, min, num; buf[0] = 0; + if (secs == 0) + return ""; + if (secs >= MONTH_INT) { num = secs / MONTH_INT; secs -= num*MONTH_INT; @@ -153,6 +158,7 @@ void list_super (struct ext2_super_block * s) struct ext2fs_sb *sb = (struct ext2fs_sb *) s; char buf[80]; const char *os; + time_t tm; inode_blocks_per_group = (((s->s_inodes_per_group * EXT2_INODE_SIZE(s)) + @@ -163,17 +169,20 @@ void list_super (struct ext2_super_block * s) if (sb->s_volume_name[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); - printf("Filesystem volume name: %s\n", buf); - } + } else + strcpy(buf, ""); + printf("Filesystem volume name: %s\n", buf); if (sb->s_last_mounted[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); - printf("Last mounted on: %s\n", buf); - } + } else + strcpy(buf, ""); + printf("Last mounted on: %s\n", buf); if (!e2p_is_null_uuid(sb->s_uuid)) { e2p_uuid_to_str(sb->s_uuid, buf); - printf("Filesystem UUID: %s\n", buf); - } + } else + strcpy(buf, ""); + printf("Filesystem UUID: %s\n", buf); printf ("Filesystem state: "); print_fs_state (stdout, s->s_state); printf ("\n"); @@ -186,38 +195,41 @@ void list_super (struct ext2_super_block * s) case EXT2_OS_MASIX: os = "Masix"; break; default: os = "unknown"; break; } - printf ("Filesystem OS type: %s\n", os); - printf ("Inode count: %u\n", s->s_inodes_count); - printf ("Block count: %u\n", s->s_blocks_count); - printf ("Reserved block count: %u\n", s->s_r_blocks_count); - printf ("Free blocks: %u\n", s->s_free_blocks_count); - printf ("Free inodes: %u\n", s->s_free_inodes_count); - printf ("First block: %u\n", s->s_first_data_block); - printf ("Block size: %u\n", EXT2_BLOCK_SIZE(s)); - printf ("Fragment size: %u\n", EXT2_FRAG_SIZE(s)); - printf ("Blocks per group: %u\n", s->s_blocks_per_group); - printf ("Fragments per group: %u\n", s->s_frags_per_group); - printf ("Inodes per group: %u\n", s->s_inodes_per_group); - printf ("Inode blocks per group: %u\n", inode_blocks_per_group); - printf ("Last mount time: %s", ctime ((time_t *) &s->s_mtime)); - printf ("Last write time: %s", ctime ((time_t *) &s->s_wtime)); - printf ("Mount count: %u\n", s->s_mnt_count); - printf ("Maximum mount count: %d\n", s->s_max_mnt_count); - printf ("Last checked: %s", ctime ((time_t *) &s->s_lastcheck)); - printf ("Check interval: %u (%s)\n", s->s_checkinterval, - interval_string(s->s_checkinterval)); + printf("Filesystem OS type: %s\n", os); + printf("Inode count: %u\n", s->s_inodes_count); + printf("Block count: %u\n", s->s_blocks_count); + printf("Reserved block count: %u\n", s->s_r_blocks_count); + printf("Free blocks: %u\n", s->s_free_blocks_count); + printf("Free inodes: %u\n", s->s_free_inodes_count); + printf("First block: %u\n", s->s_first_data_block); + printf("Block size: %u\n", EXT2_BLOCK_SIZE(s)); + printf("Fragment size: %u\n", EXT2_FRAG_SIZE(s)); + printf("Blocks per group: %u\n", s->s_blocks_per_group); + printf("Fragments per group: %u\n", s->s_frags_per_group); + printf("Inodes per group: %u\n", s->s_inodes_per_group); + printf("Inode blocks per group: %u\n", inode_blocks_per_group); + tm = s->s_mtime; + printf("Last mount time: %s", ctime(&tm)); + tm = s->s_wtime; + printf("Last write time: %s", ctime(&tm)); + printf("Mount count: %u\n", s->s_mnt_count); + printf("Maximum mount count: %d\n", s->s_max_mnt_count); + tm = s->s_lastcheck; + printf("Last checked: %s", ctime(&tm)); + printf("Check interval: %u (%s)\n", s->s_checkinterval, + interval_string(s->s_checkinterval)); if (s->s_checkinterval) { time_t next; next = s->s_lastcheck + s->s_checkinterval; - printf ("Next check after: %s", ctime (&next)); + printf("Next check after: %s", ctime(&next)); } #ifdef EXT2_DEF_RESUID - printf ("Reserved blocks uid: "); - print_user (s->s_def_resuid); - printf ("Reserved blocks gid: "); - print_group (s->s_def_resgid); + printf("Reserved blocks uid: "); + print_user(s->s_def_resuid); + printf("Reserved blocks gid: "); + print_group(s->s_def_resgid); #endif #ifdef EXT2_DYNAMIC_REV if (s->s_rev_level >= EXT2_DYNAMIC_REV) { diff --git a/lib/e2p/uuid.c b/lib/e2p/uuid.c index 82462e83..39082a68 100644 --- a/lib/e2p/uuid.c +++ b/lib/e2p/uuid.c @@ -3,8 +3,11 @@ */ #include +#include #include +#include "e2p.h" + struct uuid { __u32 time_low; __u16 time_mid; diff --git a/lib/et/ChangeLog b/lib/et/ChangeLog index e34d3452..a0f5fbf2 100644 --- a/lib/et/ChangeLog +++ b/lib/et/ChangeLog @@ -1,3 +1,11 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Thu Sep 12 15:23:07 1996 Theodore Ts'o * Release of E2fsprogs version 1.05 diff --git a/lib/et/Makefile.in b/lib/et/Makefile.in index 627ef4d9..a0b05b39 100644 --- a/lib/et/Makefile.in +++ b/lib/et/Makefile.in @@ -36,6 +36,7 @@ ELF_SO_VERSION = 2 ELF_IMAGE = libcom_err ELF_MYDIR = et ELF_INSTALL_DIR = $(libdir) +ELF_OTHER_LIBS = -lc BSDLIB_VERSION = 1.0 BSDLIB_IMAGE = libcom_err @@ -47,7 +48,7 @@ BSDLIB_INSTALL_DIR = $(libdir) # .c.o: $(CC) $(ALL_CFLAGS) -c $< -o $@ -@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -pg -o profiled/$*.o -c $< +@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @DLL_CMT@ (export JUMP_DIR=`pwd`/jump; $(CC) -B$(JUMP_PREFIX) $(ALL_CFLAGS) \ @DLL_CMT@ -o jump/$*.o -c $<) diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog index d8dad462..dd6da6a3 100644 --- a/lib/ext2fs/ChangeLog +++ b/lib/ext2fs/ChangeLog @@ -1,3 +1,154 @@ +Wed Mar 12 13:32:05 1997 Theodore Y. Ts'o + + * Release of E2fsprogs version 1.07 + +Sun Mar 2 16:46:18 1997 Theodore Ts'o + + * Makefile.in (ELF_VERSION): Change version to be 2.2 + +Tue Feb 11 14:54:02 1997 Theodore Ts'o + + * alloc.c (ext2fs_get_free_blocks): Change routine to use + ext2fs_fast_test_block_bitmap_range(). + + * bitops.h (ext2fs_fast_test_block_bitmap_range, + ext2fs_test_block_bitmap_range: New inline functions which + test to see whether a contiguous range of blocks is + available. + +Thu Feb 6 10:00:13 1997 Theodore Ts'o + + * badblocks.c (ext2fs_badblocks_list_create): Rename sybmols to use + use ext2fs_badblocks_* instead of badblocks_* + + * bb_compat.c: New file which translates between old badblocks_*() + names to ext2fs_badblocks_*() + + * unlink.c (ext2fs_unlink): New file, moved ext2fs_unlink() from + link.c (since e2fsck doesn't use ext2fs_unlink()). + + * rs_bitmap.c (ext2fs_resize_generic_bitmap): New file, contains + bitmap resizing routine moved from bitmaps.c, since e2fsck + doesn't need to use this function. + + * lookup.c (ext2fs_lookup): Moved ext2fs_lookup to its own file, + since e2fsck only needs ext2fs_lookup. + +Mon Feb 3 10:11:40 1997 Theodore Ts'o + + * inode.c (ext2fs_open_inode_scan): Set fs->badblocks if it is not + already set; this is needed so that programs like dump + which use the inode scan functions will deal with + filesystems that have bad blocks in the inode table. + +Sun Feb 2 00:17:36 1997 Theodore Ts'o + + * ext2fs.h (struct_badblocks_list, struct_badblocks_iterate): + Moved to ext2fsP.h, since it doesn't need to be part of + the public interface. + + * dir_iterate.c: Move ext2_dir_iterate out of namei.c. + +Sat Feb 1 10:14:55 1997 Theodore Ts'o + + * dblist.c (ext2fs_get_num_dirs): New file, which implements a + directory block list abstraction. (Code moved from + e2fsck). + + * ext2fs.h, inode.c: Moved definition of ext2_struct_inode_scan to + to inode.c (since no one else should be peeking inside it!) + + * valid_blk.c (ext2_inode_has_valid_blocks): New function. + + * openfs.c (ext2fs_open): Check the feature set in the ext2 + superblock, and refuse to open filesystems if they contain + incompatible features. (Can be overriden with the + EXT2_FLAG_FORCE + +Sun Jan 12 11:31:46 1997 Theodore Ts'o + + * block.c (ext2fs_block_iterate2): Added new function + ext2fs_block_iterate2 which changes the function + signature of the callback function to include the + referencing block and offset. + + * inode.c (ext2fs_inode_scan_goto_blockgroup): Added new function + ext2fs_inode_scan_goto_blockgroup which allows an + application to jump to a particular block group while + doing an inode scan. + +Wed Jan 1 23:50:12 1997 Theodore Ts'o + + * dirblock.c: Include string.h, since we use memcpy(). + +Tue Dec 3 12:27:29 1996 Theodore Ts'o + + * getsize.c (ext2fs_get_device_size): The ioctl BLKGETSIZE returns + a long not an int; this doesn't matter on i386 machines, + but it does on Alpha's. + +Fri Nov 29 20:57:37 1996 Theodore Ts'o + + * inode.c (ext2fs_write_inode, ext2fs_read_inode): If the inode + table pointer is NULL, then return an error indicating + that the inode table is missing. + (get_next_blockgroup, get_next_blocks, + ext2fs_get_next_inode): Don't treat a missing inode table + as permanent error. Return MISSING_INODE_TABLE, but as an + advisory error code, much like BAD_BLOCK_IN_INODE_TABLE. + + * rw_bitmaps.c (ext2fs_write_block_bitmap, + ext2fs_write_inode_bitmap): If the inode or block bitmap + block is zero, then don't write out the inode or block + bitmap. The idea here is to avoid stomping on the + superblock. + (read_bitmaps): If the inode or block bitmap block is + zero, then fill in that portion of the inode or block + bitmap with all zeros. + + * inode.c (ext2fs_get_next_inode): Fix bug in handling of bad + blocks in inode table when the inode table size is + non-standard (and can therefore span blocks). + +Tue Oct 29 20:13:14 1996 Theodore Ts'o + + * alloc.c (ext2fs_new_block): Fix fencepost error in + ext2fs_new_block; make sure we don't try to allocate the + first block beyond the end of the filesystem. + +Mon Oct 14 11:00:48 1996 Theodore Ts'o + + * inode.c (check_for_inode_bad_blocks): New function called by + get_next_blocks() to avoid reading in bad blocks marked in + fs->badblocks. Inodes located in bad blocks are returned + by ext2fs_get_next_inode() returns the error code + EXT2_ET_BAD_BLOCK_IN_INODE_TABLE. + + * alloc_tables.c (ext2fs_allocate_tables): New function which + performs the part of mke2fs's job of allocating the + filesystem tables. + + * test_io.c (test_close): IO manager which is used for testing + purposes. + +Sun Oct 13 04:31:57 1996 Theodore Ts'o + + * inode.c (ext2fs_get_next_inode): Separate out the function of + setting up for a new block group to get_next_blockgroup(). + Separate out the function of reading in blocks of the + inode table to get_next_blocks(). + + * ext2fs.h: Add the badblocks list to the ext2_filsys entry + + * badblocks.c (badblocks_list_add, badblocks_list_test): Add + blocks to the badblock list in sorted order. This allows + badblocks_list_test to be coded using a binary search for + speed. + +Tue Oct 8 02:02:03 1996 Theodore Ts'o + + * Release of E2fsprogs version 1.06 + Mon Oct 7 00:44:17 1996 Theodore Ts'o * ext2fs.h, block.c, closefs.c, dirblock.c, inode.c, native.c, diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 5319ce51..2867dce8 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -9,25 +9,34 @@ INSTALL = @INSTALL@ OBJS= ext2_err.o \ alloc.o \ + alloc_tables.o \ badblocks.o \ + bb_compat.o \ bb_inode.o \ bitmaps.o \ bitops.o \ block.o \ + brel_ma.o \ check_desc.o \ closefs.o \ cmp_bitmaps.o \ + dblist.o \ + dblist_dir.o \ dirblock.o \ + dir_iterate.o \ expanddir.o \ freefs.o \ get_pathname.o \ getsize.o \ + icount.o \ initialize.o \ inline.o \ inode.o \ + irel_ma.o \ ismounted.o \ link.o \ llseek.o \ + lookup.o \ mkdir.o \ namei.o \ native.o \ @@ -35,31 +44,44 @@ OBJS= ext2_err.o \ openfs.o \ read_bb.o \ read_bb_file.o \ + rs_bitmap.o \ rw_bitmaps.o \ swapfs.o \ - unix_io.o + test_io.o \ + unix_io.o \ + unlink.o \ + valid_blk.o SRCS= ext2_err.c \ $(srcdir)/alloc.c \ + $(srcdir)/alloc_tables.c \ $(srcdir)/badblocks.c \ + $(srcdir)/bb_compat.c \ $(srcdir)/bb_inode.c \ $(srcdir)/bitmaps.c \ $(srcdir)/bitops.c \ $(srcdir)/block.c \ + $(srcdir)/brel_ma.c \ $(srcdir)/check_desc.c \ $(srcdir)/closefs.c \ $(srcdir)/cmp_bitmaps.c \ + $(srcdir)/dblist.c \ + $(srcdir)/dblist_dir.c \ $(srcdir)/dirblock.c \ + $(srcdir)/dir_iterate.c \ $(srcdir)/expanddir.c \ $(srcdir)/freefs.c \ $(srcdir)/get_pathname.c \ $(srcdir)/getsize.c \ + $(srcdir)/icount.c \ $(srcdir)/initialize.c \ $(srcdir)/inline.c \ $(srcdir)/inode.c \ + $(srcdir)/irel_ma.c \ $(srcdir)/ismounted.c \ $(srcdir)/link.c \ $(srcdir)/llseek.c \ + $(srcdir)/lookup.c \ $(srcdir)/mkdir.c \ $(srcdir)/namei.c \ $(srcdir)/native.c \ @@ -67,9 +89,13 @@ SRCS= ext2_err.c \ $(srcdir)/openfs.c \ $(srcdir)/read_bb.c \ $(srcdir)/read_bb_file.c \ + $(srcdir)/rs_bitmap.c \ $(srcdir)/rw_bitmaps.c \ $(srcdir)/swapfs.c \ - $(srcdir)/unix_io.c + $(srcdir)/test_io.c \ + $(srcdir)/unix_io.c \ + $(srcdir)/unlink.c \ + $(srcdir)/valid_blk.c HFILES= bitops.h ext2fs.h io.h @@ -86,11 +112,12 @@ DLL_LIBS = -L../.. -lcom_err DLL_MYDIR = ext2fs DLL_INSTALL_DIR = $(libdir) -ELF_VERSION = 2.1 +ELF_VERSION = 2.2 ELF_SO_VERSION = 2 ELF_IMAGE = libext2fs ELF_MYDIR = ext2fs ELF_INSTALL_DIR = $(libdir) +ELF_OTHER_LIBS = -lc -L../.. -lcom_err BSDLIB_VERSION = 2.0 BSDLIB_IMAGE = libext2fs @@ -106,7 +133,7 @@ BSDLIB_INSTALL_DIR = $(libdir) .c.o: $(CC) $(ALL_CFLAGS) -c $< -o $@ -@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -pg -o profiled/$*.o -c $< +@PROFILE_CMT@ $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @DLL_CMT@ (export JUMP_DIR=`pwd`/jump; $(CC) -B$(JUMP_PREFIX) $(ALL_CFLAGS) \ @DLL_CMT@ -o jump/$*.o -c $<) @@ -124,6 +151,17 @@ ext2_err.et: $(SUBSTITUTE) $(srcdir)/ext2_err.et.in ext2_err.c ext2_err.h: ext2_err.et $(COMPILE_ET) ext2_err.et +tst_badblocks: tst_badblocks.o badblocks.o + $(CC) -o tst_badblocks tst_badblocks.o badblocks.o $(LIBCOM_ERR) + +tst_iscan: tst_iscan.o inode.o $(STATIC_LIBEXT2FS) + $(CC) -o tst_iscan tst_iscan.o inode.o $(STATIC_LIBEXT2FS) \ + $(LIBCOM_ERR) + +check:: tst_badblocks tst_iscan + ./tst_badblocks + ./tst_iscan + installdirs:: $(top_srcdir)/mkinstalldirs $(DESTDIR)$(ulibdir) \ $(DESTDIR)$(includedir)/ext2fs @@ -157,90 +195,205 @@ distclean:: clean # the Makefile.in file # ext2_err.o: ext2_err.c -alloc.o: $(srcdir)/alloc.c $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h -badblocks.o: $(srcdir)/badblocks.c $(srcdir)/ext2fs.h \ +alloc.o: $(srcdir)/alloc.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +alloc_tables.o: $(srcdir)/alloc_tables.c \ + $(srcdir)/ext2fs.h \ $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h -bb_inode.o: $(srcdir)/bb_inode.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -bitmaps.o: $(srcdir)/bitmaps.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -bitops.o: $(srcdir)/bitops.c $(srcdir)/ext2fs.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +badblocks.o: $(srcdir)/badblocks.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +bb_compat.o: $(srcdir)/bb_compat.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +bb_inode.o: $(srcdir)/bb_inode.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +bitmaps.o: $(srcdir)/bitmaps.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +bitops.o: $(srcdir)/bitops.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +block.o: $(srcdir)/block.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +brel_ma.o: $(srcdir)/brel_ma.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h $(srcdir)/brel.h +check_desc.o: $(srcdir)/check_desc.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +closefs.o: $(srcdir)/closefs.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +cmp_bitmaps.o: $(srcdir)/cmp_bitmaps.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +dblist.o: $(srcdir)/dblist.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +dblist_dir.o: $(srcdir)/dblist_dir.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +dirblock.o: $(srcdir)/dirblock.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +dir_iterate.o: $(srcdir)/dir_iterate.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +expanddir.o: $(srcdir)/expanddir.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +freefs.o: $(srcdir)/freefs.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +get_pathname.o: $(srcdir)/get_pathname.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +getsize.o: $(srcdir)/getsize.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +icount.o: $(srcdir)/icount.c $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/ext2fs.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +initialize.o: $(srcdir)/initialize.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +inline.o: $(srcdir)/inline.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +inode.o: $(srcdir)/inode.c \ + $(srcdir)/ext2fsP.h \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +irel_ma.o: $(srcdir)/irel_ma.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h $(srcdir)/irel.h +ismounted.o: $(srcdir)/ismounted.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +link.o: $(srcdir)/link.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +llseek.o: $(srcdir)/llseek.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h +lookup.o: $(srcdir)/lookup.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +mkdir.o: $(srcdir)/mkdir.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +namei.o: $(srcdir)/namei.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +native.o: $(srcdir)/native.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +newdir.o: $(srcdir)/newdir.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +openfs.o: $(srcdir)/openfs.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +read_bb.o: $(srcdir)/read_bb.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +read_bb_file.o: $(srcdir)/read_bb_file.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +rs_bitmap.o: $(srcdir)/rs_bitmap.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +rw_bitmaps.o: $(srcdir)/rw_bitmaps.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +swapfs.o: $(srcdir)/swapfs.c \ + $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +test_io.o: $(srcdir)/test_io.c \ $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h -block.o: $(srcdir)/block.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -check_desc.o: $(srcdir)/check_desc.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -closefs.o: $(srcdir)/closefs.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -cmp_bitmaps.o: $(srcdir)/cmp_bitmaps.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -dirblock.o: $(srcdir)/dirblock.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -expanddir.o: $(srcdir)/expanddir.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -freefs.o: $(srcdir)/freefs.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -get_pathname.o: $(srcdir)/get_pathname.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -getsize.o: $(srcdir)/getsize.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -initialize.o: $(srcdir)/initialize.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -inline.o: $(srcdir)/inline.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -inode.o: $(srcdir)/inode.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -ismounted.o: $(srcdir)/ismounted.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -link.o: $(srcdir)/link.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -llseek.o: $(srcdir)/llseek.c $(top_srcdir)/lib/et/com_err.h \ - $(srcdir)/io.h -mkdir.o: $(srcdir)/mkdir.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -namei.o: $(srcdir)/namei.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -native.o: $(srcdir)/native.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -newdir.o: $(srcdir)/newdir.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -openfs.o: $(srcdir)/openfs.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -read_bb.o: $(srcdir)/read_bb.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -read_bb_file.o: $(srcdir)/read_bb_file.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -rw_bitmaps.o: $(srcdir)/rw_bitmaps.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -swapfs.o: $(srcdir)/swapfs.c $(srcdir)/ext2fs.h \ - $(top_srcdir)/lib/et/com_err.h $(srcdir)/bitops.h \ - $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h -unix_io.o: $(srcdir)/unix_io.c $(top_srcdir)/lib/et/com_err.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/io.h +unix_io.o: $(srcdir)/unix_io.c \ + $(top_srcdir)/lib/et/com_err.h \ + $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/io.h +unlink.o: $(srcdir)/unlink.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h +valid_blk.o: $(srcdir)/valid_blk.c \ + $(srcdir)/ext2fs.h \ + $(top_srcdir)/lib/et/com_err.h \ + $(srcdir)/io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ + $(srcdir)/bitops.h diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c index c0488795..0bc637db 100644 --- a/lib/ext2fs/alloc.c +++ b/lib/ext2fs/alloc.c @@ -1,8 +1,13 @@ /* * alloc.c --- allocate new inodes, blocks for ext2fs * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * */ #include @@ -84,26 +89,12 @@ errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, return 0; } i++; - if (i > fs->super->s_blocks_count) + if (i >= fs->super->s_blocks_count) i = fs->super->s_first_data_block; } while (i != goal); return ENOSPC; } -static int check_blocks_free(ext2_filsys fs, ext2fs_block_bitmap map, - blk_t blk, int num) -{ - int i; - - for (i=0; i < num; i++) { - if ((blk+i) > fs->super->s_blocks_count) - return 0; - if (ext2fs_test_block_bitmap(map, blk+i)) - return 0; - } - return 1; -} - errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, int num, ext2fs_block_bitmap map, blk_t *ret) { @@ -122,13 +113,13 @@ errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, if (!num) num = 1; do { - if (check_blocks_free(fs, map, b, num)) { + if (b+num-1 > fs->super->s_blocks_count) + b = fs->super->s_first_data_block; + if (ext2fs_fast_test_block_bitmap_range(map, b, num)) { *ret = b; return 0; } b++; - if (b > fs->super->s_blocks_count) - b = fs->super->s_first_data_block; } while (b != finish); return ENOSPC; } diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c new file mode 100644 index 00000000..97bceefe --- /dev/null +++ b/lib/ext2fs/alloc_tables.c @@ -0,0 +1,80 @@ +/* + * alloc_tables.c --- Allocate tables for a newly initialized + * filesystem. Used by mke2fs when initializing a filesystem + * + * Copyright (C) 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_ERRNO_H +#include +#endif + +#include + +#include "ext2fs.h" + +errcode_t ext2fs_allocate_tables(ext2_filsys fs) +{ + errcode_t retval; + blk_t group_blk, last_blk, new_blk, blk; + int i, j; + + group_blk = fs->super->s_first_data_block; + for (i = 0; i < fs->group_desc_count; i++) { + last_blk = group_blk + fs->super->s_blocks_per_group; + + /* + * Allocate the block bitmap + */ + retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, + 1, fs->block_map, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap(fs->block_map, new_blk); + fs->group_desc[i].bg_block_bitmap = new_blk; + + /* + * ... and the inode bitmap + */ + retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, + 1, fs->block_map, &new_blk); + if (retval) + return retval; + ext2fs_mark_block_bitmap(fs->block_map, new_blk); + fs->group_desc[i].bg_inode_bitmap = new_blk; + + /* + * Finally, allocate the inode table + */ + retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, + fs->inode_blocks_per_group, + fs->block_map, &new_blk); + if (retval) + return retval; + for (j=0, blk = new_blk; + j < fs->inode_blocks_per_group; + j++, blk++) + ext2fs_mark_block_bitmap(fs->block_map, blk); + fs->group_desc[i].bg_inode_table = new_blk; + + /* + * Increment the start of the block group + */ + group_blk += fs->super->s_blocks_per_group; + } + return 0; +} + diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c index f286747b..722af8d0 100644 --- a/lib/ext2fs/badblocks.c +++ b/lib/ext2fs/badblocks.c @@ -1,8 +1,12 @@ /* * badblocks.c --- routines to manipulate the bad block structure * - * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -19,19 +23,19 @@ #include -#include "ext2fs.h" +#include "ext2fsP.h" /* * This procedure create an empty badblocks list. */ -errcode_t badblocks_list_create(badblocks_list *ret, int size) +errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) { - badblocks_list bb; + ext2_badblocks_list bb; - bb = malloc(sizeof(struct struct_badblocks_list)); + bb = malloc(sizeof(struct ext2_struct_badblocks_list)); if (!bb) return ENOMEM; - memset(bb, 0, sizeof(struct struct_badblocks_list)); + memset(bb, 0, sizeof(struct ext2_struct_badblocks_list)); bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; bb->size = size ? size : 10; bb->list = malloc(bb->size * sizeof(blk_t)); @@ -45,42 +49,42 @@ errcode_t badblocks_list_create(badblocks_list *ret, int size) /* * This procedure frees a badblocks list. + * + * (note: moved to closefs.c) */ -void badblocks_list_free(badblocks_list bb) -{ - if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) - return; - if (bb->list) - free(bb->list); - bb->list = 0; - free(bb); -} /* * This procedure adds a block to a badblocks list. */ -errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) +errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) { - int i; + int i, j; + blk_t *new_list; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); - for (i=0; i < bb->num; i++) - if (bb->list[i] == blk) - return 0; - if (bb->num >= bb->size) { bb->size += 10; - bb->list = realloc(bb->list, bb->size * sizeof(blk_t)); - if (!bb->list) { - bb->size = 0; - bb->num = 0; + new_list = realloc(bb->list, bb->size * sizeof(blk_t)); + if (!new_list) return ENOMEM; - } + bb->list = new_list; } - bb->list[bb->num++] = blk; + j = bb->num; + for (i=0; i < bb->num; i++) { + if (bb->list[i] == blk) + return 0; + if (bb->list[i] > blk) { + j = i; + break; + } + } + for (i=bb->num; i > j; i--) + bb->list[i] = bb->list[i-1]; + bb->list[j] = blk; + bb->num++; return 0; } @@ -88,27 +92,42 @@ errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) * This procedure tests to see if a particular block is on a badblocks * list. */ -int badblocks_list_test(badblocks_list bb, blk_t blk) +int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) { - int i; + int low, high, mid; - EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return 0; - for (i=0; i < bb->num; i++) - if (bb->list[i] == blk) + low = 0; + high = bb->num-1; + if (blk == bb->list[low]) + return 1; + if (blk == bb->list[high]) + return 1; + + while (low < high) { + mid = (low+high)/2; + if (mid == low || mid == high) + break; + if (blk == bb->list[mid]) return 1; - + if (blk < bb->list[mid]) + high = mid; + else + low = mid; + } return 0; } -errcode_t badblocks_list_iterate_begin(badblocks_list bb, - badblocks_iterate *ret) +errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, + ext2_badblocks_iterate *ret) { - badblocks_iterate iter; + ext2_badblocks_iterate iter; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); - iter = malloc(sizeof(struct struct_badblocks_iterate)); + iter = malloc(sizeof(struct ext2_struct_badblocks_iterate)); if (!iter) return ENOMEM; @@ -119,9 +138,9 @@ errcode_t badblocks_list_iterate_begin(badblocks_list bb, return 0; } -int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) +int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) { - badblocks_list bb; + ext2_badblocks_list bb; if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) return 0; @@ -139,7 +158,7 @@ int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) return 0; } -void badblocks_list_iterate_end(badblocks_iterate iter) +void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) { if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) return; diff --git a/lib/ext2fs/bb_compat.c b/lib/ext2fs/bb_compat.c new file mode 100644 index 00000000..38aeb8aa --- /dev/null +++ b/lib/ext2fs/bb_compat.c @@ -0,0 +1,62 @@ +/* + * bb_compat.c --- compatibility badblocks routines + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_ERRNO_H +#include +#endif + +#include + +#include "ext2fsP.h" + +errcode_t badblocks_list_create(badblocks_list *ret, int size) +{ + return ext2fs_badblocks_list_create(ret, size); +} + +void badblocks_list_free(badblocks_list bb) +{ + ext2fs_badblocks_list_free(bb); +} + +errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_add(bb, blk); +} + +int badblocks_list_test(badblocks_list bb, blk_t blk) +{ + return ext2fs_badblocks_list_test(bb, blk); +} + +errcode_t badblocks_list_iterate_begin(badblocks_list bb, + badblocks_iterate *ret) +{ + return ext2fs_badblocks_list_iterate_begin(bb, ret); +} + +int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) +{ + return ext2fs_badblocks_list_iterate(iter, blk); +} + +void badblocks_list_iterate_end(badblocks_iterate iter) +{ + ext2fs_badblocks_list_iterate_end(iter); +} diff --git a/lib/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c index c8dfeba1..a5dd3a96 100644 --- a/lib/ext2fs/bb_inode.c +++ b/lib/ext2fs/bb_inode.c @@ -5,8 +5,12 @@ * this routine returns an error, the bad block inode may be in an * inconsistent state. * - * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1994, 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -26,7 +30,7 @@ #include "ext2fs.h" struct set_badblock_record { - badblocks_iterate bb_iter; + ext2_badblocks_iterate bb_iter; int bad_block_count; blk_t *ind_blocks; int max_ind_blocks; @@ -37,15 +41,16 @@ struct set_badblock_record { }; static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, - void *private); + blk_t ref_block, int ref_offset, void *private); static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, - void *private); + blk_t ref_block, int ref_offset, + void *private); /* * Given a bad blocks bitmap, update the bad blocks inode to reflect * the map. */ -errcode_t ext2fs_update_bb_inode(ext2_filsys fs, badblocks_list bb_list) +errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) { errcode_t retval; struct set_badblock_record rec; @@ -75,9 +80,9 @@ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, badblocks_list bb_list) /* * First clear the old bad blocks (while saving the indirect blocks) */ - retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, - BLOCK_FLAG_DEPTH_TRAVERSE, 0, - clear_bad_block_proc, &rec); + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_DEPTH_TRAVERSE, 0, + clear_bad_block_proc, &rec); if (retval) goto cleanup; if (rec.err) { @@ -93,22 +98,24 @@ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, badblocks_list bb_list) * block inode (!). */ if (bb_list) { - retval = badblocks_list_iterate_begin(bb_list, &rec.bb_iter); + retval = ext2fs_badblocks_list_iterate_begin(bb_list, + &rec.bb_iter); if (retval) goto cleanup; - while (badblocks_list_iterate(rec.bb_iter, &blk)) { + while (ext2fs_badblocks_list_iterate(rec.bb_iter, &blk)) { ext2fs_mark_block_bitmap(fs->block_map, blk); } - badblocks_list_iterate_end(rec.bb_iter); + ext2fs_badblocks_list_iterate_end(rec.bb_iter); ext2fs_mark_bb_dirty(fs); - retval = badblocks_list_iterate_begin(bb_list, &rec.bb_iter); + retval = ext2fs_badblocks_list_iterate_begin(bb_list, + &rec.bb_iter); if (retval) goto cleanup; - retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, - BLOCK_FLAG_APPEND, 0, - set_bad_block_proc, &rec); - badblocks_list_iterate_end(rec.bb_iter); + retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, + BLOCK_FLAG_APPEND, 0, + set_bad_block_proc, &rec); + ext2fs_badblocks_list_iterate_end(rec.bb_iter); if (retval) goto cleanup; if (rec.err) { @@ -148,7 +155,7 @@ cleanup: * indirect blocks. */ static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, - void *private) + blk_t ref_block, int ref_offset, void *private) { struct set_badblock_record *rec = (struct set_badblock_record *) private; @@ -201,7 +208,8 @@ static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt, * Set the block list in the bad block inode, using the supplied bitmap. */ static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, - int blockcnt, void *private) + int blockcnt, blk_t ref_block, + int ref_offset, void *private) { struct set_badblock_record *rec = (struct set_badblock_record *) private; @@ -213,7 +221,7 @@ static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, /* * Get the next bad block. */ - if (!badblocks_list_iterate(rec->bb_iter, &blk)) + if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) return BLOCK_ABORT; rec->bad_block_count++; } else { diff --git a/lib/ext2fs/bitmaps.c b/lib/ext2fs/bitmaps.c index 7250c800..3ef5666c 100644 --- a/lib/ext2fs/bitmaps.c +++ b/lib/ext2fs/bitmaps.c @@ -2,8 +2,12 @@ * bitmaps.c --- routines to read, write, and manipulate the inode and * block bitmaps. * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -166,4 +170,3 @@ void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) memset(bitmap->bitmap, 0, ((bitmap->real_end - bitmap->start) / 8) + 1); } - diff --git a/lib/ext2fs/bitops.c b/lib/ext2fs/bitops.c index 6f256e5c..8ae02427 100644 --- a/lib/ext2fs/bitops.c +++ b/lib/ext2fs/bitops.c @@ -2,10 +2,12 @@ * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined * routines. * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. - * - * Taken from , Copyright 1992, Linus Torvalds. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h index e967c876..24bd5a74 100644 --- a/lib/ext2fs/bitops.h +++ b/lib/ext2fs/bitops.h @@ -2,10 +2,15 @@ * bitops.h --- Bitmap frobbing code. The byte swapping routines are * also included here. * - * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. This file may be - * redistributed under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% * - * Taken from , Copyright 1992, Linus Torvalds. + * i386 bitops operations taken from , Copyright 1992, + * Linus Torvalds. */ @@ -54,9 +59,22 @@ extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ino_t inode); extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); -extern blk_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); +extern ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); -extern blk_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); +extern ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); + +extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); +extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num); /* * The inline routines themselves... @@ -305,7 +323,14 @@ _INLINE_ __u32 ext2fs_swab32(__u32 val) #endif /* !_EXT2_HAVE_ASM_SWAB */ _INLINE_ void ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, - __u32 bitno) + __u32 bitno); +_INLINE_ void ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); +_INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, + blk_t bitno); + +_INLINE_ void ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, + __u32 bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); @@ -315,7 +340,7 @@ _INLINE_ void ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, } _INLINE_ void ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, - blk_t bitno) + blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); @@ -325,7 +350,7 @@ _INLINE_ void ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, } _INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, - blk_t bitno) + blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); @@ -455,7 +480,7 @@ _INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) return bitmap->start; } -_INLINE_ blk_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) +_INLINE_ ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) { return bitmap->start; } @@ -465,11 +490,107 @@ _INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) return bitmap->end; } -_INLINE_ blk_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) +_INLINE_ ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) { return bitmap->end; } +_INLINE_ int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, + block, bitmap->description); + return 0; + } + for (i=0; i < num; i++) { + if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) + return 0; + } + return 1; +} + +_INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + +#ifdef EXT2FS_DEBUG_FAST_OPS + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, + block, bitmap->description); + return 0; + } +#endif + for (i=0; i < num; i++) { + if (ext2fs_fast_test_block_bitmap(bitmap, block+i)) + return 0; + } + return 1; +} + +_INLINE_ void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +_INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + +#ifdef EXT2FS_DEBUG_FAST_OPS + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, + bitmap->description); + return; + } +#endif + for (i=0; i < num; i++) + ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap); +} + +_INLINE_ void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bitmap->description); + return; + } + for (i=0; i < num; i++) + ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); +} + +_INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, + blk_t block, int num) +{ + int i; + +#ifdef EXT2FS_DEBUG_FAST_OPS + if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { + ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, + bitmap->description); + return; + } +#endif + for (i=0; i < num; i++) + ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap); +} + #undef _INLINE_ #endif diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c index b7f0aa3b..3552dab1 100644 --- a/lib/ext2fs/block.c +++ b/lib/ext2fs/block.c @@ -1,8 +1,12 @@ /* * block.c --- iterate over all blocks in an inode * - * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -22,6 +26,8 @@ struct block_context { int (*func)(ext2_filsys fs, blk_t *blocknr, int bcount, + blk_t ref_blk, + int ref_offset, void *private); int bcount; int bsize; @@ -33,16 +39,18 @@ struct block_context { void *private; }; -static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx) +static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; - int i, flags, limit; + int i, flags, limit, offset; blk_t *block_nr; if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) ret = (*ctx->func)(ctx->fs, ind_block, - BLOCK_COUNT_IND, ctx->private); + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->private); if (!*ind_block || (ret & BLOCK_ABORT)) return ret; if (*ind_block >= ctx->fs->super->s_blocks_count || @@ -65,27 +73,32 @@ static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx) *block_nr = ext2fs_swab32(*block_nr); } block_nr = (blk_t *) ctx->ind_buf; + offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, ctx->private); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } + offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { if (*block_nr == 0) continue; flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, ctx->private); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } + offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { @@ -104,20 +117,23 @@ static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx) !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, ind_block, - BLOCK_COUNT_IND, ctx->private); + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->private); return ret; } -static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx) +static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; - int i, flags, limit; + int i, flags, limit, offset; blk_t *block_nr; if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) ret = (*ctx->func)(ctx->fs, dind_block, - BLOCK_COUNT_DIND, ctx->private); + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->private); if (!*dind_block || (ret & BLOCK_ABORT)) return ret; if (*dind_block >= ctx->fs->super->s_blocks_count || @@ -140,25 +156,32 @@ static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx) *block_nr = ext2fs_swab32(*block_nr); } block_nr = (blk_t *) ctx->dind_buf; + offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { - flags = block_iterate_ind(block_nr, ctx); + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } + offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) continue; - flags = block_iterate_ind(block_nr, ctx); + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } + offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { @@ -177,20 +200,23 @@ static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx) !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, dind_block, - BLOCK_COUNT_DIND, ctx->private); + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->private); return ret; } -static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx) +static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; - int i, flags, limit; + int i, flags, limit, offset; blk_t *block_nr; if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) ret = (*ctx->func)(ctx->fs, tind_block, - BLOCK_COUNT_TIND, ctx->private); + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->private); if (!*tind_block || (ret & BLOCK_ABORT)) return ret; if (*tind_block >= ctx->fs->super->s_blocks_count || @@ -213,25 +239,32 @@ static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx) *block_nr = ext2fs_swab32(*block_nr); } block_nr = (blk_t *) ctx->tind_buf; + offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { - flags = block_iterate_dind(block_nr, ctx); + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } + offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) continue; - flags = block_iterate_dind(block_nr, ctx); + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } + offset += sizeof(blk_t); } } if (changed & BLOCK_CHANGED) { @@ -250,29 +283,32 @@ static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx) !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, tind_block, - BLOCK_COUNT_TIND, ctx->private); + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->private); return ret; } -errcode_t ext2fs_block_iterate(ext2_filsys fs, - ino_t ino, - int flags, - char *block_buf, - int (*func)(ext2_filsys fs, - blk_t *blocknr, - int blockcnt, - void *private), - void *private) +errcode_t ext2fs_block_iterate2(ext2_filsys fs, + ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + blk_t ref_blk, + int ref_offset, + void *private), + void *private) { int i; int got_inode = 0; int ret = 0; - struct block_context ctx; blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */ struct ext2_inode inode; errcode_t retval; - + struct block_context ctx; + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); ret = ext2fs_get_blocks(fs, ino, blocks); @@ -282,8 +318,8 @@ errcode_t ext2fs_block_iterate(ext2_filsys fs, ctx.fs = fs; ctx.func = func; ctx.private = private; - ctx.bcount = 0; ctx.flags = flags; + ctx.bcount = 0; if (block_buf) { ctx.ind_buf = block_buf; } else { @@ -304,8 +340,10 @@ errcode_t ext2fs_block_iterate(ext2_filsys fs, goto abort; got_inode = 1; if (inode.osd1.hurd1.h_i_translator) { - ret |= (*func)(fs, &inode.osd1.hurd1.h_i_translator, - BLOCK_COUNT_TRANSLATOR, private); + ret |= (*ctx.func)(fs, + &inode.osd1.hurd1.h_i_translator, + BLOCK_COUNT_TRANSLATOR, + 0, 0, private); if (ret & BLOCK_ABORT) goto abort; } @@ -316,23 +354,27 @@ errcode_t ext2fs_block_iterate(ext2_filsys fs, */ for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) { - ret |= (*func)(fs, &blocks[i], ctx.bcount, private); + ret |= (*ctx.func)(fs, &blocks[i], + ctx.bcount, 0, 0, private); if (ret & BLOCK_ABORT) goto abort; } } if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { - ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, &ctx); + ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, + 0, 0, &ctx); if (ret & BLOCK_ABORT) goto abort; } if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { - ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, &ctx); + ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, + 0, 0, &ctx); if (ret & BLOCK_ABORT) goto abort; } if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) { - ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, &ctx); + ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, + 0, 0, &ctx); if (ret & BLOCK_ABORT) goto abort; } @@ -356,3 +398,40 @@ abort: return (ret & BLOCK_ERROR) ? ctx.errcode : 0; } + +struct xlate { + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int bcount, + void *private); + void *real_private; +}; + +static int xlate_func(ext2_filsys fs, blk_t *blocknr, int blockcnt, + blk_t ref_block, int ref_offset, void *private) +{ + struct xlate *xl = private; + + return (*xl->func)(fs, blocknr, blockcnt, xl->real_private); +} + +errcode_t ext2fs_block_iterate(ext2_filsys fs, + ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *private), + void *private) +{ + struct xlate xl; + + xl.real_private = private; + xl.func = func; + + return ext2fs_block_iterate2(fs, ino, flags, block_buf, + xlate_func, &xl); +} + + diff --git a/lib/ext2fs/brel.h b/lib/ext2fs/brel.h new file mode 100644 index 00000000..f31b7ae2 --- /dev/null +++ b/lib/ext2fs/brel.h @@ -0,0 +1,84 @@ +/* + * brel.h + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +struct ext2_block_relocate_entry { + blk_t new; + __s16 offset; + __u16 flags; + union { + blk_t block_ref; + ino_t inode_ref; + } owner; +}; + +#define RELOCATE_INODE_REF 0x0001 + +typedef struct ext2_block_relocation_table *ext2_brel; + +struct ext2_block_relocation_table { + __u32 magic; + char *name; + blk_t current; + void *private; + + /* + * Add a block relocation entry. + */ + errcode_t (*put)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Get a block relocation entry. + */ + errcode_t (*get)(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); + + /* + * Initialize for iterating over the block relocation entries. + */ + errcode_t (*start_iter)(ext2_brel brel); + + /* + * The iterator function for the inode relocation entries. + * Returns an inode number of 0 when out of entries. + */ + errcode_t (*next)(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); + + /* + * Move the inode relocation table from one block number to + * another. + */ + errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); + + /* + * Remove a block relocation entry. + */ + errcode_t (*delete)(ext2_brel brel, blk_t old); + + + /* + * Free the block relocation table. + */ + errcode_t (*free)(ext2_brel brel); +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *brel); + +#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) +#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) +#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) +#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) +#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) +#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) +#define ext2fs_brel_free(brel) ((brel)->free((brel))) + diff --git a/lib/ext2fs/brel_ma.c b/lib/ext2fs/brel_ma.c new file mode 100644 index 00000000..a4258a70 --- /dev/null +++ b/lib/ext2fs/brel_ma.c @@ -0,0 +1,191 @@ +/* + * brel_ma.c + * + * Copyright (C) 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "ext2fs.h" +#include "brel.h" + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_start_iter(ext2_brel brel); +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent); +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); +static errcode_t bma_delete(ext2_brel brel, blk_t old); +static errcode_t bma_free(ext2_brel brel); + +struct brel_ma { + __u32 magic; + blk_t max_block; + struct ext2_block_relocate_entry *entries; +}; + +errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, + ext2_brel *new_brel) +{ + ext2_brel brel = 0; + errcode_t retval; + struct brel_ma *ma = 0; + size_t size; + + *new_brel = 0; + + /* + * Allocate memory structures + */ + retval = ENOMEM; + brel = malloc(sizeof(struct ext2_block_relocation_table)); + if (!brel) + goto errout; + memset(brel, 0, sizeof(struct ext2_block_relocation_table)); + + brel->name = malloc(strlen(name)+1); + if (!brel->name) + goto errout; + strcpy(brel->name, name); + + ma = malloc(sizeof(struct brel_ma)); + if (!ma) + goto errout; + memset(ma, 0, sizeof(struct brel_ma)); + brel->private = ma; + + size = sizeof(struct ext2_block_relocate_entry) * (max_block+1); + ma->entries = malloc(size); + if (!ma->entries) + goto errout; + memset(ma->entries, 0, size); + ma->max_block = max_block; + + /* + * Fill in the brel data structure + */ + brel->put = bma_put; + brel->get = bma_get; + brel->start_iter = bma_start_iter; + brel->next = bma_next; + brel->move = bma_move; + brel->delete = bma_delete; + brel->free = bma_free; + + *new_brel = brel; + return 0; + +errout: + bma_free(brel); + return retval; +} + +static errcode_t bma_put(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->private; + if (old > ma->max_block) + return EINVAL; + ma->entries[old] = *ent; + return 0; +} + +static errcode_t bma_get(ext2_brel brel, blk_t old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->private; + if (old > ma->max_block) + return EINVAL; + if (ma->entries[old].new == 0) + return ENOENT; + *ent = ma->entries[old]; + return 0; +} + +static errcode_t bma_start_iter(ext2_brel brel) +{ + brel->current = 0; + return 0; +} + +static errcode_t bma_next(ext2_brel brel, blk_t *old, + struct ext2_block_relocate_entry *ent) +{ + struct brel_ma *ma; + + ma = brel->private; + while (++brel->current < ma->max_block) { + if (ma->entries[brel->current].new == 0) + continue; + *old = brel->current; + *ent = ma->entries[brel->current]; + return 0; + } + *old = 0; + return 0; +} + +static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) +{ + struct brel_ma *ma; + + ma = brel->private; + if ((old > ma->max_block) || (new > ma->max_block)) + return EINVAL; + if (ma->entries[old].new == 0) + return ENOENT; + ma->entries[new] = ma->entries[old]; + ma->entries[old].new = 0; + return 0; +} + +static errcode_t bma_delete(ext2_brel brel, blk_t old) +{ + struct brel_ma *ma; + + ma = brel->private; + if (old > ma->max_block) + return EINVAL; + if (ma->entries[old].new == 0) + return ENOENT; + ma->entries[old].new = 0; + return 0; +} + +static errcode_t bma_free(ext2_brel brel) +{ + struct brel_ma *ma; + + if (!brel) + return 0; + + ma = brel->private; + + if (ma) { + if (ma->entries) + free (ma->entries); + free(ma); + } + if (brel->name) + free(brel->name); + free (brel); + return 0; +} diff --git a/lib/ext2fs/check_desc.c b/lib/ext2fs/check_desc.c index 7d3f3d41..74b3e2d4 100644 --- a/lib/ext2fs/check_desc.c +++ b/lib/ext2fs/check_desc.c @@ -1,8 +1,12 @@ /* * check_desc.c --- Check the group descriptors of an ext2 filesystem * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c index 18a93db5..e24b6b69 100644 --- a/lib/ext2fs/closefs.c +++ b/lib/ext2fs/closefs.c @@ -1,8 +1,12 @@ /* * closefs.c --- close an ext2 filesystem * - * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include @@ -16,7 +20,7 @@ #include -#include "ext2fs.h" +#include "ext2fsP.h" errcode_t ext2fs_flush(ext2_filsys fs) { @@ -118,7 +122,6 @@ errcode_t ext2fs_flush(ext2_filsys fs) if (retval) goto errout; } - retval = 0; errout: fs->super->s_state = fs_state; @@ -142,6 +145,42 @@ errcode_t ext2fs_close(ext2_filsys fs) if (retval) return retval; } + if (fs->write_bitmaps) { + retval = fs->write_bitmaps(fs); + if (retval) + return retval; + } ext2fs_free(fs); return 0; } + +/* + * This procedure frees a badblocks list. + */ +void ext2fs_badblocks_list_free(ext2_badblocks_list bb) +{ + if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) + return; + + if (bb->list) + free(bb->list); + bb->list = 0; + free(bb); +} + +/* + * Close a directory block list + */ +void ext2fs_free_dblist(ext2_dblist dblist) +{ + if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) + return; + + if (dblist->list) + free(dblist->list); + dblist->list = 0; + if (dblist->fs && dblist->fs->dblist == dblist) + dblist->fs->dblist = 0; + dblist->magic = 0; + free(dblist); +} diff --git a/lib/ext2fs/cmp_bitmaps.c b/lib/ext2fs/cmp_bitmaps.c index cc9eb6d7..3d04a97e 100644 --- a/lib/ext2fs/cmp_bitmaps.c +++ b/lib/ext2fs/cmp_bitmaps.c @@ -1,8 +1,12 @@ /* * cmp_bitmaps.c --- routines to compare inode and block bitmaps. * - * Copyright (C) 1995 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1995 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c new file mode 100644 index 00000000..72d58407 --- /dev/null +++ b/lib/ext2fs/dblist.c @@ -0,0 +1,175 @@ +/* + * dblist.c -- directory block list functions + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include "ext2fsP.h" + +static int dir_block_cmp(const void *a, const void *b); + +/* + * Returns the number of directories in the filesystem as reported by + * the group descriptors. Of course, the group descriptors could be + * wrong! + */ +errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ino_t *ret_num_dirs) +{ + int i; + ino_t num_dirs; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + num_dirs = 0; + for (i = 0; i < fs->group_desc_count; i++) + num_dirs += fs->group_desc[i].bg_used_dirs_count; + + *ret_num_dirs = num_dirs; + + return 0; +} + +/* + * Initialize a directory block list + */ +errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) +{ + ext2_dblist dblist; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + if ((ret_dblist == 0) && fs->dblist && + (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) + return 0; + + dblist = malloc(sizeof(struct ext2_struct_dblist)); + if (!dblist) + return ENOMEM; + memset(dblist, 0, sizeof(struct ext2_struct_dblist)); + + dblist->magic = EXT2_ET_MAGIC_DBLIST; + dblist->fs = fs; + retval = ext2fs_get_num_dirs(fs, &dblist->size); + if (retval) + goto cleanup; + + dblist->count = 0; + dblist->sorted = 1; + dblist->list = malloc(sizeof(struct ext2_db_entry) * dblist->size); + if (dblist->list == NULL) { + retval = ENOMEM; + goto cleanup; + } + if (ret_dblist) + *ret_dblist = dblist; + else + fs->dblist = dblist; + + return 0; +cleanup: + if (dblist) + free(dblist); + return retval; +} + +/* + * Close a directory block list + * + * (moved to closefs.c) + */ + + +/* + * Add a directory block to the directory block list + */ +errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ino_t ino, blk_t blk, + int blockcnt) +{ + struct ext2_db_entry *nlist, *new; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (dblist->count >= dblist->size) { + dblist->size += 100; + nlist = realloc(dblist->list, + dblist->size * sizeof(struct ext2_db_entry)); + if (nlist == 0) { + dblist->size -= 100; + return ENOMEM; + } + dblist->list = nlist; + } + new = dblist->list + dblist->count++; + new->blk = blk; + new->ino = ino; + new->blockcnt = blockcnt; + + dblist->sorted = 0; + + return 0; +} + +/* + * This function iterates over the directory block list + */ +errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, + int (*func)(ext2_filsys fs, + struct ext2_db_entry *db_info, + void *private), + void *private) +{ + ino_t i; + int ret; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + if (!dblist->sorted) { + qsort(dblist->list, dblist->count, + sizeof(struct ext2_db_entry), dir_block_cmp); + dblist->sorted = 1; + } + for (i=0; i < dblist->count; i++) { + ret = (*func)(dblist->fs, &dblist->list[i], private); + if (ret & DBLIST_ABORT) + return 0; + } + + return 0; +} + + +static int dir_block_cmp(const void *a, const void *b) +{ + const struct ext2_db_entry *db_a = + (const struct ext2_db_entry *) a; + const struct ext2_db_entry *db_b = + (const struct ext2_db_entry *) b; + + if (db_a->blk != db_b->blk) + return (db_a->blk - db_b->blk); + + if (db_a->ino != db_b->ino) + return (db_a->ino - db_b->ino); + + return (db_a->blockcnt - db_b->blockcnt); +} + + diff --git a/lib/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c new file mode 100644 index 00000000..10c7b58a --- /dev/null +++ b/lib/ext2fs/dblist_dir.c @@ -0,0 +1,79 @@ +/* + * dblist_dir.c --- iterate by directory entry + * + * Copyright 1997 by Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include "ext2fsP.h" + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, + void *private); + +extern errcode_t + ext2fs_dblist_dir_iterate(ext2_dblist dblist, + int flags, + char *block_buf, + int (*func)(ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *private), + void *private) +{ + errcode_t retval; + struct dir_context ctx; + + EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); + + ctx.dir = 0; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + ctx.buf = malloc(dblist->fs->blocksize); + if (!ctx.buf) + return ENOMEM; + } + ctx.func = 0; + ctx.func2 = func; + ctx.private = private; + ctx.errcode = 0; + + retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx); + + if (!block_buf) + free(ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, + void *private) +{ + struct dir_context *ctx = private; + + ctx->dir = db_info->ino; + + return ext2fs_process_dir_block(fs, &db_info->blk, + db_info->blockcnt, private); +} diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c new file mode 100644 index 00000000..91f8ca67 --- /dev/null +++ b/lib/ext2fs/dir_iterate.c @@ -0,0 +1,134 @@ +/* + * dir_iterate.c --- ext2fs directory iteration operations + * + * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include +#include +#include +#include +#if HAVE_ERRNO_H +#include +#endif + +#include + +#include "ext2fsP.h" + +errcode_t ext2fs_dir_iterate(ext2_filsys fs, + ino_t dir, + int flags, + char *block_buf, + int (*func)(struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *private), + void *private) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + retval = ext2fs_check_directory(fs, dir); + if (retval) + return retval; + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + ctx.buf = malloc(fs->blocksize); + if (!ctx.buf) + return ENOMEM; + } + ctx.func = func; + ctx.func2 = 0; + ctx.private = private; + ctx.errcode = 0; + retval = ext2fs_block_iterate(fs, dir, 0, 0, + ext2fs_process_dir_block, &ctx); + if (!block_buf) + free(ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + +/* + * Helper function which is private to this module. Used by + * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() + */ +extern int ext2fs_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + int blockcnt, + void *private) +{ + struct dir_context *ctx = (struct dir_context *) private; + int offset = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + int entry; + struct ext2_dir_entry *dirent; + + if (blockcnt < 0) + return 0; + + ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + if (ctx->func) + ret = (ctx->func)(dirent, offset, fs->blocksize, + ctx->buf, ctx->private); + else if (ctx->func2) { + ret = (ctx->func2)(ctx->dir, entry, dirent, offset, + fs->blocksize, ctx->buf, + ctx->private); + if (entry < DIRENT_OTHER_FILE) + entry++; + } + + if (ret & DIRENT_CHANGED) + changed++; + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (((offset + dirent->rec_len) > fs->blocksize) || + (dirent->rec_len < 8) || + ((dirent->name_len+8) > dirent->rec_len)) { + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + offset += dirent->rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} + diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c index 0f56617d..4db5b18c 100644 --- a/lib/ext2fs/dirblock.c +++ b/lib/ext2fs/dirblock.c @@ -1,13 +1,18 @@ /* * dirblock.c --- directory block routines. * - * Copyright (C) 1995 Theodore Ts'o. This file may be redistributed - * under the terms of the GNU Public License. + * Copyright (C) 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% */ #include #include #include +#include #include #ifdef HAVE_ERRNO_H #include diff --git a/version.h b/version.h index 37c017af..eb84da55 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,5 @@ * under the GNU Public License. */ -#define E2FSPROGS_VERSION "1.06" -#define E2FSPROGS_DATE "7-Oct-96" - +#define E2FSPROGS_VERSION "1.07" +#define E2FSPROGS_DATE "14-Mar-97"