summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.bsd-lib2
-rw-r--r--lib/Makefile.elf-lib16
-rw-r--r--lib/Makefile.solaris-lib3
-rw-r--r--lib/blkid/Makefile.in13
-rw-r--r--lib/blkid/blkid.pc.in2
-rw-r--r--lib/blkid/blkid_types.h.in16
-rw-r--r--lib/blkid/cache.c9
-rw-r--r--lib/blkid/devname.c2
-rw-r--r--lib/blkid/getsize.c109
-rw-r--r--lib/blkid/list.h5
-rw-r--r--lib/blkid/probe.c39
-rw-r--r--lib/blkid/save.c9
-rw-r--r--lib/blkid/test_probe.in4
-rw-r--r--lib/config.h.in38
-rw-r--r--lib/e2p/Makefile.in9
-rw-r--r--lib/e2p/e2p.h2
-rw-r--r--lib/e2p/e2p.pc.in2
-rw-r--r--lib/e2p/feature.c12
-rw-r--r--lib/e2p/getversion.c2
-rw-r--r--lib/e2p/ls.c10
-rw-r--r--lib/e2p/parse_num.c24
-rw-r--r--lib/e2p/pf.c1
-rw-r--r--lib/et/Makefile.in44
-rw-r--r--lib/et/com_err.c5
-rw-r--r--lib/et/com_err.pc.in2
-rw-r--r--lib/et/com_err.texinfo43
-rw-r--r--lib/et/error_message.c10
-rw-r--r--lib/et/error_table.h2
-rw-r--r--lib/et/et_name.c3
-rw-r--r--lib/et/texinfo.tex8217
-rw-r--r--lib/ext2fs/Makefile.in146
-rw-r--r--lib/ext2fs/alloc.c86
-rw-r--r--lib/ext2fs/alloc_sb.c13
-rw-r--r--lib/ext2fs/alloc_stats.c41
-rw-r--r--lib/ext2fs/alloc_tables.c26
-rw-r--r--lib/ext2fs/bitops.c40
-rw-r--r--lib/ext2fs/bitops.h173
-rw-r--r--lib/ext2fs/blkmap64_ba.c83
-rw-r--r--lib/ext2fs/blkmap64_rb.c331
-rw-r--r--lib/ext2fs/blknum.c7
-rw-r--r--lib/ext2fs/block.c23
-rw-r--r--lib/ext2fs/bmap.c82
-rw-r--r--lib/ext2fs/bmap64.h4
-rw-r--r--lib/ext2fs/brel.h18
-rw-r--r--lib/ext2fs/brel_ma.c24
-rw-r--r--lib/ext2fs/check_desc.c3
-rw-r--r--lib/ext2fs/closefs.c33
-rw-r--r--lib/ext2fs/crc32c.c16
-rw-r--r--lib/ext2fs/csum.c58
-rw-r--r--lib/ext2fs/dblist.c5
-rw-r--r--lib/ext2fs/dir_iterate.c20
-rw-r--r--lib/ext2fs/dupfs.c25
-rw-r--r--lib/ext2fs/e2image.h9
-rw-r--r--lib/ext2fs/expanddir.c2
-rw-r--r--lib/ext2fs/ext2_err.et.in39
-rw-r--r--lib/ext2fs/ext2_fs.h10
-rw-r--r--lib/ext2fs/ext2_io.h3
-rw-r--r--lib/ext2fs/ext2_types.h.in16
-rw-r--r--lib/ext2fs/ext2fs.h156
-rw-r--r--lib/ext2fs/ext2fs.pc.in2
-rw-r--r--lib/ext2fs/ext2fsP.h6
-rw-r--r--lib/ext2fs/extent.c531
-rw-r--r--lib/ext2fs/extent_dbg.ct74
-rw-r--r--lib/ext2fs/fiemap.h2
-rw-r--r--lib/ext2fs/fileio.c105
-rw-r--r--lib/ext2fs/flushb.c2
-rw-r--r--lib/ext2fs/freefs.c2
-rw-r--r--lib/ext2fs/gen_bitmap.c47
-rw-r--r--lib/ext2fs/gen_bitmap64.c138
-rw-r--r--lib/ext2fs/get_pathname.c10
-rw-r--r--lib/ext2fs/getsectsize.c26
-rw-r--r--lib/ext2fs/getsize.c121
-rw-r--r--lib/ext2fs/icount.c26
-rw-r--r--lib/ext2fs/imager.c15
-rw-r--r--lib/ext2fs/initialize.c61
-rw-r--r--lib/ext2fs/inline.c53
-rw-r--r--lib/ext2fs/inode.c14
-rw-r--r--lib/ext2fs/io_manager.c17
-rw-r--r--lib/ext2fs/irel.h2
-rw-r--r--lib/ext2fs/ismounted.c83
-rw-r--r--lib/ext2fs/kernel-jbd.h6
-rw-r--r--lib/ext2fs/kernel-list.h3
-rw-r--r--lib/ext2fs/link.c3
-rw-r--r--lib/ext2fs/llseek.c18
-rw-r--r--lib/ext2fs/mkjournal.c112
-rw-r--r--lib/ext2fs/mmp.c48
-rw-r--r--lib/ext2fs/namei.c9
-rw-r--r--lib/ext2fs/newdir.c8
-rw-r--r--lib/ext2fs/openfs.c91
-rw-r--r--lib/ext2fs/progress.c14
-rw-r--r--lib/ext2fs/punch.c192
-rw-r--r--lib/ext2fs/qcow2.c22
-rw-r--r--lib/ext2fs/rbtree.c4
-rw-r--r--lib/ext2fs/rbtree.h6
-rw-r--r--lib/ext2fs/read_bb_file.c7
-rw-r--r--lib/ext2fs/res_gdt.c13
-rw-r--r--lib/ext2fs/rw_bitmaps.c71
-rw-r--r--lib/ext2fs/sparse.c79
-rw-r--r--lib/ext2fs/swapfs.c8
-rw-r--r--lib/ext2fs/symlink.c150
-rw-r--r--lib/ext2fs/test_io.c134
-rw-r--r--lib/ext2fs/tst_bitmaps.c167
-rw-r--r--lib/ext2fs/tst_bitmaps_cmd.ct12
-rw-r--r--lib/ext2fs/tst_bitmaps_cmds102
-rw-r--r--lib/ext2fs/tst_bitmaps_exp217
-rw-r--r--lib/ext2fs/tst_inode_size.c3
-rw-r--r--lib/ext2fs/tst_iscan.c4
-rw-r--r--lib/ext2fs/tst_super_size.c6
-rw-r--r--lib/ext2fs/undo_io.c55
-rw-r--r--lib/ext2fs/unix_io.c181
-rw-r--r--lib/quota/Makefile.in85
-rw-r--r--lib/quota/common.h20
-rw-r--r--lib/quota/dqblk_v2.h22
-rw-r--r--lib/quota/mkquota.c216
-rw-r--r--lib/quota/mkquota.h63
-rw-r--r--lib/quota/quota.h155
-rw-r--r--lib/quota/quota.pc.in11
-rw-r--r--lib/quota/quotaio.c127
-rw-r--r--lib/quota/quotaio.h119
-rw-r--r--lib/quota/quotaio_tree.c80
-rw-r--r--lib/quota/quotaio_tree.h13
-rw-r--r--lib/quota/quotaio_v2.c56
-rw-r--r--lib/quota/quotaio_v2.h91
-rw-r--r--lib/ss/Makefile.in13
-rw-r--r--lib/ss/error.c8
-rw-r--r--lib/ss/execute_cmd.c20
-rw-r--r--lib/ss/help.c19
-rw-r--r--lib/ss/invocation.c18
-rw-r--r--lib/ss/list_rqs.c29
-rw-r--r--lib/ss/pager.c10
-rw-r--r--lib/ss/parse.c23
-rw-r--r--lib/ss/request_tbl.c13
-rw-r--r--lib/ss/ss.pc.in2
-rw-r--r--lib/ss/ss_internal.h1
-rw-r--r--lib/uuid/Makefile.in15
-rw-r--r--lib/uuid/gen_uuid.c40
-rw-r--r--lib/uuid/tst_uuid.c33
-rw-r--r--lib/uuid/uuid.pc.in2
-rw-r--r--lib/uuid/uuid_time.c2
139 files changed, 10469 insertions, 4066 deletions
diff --git a/lib/Makefile.bsd-lib b/lib/Makefile.bsd-lib
index 51e9dc77..0ca09f8c 100644
--- a/lib/Makefile.bsd-lib
+++ b/lib/Makefile.bsd-lib
@@ -22,7 +22,7 @@ BSDLIB_PIC_FLAG = -fpic
image: $(BSD_LIB)
$(BSD_LIB): $(OBJS)
- (cd pic; ld -Bshareable -o $(BSD_LIB) $(OBJS))
+ (cd pic; ld -Bshareable -o $(BSD_LIB) $(LDFLAGS) $(OBJS))
$(MV) pic/$(BSD_LIB) .
$(RM) -f ../$(BSD_LIB)
(cd ..; $(LN) $(LINK_BUILD_FLAGS) \
diff --git a/lib/Makefile.elf-lib b/lib/Makefile.elf-lib
index ea600c7a..78479d31 100644
--- a/lib/Makefile.elf-lib
+++ b/lib/Makefile.elf-lib
@@ -24,7 +24,8 @@ image: $(ELF_LIB)
$(ELF_LIB): $(OBJS)
$(E) " GEN_ELF_SOLIB $(ELF_LIB)"
- $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) $(LDFLAGS) \
+ $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) \
+ -L$(top_builddir)/../lib $(LDFLAGS) \
-Wl,-soname,$(ELF_SONAME) $(OBJS) $(ELF_OTHER_LIBS))
$(Q) $(MV) elfshared/$(ELF_LIB) .
$(Q) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
@@ -40,18 +41,15 @@ installdirs-elf-lib::
installdirs:: installdirs-elf-lib
-install-shlibs install:: $(ELF_LIB) installdirs-elf-lib
+install-shlibs install:: $(ELF_LIB) installdirs-elf-lib $(DEP_INSTALL_SYMLINK)
$(E) " INSTALL-ELF-LIB $(ELF_INSTALL_DIR)/$(ELF_LIB)"
$(Q) $(INSTALL_PROGRAM) $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
$(E) " SYMLINK $(ELF_INSTALL_DIR)/$(ELF_SONAME)"
- $(Q) $(LN_S) -f $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME)
+ $(Q) $(INSTALL_SYMLINK) $(ELF_INSTALL_DIR)/$(ELF_LIB) \
+ $(ELF_INSTALL_DIR)/$(ELF_SONAME) $(DESTDIR)
$(E) " SYMLINK $(libdir)/$(ELF_IMAGE).so"
- $(Q) if test "$(ELF_INSTALL_DIR)" = "$(libdir)"; then \
- $(LN_S) -f $(ELF_SONAME) $(DESTDIR)$(libdir)/$(ELF_IMAGE).so ; \
- else \
- $(LN_S) -f $(ELF_INSTALL_DIR)/$(ELF_SONAME) \
- $(DESTDIR)$(libdir)/$(ELF_IMAGE).so; \
- fi
+ $(Q) $(INSTALL_SYMLINK) $(ELF_INSTALL_DIR)/$(ELF_SONAME) \
+ $(libdir)/$(ELF_IMAGE).so $(DESTDIR)
$(E) " LDCONFIG"
$(Q) -$(LDCONFIG)
diff --git a/lib/Makefile.solaris-lib b/lib/Makefile.solaris-lib
index 92bdbe28..5990be8a 100644
--- a/lib/Makefile.solaris-lib
+++ b/lib/Makefile.solaris-lib
@@ -24,7 +24,8 @@ image: $(ELF_LIB)
$(ELF_LIB): $(OBJS)
$(E) " GEN_ELF_SOLIB $(ELF_LIB)"
- $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) $(LDFLAGS) \
+ $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) \
+ -L$(top_builddir)/../lib $(LDFLAGS) \
-Wl,-h,$(ELF_SONAME) $(OBJS) $(ELF_OTHER_LIBS))
$(Q) $(MV) elfshared/$(ELF_LIB) .
$(Q) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
diff --git a/lib/blkid/Makefile.in b/lib/blkid/Makefile.in
index f23a1376..14724325 100644
--- a/lib/blkid/Makefile.in
+++ b/lib/blkid/Makefile.in
@@ -36,7 +36,7 @@ ELF_SO_VERSION = 1
ELF_IMAGE = libblkid
ELF_MYDIR = blkid
ELF_INSTALL_DIR = $(root_libdir)
-ELF_OTHER_LIBS = -L../.. -luuid
+ELF_OTHER_LIBS = -luuid
BSDLIB_VERSION = 2.0
BSDLIB_IMAGE = libblkid
@@ -55,6 +55,7 @@ DEPLIBS_BLKID= $(DEPSTATIC_LIBBLKID) $(DEPSTATIC_LIBUUID)
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
@@ -117,7 +118,7 @@ tst_tag: $(srcdir)/tag.c $(DEPLIBS_BLKID)
tst_types: tst_types.o blkid_types.h
$(E) " LD $@"
- $(Q) $(CC) -o tst_types tst_types.o
+ $(Q) $(CC) -o tst_types $(ALL_LDFLAGS) tst_types.o
../../misc/blkid.o: $(top_srcdir)/misc/blkid.c blkid.h
$(E) " CC $@"
@@ -147,7 +148,7 @@ blkid.pc: $(srcdir)/blkid.pc.in $(top_builddir)/config.status
installdirs::
$(E) " MKINSTALLDIRS $(libdir) $(includedir)/blkid"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
- $(DESTDIR)$(includedir)/blkid $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(includedir)/blkid $(DESTDIR)$(pkgconfigdir)
install:: all installdirs
$(E) " INSTALL_DATA $(libdir)/libblkid.a"
@@ -162,12 +163,12 @@ install:: all installdirs
echo " INSTALL_DATA $(man3dir)/$$i"; \
$(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \
done
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/blkid.pc"
- $(Q) $(INSTALL_DATA) blkid.pc $(DESTDIR)$(libdir)/pkgconfig/blkid.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/blkid.pc"
+ $(Q) $(INSTALL_DATA) blkid.pc $(DESTDIR)$(pkgconfigdir)/blkid.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libblkid.a \
- $(DESTDIR)$(libdir)/pkgconfig/blkid.pc
+ $(DESTDIR)$(pkgconfigdir)/blkid.pc
$(RM) -rf $(DESTDIR)$(includedir)/blkid
for i in $(SMANPAGES); do \
$(RM) -f $(DESTDIR)$(man3dir)/$$i; \
diff --git a/lib/blkid/blkid.pc.in b/lib/blkid/blkid.pc.in
index b984f6d0..808fe229 100644
--- a/lib/blkid/blkid.pc.in
+++ b/lib/blkid/blkid.pc.in
@@ -7,5 +7,5 @@ Name: blkid
Description: Block device id library
Version: @E2FSPROGS_VERSION@
Requires.private: uuid
-Cflags: -I${includedir}/blkid
+Cflags: -I${includedir}/blkid -I${includedir}
Libs: -L${libdir} -lblkid
diff --git a/lib/blkid/blkid_types.h.in b/lib/blkid/blkid_types.h.in
index cb5b10d5..2edc0dad 100644
--- a/lib/blkid/blkid_types.h.in
+++ b/lib/blkid/blkid_types.h.in
@@ -92,13 +92,13 @@ typedef __U64_TYPEDEF __u64;
#if (@SIZEOF_INT@ == 8)
typedef unsigned int __u64;
#else
-#if (@SIZEOF_LONG@ == 8)
-typedef unsigned long __u64;
-#else
#if (@SIZEOF_LONG_LONG@ == 8)
typedef unsigned long long __u64;
-#endif /* SIZEOF_LONG_LONG == 8 */
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef unsigned long __u64;
#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __U64_TYPEDEF */
@@ -108,17 +108,17 @@ typedef __S64_TYPEDEF __s64;
#if (@SIZEOF_INT@ == 8)
typedef int __s64;
#else
-#if (@SIZEOF_LONG@ == 8)
-typedef long __s64;
-#else
#if (@SIZEOF_LONG_LONG@ == 8)
#if defined(__GNUC__)
typedef __signed__ long long __s64;
#else
typedef signed long long __s64;
#endif /* __GNUC__ */
-#endif /* SIZEOF_LONG_LONG == 8 */
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef long __s64;
#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
#endif /* __S64_TYPEDEF */
diff --git a/lib/blkid/cache.c b/lib/blkid/cache.c
index 73900a51..2a2df13c 100644
--- a/lib/blkid/cache.c
+++ b/lib/blkid/cache.c
@@ -11,6 +11,9 @@
*/
#include "config.h"
+#if HAVE_SECURE_GETENV
+#define _GNU_SOURCE
+#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -49,7 +52,9 @@ static char *safe_getenv(const char *arg)
#endif
#endif
-#ifdef HAVE___SECURE_GETENV
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(arg);
#else
return getenv(arg);
@@ -162,8 +167,6 @@ void blkid_gc_cache(blkid_cache cache)
list_for_each_safe(p, pnext, &cache->bic_devs) {
blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
- if (!p)
- break;
if (stat(dev->bid_name, &st) < 0) {
DBG(DEBUG_CACHE,
printf("freeing %s\n", dev->bid_name));
diff --git a/lib/blkid/devname.c b/lib/blkid/devname.c
index a6673c17..3e2efa9d 100644
--- a/lib/blkid/devname.c
+++ b/lib/blkid/devname.c
@@ -91,8 +91,6 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
*/
list_for_each_safe(p, pnext, &cache->bic_devs) {
blkid_dev dev2;
- if (!p)
- break;
dev2 = list_entry(p, struct blkid_struct_dev, bid_devs);
if (dev2->bid_flags & BLKID_BID_FL_VERIFIED)
continue;
diff --git a/lib/blkid/getsize.c b/lib/blkid/getsize.c
index f670e1b1..c2a8f72a 100644
--- a/lib/blkid/getsize.c
+++ b/lib/blkid/getsize.c
@@ -75,52 +75,46 @@ static int valid_offset(int fd, blkid_loff_t offset)
*/
blkid_loff_t blkid_get_dev_size(int fd)
{
- int valid_blkgetsize64 = 1;
-#ifdef __linux__
- struct utsname ut;
-#endif
unsigned long long size64;
- unsigned long size;
blkid_loff_t high, low;
-#ifdef FDGETPRM
- struct floppy_struct this_floppy;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
- int part = -1;
- struct disklabel lab;
- struct partition *pp;
- char ch;
- struct stat st;
-#endif /* HAVE_SYS_DISKLABEL_H */
#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
- if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
- && (size64 << 9 > 0xFFFFFFFF))
+ if (sizeof(blkid_loff_t) < sizeof(unsigned long long) &&
+ (size64 << 9) > 0xFFFFFFFF)
return 0; /* EFBIG */
- return (blkid_loff_t) size64 << 9;
+ return (blkid_loff_t)size64 << 9;
}
#endif
#ifdef BLKGETSIZE64
+ {
+ int valid_blkgetsize64 = 1;
#ifdef __linux__
- if ((uname(&ut) == 0) &&
- ((ut.release[0] == '2') && (ut.release[1] == '.') &&
- (ut.release[2] < '6') && (ut.release[3] == '.')))
- valid_blkgetsize64 = 0;
-#endif
- if (valid_blkgetsize64 &&
- ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
- if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
- && ((size64) > 0xFFFFFFFF))
- return 0; /* EFBIG */
- return size64;
+ struct utsname ut;
+
+ if ((uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] < '6') && (ut.release[3] == '.')))
+ valid_blkgetsize64 = 0;
+#endif
+ if (valid_blkgetsize64 &&
+ ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+ if (sizeof(blkid_loff_t) < sizeof(unsigned long long) &&
+ (size64 > 0xFFFFFFFF))
+ return 0; /* EFBIG */
+ return size64;
+ }
}
#endif /* BLKGETSIZE64 */
#ifdef BLKGETSIZE
- if (ioctl(fd, BLKGETSIZE, &size) >= 0)
- return (blkid_loff_t)size << 9;
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0)
+ return (blkid_loff_t)size << 9;
+ }
#endif
/* tested on FreeBSD 6.1-RELEASE i386 */
@@ -130,26 +124,39 @@ blkid_loff_t blkid_get_dev_size(int fd)
#endif /* DIOCGMEDIASIZE */
#ifdef FDGETPRM
- if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
- return (blkid_loff_t)this_floppy.size << 9;
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
+ return (blkid_loff_t)this_floppy.size << 9;
+ }
#endif
#ifdef HAVE_SYS_DISKLABEL_H
- /*
- * This code works for FreeBSD 4.11 i386, except for the full device
- * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
- * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
- * above however.
- *
- * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
- * character) devices, so we need to check for S_ISCHR, too.
- */
- if (fstat(fd, &st) >= 0 && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
- part = st.st_rdev & 7;
-
- if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
- pp = &lab.d_partitions[part];
- if (pp->p_size)
- return pp->p_size << 9;
+ {
+ int part = -1;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+ struct stat st;
+
+ /*
+ * This code works for FreeBSD 4.11 i386, except for the full
+ * device (such as /dev/ad0). It doesn't work properly for
+ * newer FreeBSD though. FreeBSD >= 5.0 should be covered by
+ * the DIOCGMEDIASIZE above however.
+ *
+ * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+ * character) devices, so we need to check for S_ISCHR, too.
+ */
+ if (fstat(fd, &st) >= 0 &&
+ (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
+ part = st.st_rdev & 7;
+
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size)
+ return pp->p_size << 9;
+ }
}
#endif /* HAVE_SYS_DISKLABEL_H */
{
@@ -199,8 +206,8 @@ int main(int argc, char **argv)
perror(argv[0]);
bytes = blkid_get_dev_size(fd);
- printf("Device %s has %Ld 1k blocks.\n", argv[1],
- (unsigned long long) bytes >> 10);
+ printf("Device %s has %lld 1k blocks.\n", argv[1],
+ (unsigned long long)bytes >> 10);
return 0;
}
diff --git a/lib/blkid/list.h b/lib/blkid/list.h
index c1cbfec5..26a5ac1b 100644
--- a/lib/blkid/list.h
+++ b/lib/blkid/list.h
@@ -1,4 +1,4 @@
-#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
+#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD_INIT)
#define _BLKID_LIST_H
#ifdef __cplusplus
@@ -27,9 +27,6 @@ struct list_head {
#define LIST_HEAD_INIT(name) { &(name), &(name) }
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c
index 6970b122..4b797ab1 100644
--- a/lib/blkid/probe.c
+++ b/lib/blkid/probe.c
@@ -43,10 +43,8 @@ static int figure_label_len(const unsigned char *label, int len)
while ((*end == ' ' || *end == 0) && end >= label)
--end;
- if (end >= label) {
- label = label;
+ if (end >= label)
return end - label + 1;
- }
return 0;
}
@@ -245,7 +243,7 @@ static int check_for_modules(const char *fs_name)
return (0);
}
-static int linux_version_code()
+static int linux_version_code(void)
{
#ifdef __linux__
struct utsname ut;
@@ -588,7 +586,7 @@ static int probe_fat(struct blkid_probe *probe,
int count;
next_sect_off = (next - 2) * vs->vs_cluster_size;
- next_off = (start_data_sect + next_sect_off) *
+ next_off = (__u64) (start_data_sect + next_sect_off) *
sector_size;
dir = (struct vfat_dir_entry *)
@@ -603,7 +601,9 @@ static int probe_fat(struct blkid_probe *probe,
break;
/* get FAT entry */
- fat_entry_off = (reserved * sector_size) +
+ fat_entry_off =
+ ((unsigned int) reserved *
+ (unsigned int) sector_size) +
(next * sizeof(__u32));
buf = get_buffer(probe, fat_entry_off, buf_size);
if (buf == NULL)
@@ -1005,7 +1005,8 @@ static int probe_udf(struct blkid_probe *probe,
(block sizes larger than 2K will be null padded) */
for (bs = 1; bs < 16; bs++) {
isosb = (struct iso_volume_descriptor *)
- get_buffer(probe, bs*2048+32768, sizeof(isosb));
+ get_buffer(probe, (blkid_loff_t) bs*2048+32768,
+ sizeof(*isosb));
if (!isosb)
return 1;
if (isosb->vd_id[0])
@@ -1017,7 +1018,7 @@ static int probe_udf(struct blkid_probe *probe,
if (j > 1) {
isosb = (struct iso_volume_descriptor *)
get_buffer(probe, j*bs*2048+32768,
- sizeof(isosb));
+ sizeof(*isosb));
if (!isosb)
return 1;
}
@@ -1163,7 +1164,8 @@ static int probe_hfs(struct blkid_probe *probe __BLKID_ATTR((unused)),
struct blkid_magic *id __BLKID_ATTR((unused)),
unsigned char *buf)
{
- struct hfs_mdb *hfs = (struct hfs_mdb *) buf;
+ struct hfs_mdb *hfs = (struct hfs_mdb *)buf;
+ unsigned long long *uuid_ptr;
char uuid_str[17];
__u64 uuid;
@@ -1171,7 +1173,8 @@ static int probe_hfs(struct blkid_probe *probe __BLKID_ATTR((unused)),
(memcmp(hfs->embed_sig, "HX", 2) == 0))
return 1; /* Not hfs, but an embedded HFS+ */
- uuid = blkid_le64(*((unsigned long long *) hfs->finder_info.id));
+ uuid_ptr = (unsigned long long *)hfs->finder_info.id;
+ uuid = blkid_le64(*uuid_ptr);
if (uuid) {
sprintf(uuid_str, "%016llX", uuid);
blkid_set_tag(probe->dev, "UUID", uuid_str, 0);
@@ -1205,9 +1208,10 @@ static int probe_hfsplus(struct blkid_probe *probe,
unsigned int leaf_node_size;
unsigned int leaf_block;
unsigned int label_len;
- int ext;
+ unsigned long long *uuid_ptr;
__u64 leaf_off, uuid;
char uuid_str[17], label[512];
+ int ext;
/* Check for a HFS+ volume embedded in a HFS volume */
if (memcmp(sbd->signature, "BD", 2) == 0) {
@@ -1222,7 +1226,7 @@ static int probe_hfsplus(struct blkid_probe *probe,
off = (alloc_first_block * 512) +
(embed_first_block * alloc_block_size);
buf = get_buffer(probe, off + (id->bim_kboff * 1024),
- sizeof(sbd));
+ sizeof(*sbd));
if (!buf)
return 1;
@@ -1235,7 +1239,8 @@ static int probe_hfsplus(struct blkid_probe *probe,
(memcmp(hfsplus->signature, "HX", 2) != 0))
return 1;
- uuid = blkid_le64(*((unsigned long long *) hfsplus->finder_info.id));
+ uuid_ptr = (unsigned long long *)hfsplus->finder_info.id;
+ uuid = blkid_le64(*uuid_ptr);
if (uuid) {
sprintf(uuid_str, "%016llX", uuid);
blkid_set_tag(probe->dev, "UUID", uuid_str, 0);
@@ -1245,7 +1250,7 @@ static int probe_hfsplus(struct blkid_probe *probe,
memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
cat_block = blkid_be32(extents[0].start_block);
- buf = get_buffer(probe, off + (cat_block * blocksize), 0x2000);
+ buf = get_buffer(probe, off + ((__u64) cat_block * blocksize), 0x2000);
if (!buf)
return 0;
@@ -1276,7 +1281,7 @@ static int probe_hfsplus(struct blkid_probe *probe,
if (ext == HFSPLUS_EXTENT_COUNT)
return 0;
- leaf_off = (ext_block_start + leaf_block) * blocksize;
+ leaf_off = (__u64) (ext_block_start + leaf_block) * blocksize;
buf = get_buffer(probe, off + leaf_off, leaf_node_size);
if (!buf)
@@ -1358,7 +1363,7 @@ static int probe_lvm2(struct blkid_probe *probe,
return 1;
}
- for (i=0, b=1, p=uuid, q= (char *) label->pv_uuid; i <= 32;
+ for (i=0, b=1, p=uuid, q= (char *) label->pv_uuid; i < LVM2_ID_LEN;
i++, b <<= 1) {
if (b & 0x4444440)
*p++ = '-';
@@ -1578,7 +1583,7 @@ try_again:
continue;
idx = id->bim_kboff + (id->bim_sboff >> 10);
- buf = get_buffer(&probe, idx << 10, 1024);
+ buf = get_buffer(&probe, (__u64) idx << 10, 1024);
if (!buf)
continue;
diff --git a/lib/blkid/save.c b/lib/blkid/save.c
index 6c20168b..762ffefd 100644
--- a/lib/blkid/save.c
+++ b/lib/blkid/save.c
@@ -94,8 +94,10 @@ int blkid_flush_cache(blkid_cache cache)
if (ret == 0 && S_ISREG(st.st_mode)) {
tmp = malloc(strlen(filename) + 8);
if (tmp) {
+ mode_t save_umask = umask(022);
sprintf(tmp, "%s-XXXXXX", filename);
fd = mkstemp(tmp);
+ umask(save_umask);
if (fd >= 0) {
file = fdopen(fd, "w");
opened = tmp;
@@ -134,7 +136,7 @@ int blkid_flush_cache(blkid_cache cache)
fclose(file);
if (opened != filename) {
if (ret < 0) {
- unlink(opened);
+ (void) unlink(opened);
DBG(DEBUG_SAVE,
printf("unlinked temp cache %s\n", opened));
} else {
@@ -144,10 +146,11 @@ int blkid_flush_cache(blkid_cache cache)
if (backup) {
sprintf(backup, "%s.old", filename);
unlink(backup);
- link(filename, backup);
+ (void) link(filename, backup);
free(backup);
}
- rename(opened, filename);
+ if (rename(opened, filename) < 0)
+ (void) unlink(opened);
DBG(DEBUG_SAVE,
printf("moved temp cache %s\n", opened));
}
diff --git a/lib/blkid/test_probe.in b/lib/blkid/test_probe.in
index 9b3edf52..a7b29a2e 100644
--- a/lib/blkid/test_probe.in
+++ b/lib/blkid/test_probe.in
@@ -21,11 +21,11 @@ do
echo "non-existent"
continue
fi
- if [ "$i" = "swap0" ]; then
+ if [ "$i" = "swap0" ] && which mkswap > /dev/null; then
# swap is native-endian, so regenerate before testing
dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null
mkswap -v0 $IMAGE > /dev/null
- elif [ "$i" = "swap1" ]; then
+ elif [ "$i" = "swap1" ] && which mkswap > /dev/null; then
# swap is native-endian, so regenerate before testing
dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null
# check if mkswap supports the "-U" option
diff --git a/lib/config.h.in b/lib/config.h.in
index f69eebe7..92b3c49b 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -26,6 +26,9 @@
/* Define to 1 if using `alloca.c'. */
#undef C_ALLOCA
+/* Define to 1 to disable use of backtrace */
+#undef DISABLE_BACKTRACE
+
/* Define to 1 if ext2 compression enabled */
#undef ENABLE_COMPRESSION
@@ -52,6 +55,9 @@
/* Define to 1 if you have the `backtrace' function. */
#undef HAVE_BACKTRACE
+/* Define to 1 if you have the `blkid_probe_enable_partitions' function. */
+#undef HAVE_BLKID_PROBE_ENABLE_PARTITIONS
+
/* Define to 1 if you have the `blkid_probe_get_topology' function. */
#undef HAVE_BLKID_PROBE_GET_TOPOLOGY
@@ -100,6 +106,9 @@
/* Define to 1 if Ext2 ioctls present */
#undef HAVE_EXT2_IOCTLS
+/* Define to 1 if you have the `fadvise64' function. */
+#undef HAVE_FADVISE64
+
/* Define to 1 if you have the `fallocate' function. */
#undef HAVE_FALLOCATE
@@ -118,6 +127,9 @@
/* Define to 1 if you have the `ftruncate64' function. */
#undef HAVE_FTRUNCATE64
+/* Define to 1 if you have the `futimes' function. */
+#undef HAVE_FUTIMES
+
/* Define to 1 if you have the `fwprintf' function. */
#undef HAVE_FWPRINTF
@@ -145,6 +157,9 @@
/* Define to 1 if you have the `getpagesize' function. */
#undef HAVE_GETPAGESIZE
+/* Define to 1 if you have the `getpwuid_r' function. */
+#undef HAVE_GETPWUID_R
+
/* Define to 1 if you have the `getrlimit' function. */
#undef HAVE_GETRLIMIT
@@ -191,6 +206,9 @@
/* Define to 1 if you have the <linux/fd.h> header file. */
#undef HAVE_LINUX_FD_H
+/* Define to 1 if you have the <linux/loop.h> header file. */
+#undef HAVE_LINUX_LOOP_H
+
/* Define to 1 if you have the <linux/major.h> header file. */
#undef HAVE_LINUX_MAJOR_H
@@ -275,6 +293,9 @@
/* Define to 1 if you have the `posix_fadvise' function. */
#undef HAVE_POSIX_FADVISE
+/* Define to 1 if you have the `posix_fadvise64' function. */
+#undef HAVE_POSIX_FADVISE64
+
/* Define to 1 if you have the `posix_memalign' function. */
#undef HAVE_POSIX_MEMALIGN
@@ -287,15 +308,15 @@
/* Define to 1 if you have the `putenv' function. */
#undef HAVE_PUTENV
-/* Define to 1 if you have the `quotactl' function. */
-#undef HAVE_QUOTACTL
-
/* Define to 1 if dirent has d_reclen */
#undef HAVE_RECLEN_DIRENT
/* Define to 1 if if struct sockaddr contains sa_len */
#undef HAVE_SA_LEN
+/* Define to 1 if you have the `secure_getenv' function. */
+#undef HAVE_SECURE_GETENV
+
/* Define to 1 if you have the <semaphore.h> header file. */
#undef HAVE_SEMAPHORE_H
@@ -311,6 +332,9 @@
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
+/* Define to 1 if you have the `setmntent' function. */
+#undef HAVE_SETMNTENT
+
/* Define to 1 if you have the `setresgid' function. */
#undef HAVE_SETRESGID
@@ -372,6 +396,9 @@
/* Define to 1 if you have the `strtoull' function. */
#undef HAVE_STRTOULL
+/* Define to 1 if `st_atim' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM
+
/* Define to 1 if you have the `sync_file_range' function. */
#undef HAVE_SYNC_FILE_RANGE
@@ -540,6 +567,9 @@
/* The size of `long long', as computed by sizeof. */
#undef SIZEOF_LONG_LONG
+/* The size of `off_t', as computed by sizeof. */
+#undef SIZEOF_OFF_T
+
/* The size of `short', as computed by sizeof. */
#undef SIZEOF_SHORT
@@ -608,4 +638,4 @@
<inttypes.h> don't define. */
#undef uintmax_t
-#include "dirpaths.h"
+#include <dirpaths.h>
diff --git a/lib/e2p/Makefile.in b/lib/e2p/Makefile.in
index e2d09402..7cdb88cb 100644
--- a/lib/e2p/Makefile.in
+++ b/lib/e2p/Makefile.in
@@ -55,6 +55,7 @@ BSDLIB_INSTALL_DIR = $(root_libdir)
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
@@ -81,7 +82,7 @@ check:: tst_ostype tst_feature
installdirs::
$(E) " MKINSTALLDIRS $(libdir) $(includedir)/e2p"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
- $(DESTDIR)$(includedir)/e2p $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(includedir)/e2p $(DESTDIR)$(pkgconfigdir)
install:: all installdirs
$(E) " INSTALL_DATA $(libdir)/libe2p.a"
@@ -92,12 +93,12 @@ install:: all installdirs
echo " INSTALL_DATA $(includedir)/e2p/$$i"; \
$(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/e2p/$$i; \
done
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/e2p.pc"
- $(Q) $(INSTALL_DATA) e2p.pc $(DESTDIR)$(libdir)/pkgconfig/e2p.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/e2p.pc"
+ $(Q) $(INSTALL_DATA) e2p.pc $(DESTDIR)$(pkgconfigdir)/e2p.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libe2p.a \
- $(DESTDIR)$(libdir)/pkgconfig/e2p.pc
+ $(DESTDIR)$(pkgconfigdir)/e2p.pc
$(RM) -rf $(DESTDIR)$(includedir)/e2p
clean::
diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
index 4a68dd9a..5fa41f46 100644
--- a/lib/e2p/e2p.h
+++ b/lib/e2p/e2p.h
@@ -36,7 +36,7 @@ int getflags (int fd, unsigned long * flags);
int getversion (int fd, unsigned long * version);
int iterate_on_dir (const char * dir_name,
int (*func) (const char *, struct dirent *, void *),
- void * private);
+ void * private_arg);
void list_super(struct ext2_super_block * s);
void list_super2(struct ext2_super_block * s, FILE *f);
void print_fs_errors (FILE * f, unsigned short errors);
diff --git a/lib/e2p/e2p.pc.in b/lib/e2p/e2p.pc.in
index 98ee9fb7..c171ae67 100644
--- a/lib/e2p/e2p.pc.in
+++ b/lib/e2p/e2p.pc.in
@@ -7,5 +7,5 @@ Name: e2p
Description: Ext2fs userpace programs utility library
Version: @E2FSPROGS_VERSION@
Requires:
-Cflags: -I${includedir}/e2p
+Cflags: -I${includedir}/e2p -I${includedir}
Libs: -L${libdir} -le2p
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 19e6f0cc..1d3e6896 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -43,6 +43,8 @@ static struct feature feature_list[] = {
"lazy_bg" },
{ E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP,
"snapshot_bitmap" },
+ { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_SPARSE_SUPER2,
+ "sparse_super2" },
{ E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
"sparse_super" },
@@ -87,6 +89,14 @@ static struct feature feature_list[] = {
"mmp" },
{ E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG,
"flex_bg"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE,
+ "ea_inode"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_DIRDATA,
+ "dirdata"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR,
+ "large_dir"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINEDATA,
+ "inline_data"},
{ 0, 0, 0 },
};
@@ -96,6 +106,8 @@ static struct feature jrnl_feature_list[] = {
{ E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE,
"journal_incompat_revoke" },
+ { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_64BIT,
+ "journal_64bit" },
{ E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT,
"journal_async_commit" },
{ 0, 0, 0 },
diff --git a/lib/e2p/getversion.c b/lib/e2p/getversion.c
index e7c6feca..71031ffc 100644
--- a/lib/e2p/getversion.c
+++ b/lib/e2p/getversion.c
@@ -31,7 +31,7 @@ int getversion (int fd, unsigned long * version)
r = ioctl (fd, EXT2_IOC_GETVERSION, &ver);
*version = ver;
- return 0;
+ return r;
#else /* ! HAVE_EXT2_IOCTLS */
extern int errno;
errno = EOPNOTSUPP;
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index f05e16dc..6f741c06 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -259,6 +259,8 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
else
fprintf(f, "Fragment size: %u\n",
EXT2_CLUSTER_SIZE(sb));
+ if (sb->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT)
+ fprintf(f, "Group descriptor size: %u\n", sb->s_desc_size);
if (sb->s_reserved_gdt_blocks)
fprintf(f, "Reserved GDT blocks: %u\n",
sb->s_reserved_gdt_blocks);
@@ -366,6 +368,14 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
fprintf(f, "type %u\n", sb->s_jnl_backup_type);
}
}
+ if (sb->s_backup_bgs[0] || sb->s_backup_bgs[1]) {
+ fprintf(f, "Backup block groups: ");
+ if (sb->s_backup_bgs[0])
+ fprintf(f, "%u ", sb->s_backup_bgs[0]);
+ if (sb->s_backup_bgs[1])
+ fprintf(f, "%u ", sb->s_backup_bgs[1]);
+ fputc('\n', f);
+ }
if (sb->s_snapshot_inum) {
fprintf(f, "Snapshot inode: %u\n",
sb->s_snapshot_inum);
diff --git a/lib/e2p/parse_num.c b/lib/e2p/parse_num.c
index d9ad3e74..e8d6283c 100644
--- a/lib/e2p/parse_num.c
+++ b/lib/e2p/parse_num.c
@@ -35,10 +35,16 @@ unsigned long long parse_num_blocks2(const char *arg, int log_block_size)
num <<= 10;
/* fallthrough */
case 'K': case 'k':
- num >>= log_block_size;
+ if (log_block_size < 0)
+ num <<= 10;
+ else
+ num >>= log_block_size;
break;
case 's':
- num >>= (1+log_block_size);
+ if (log_block_size < 0)
+ num <<= 9;
+ else
+ num >>= (1+log_block_size);
break;
case '\0':
break;
@@ -62,11 +68,21 @@ main(int argc, char **argv)
unsigned long num;
int log_block_size = 0;
- if (argc != 2) {
- fprintf(stderr, "Usage: %s arg\n", argv[0]);
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage: %s arg [log_block_size]\n", argv[0]);
exit(1);
}
+ if (argc == 3) {
+ char *p;
+
+ log_block_size = strtol(argv[2], &p, 0);
+ if (*p) {
+ fprintf(stderr, "Bad log_block_size: %s\n", argv[2]);
+ exit(1);
+ }
+ }
+
num = parse_num_blocks(argv[1], log_block_size);
printf("Parsed number: %lu\n", num);
diff --git a/lib/e2p/pf.c b/lib/e2p/pf.c
index f03193c0..e2f8ce50 100644
--- a/lib/e2p/pf.c
+++ b/lib/e2p/pf.c
@@ -49,6 +49,7 @@ static struct flags_name flags_array[] = {
{ EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" },
{ EXT4_EXTENTS_FL, "e", "Extents" },
{ EXT4_HUGE_FILE_FL, "h", "Huge_file" },
+ { FS_NOCOW_FL, "C", "No_COW" },
{ 0, NULL, NULL }
};
diff --git a/lib/et/Makefile.in b/lib/et/Makefile.in
index 26a33608..4441e1f8 100644
--- a/lib/et/Makefile.in
+++ b/lib/et/Makefile.in
@@ -43,6 +43,7 @@ BSDLIB_INSTALL_DIR = $(root_libdir)
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
@@ -59,15 +60,34 @@ compile_et: $(DEP_SUBSTITUTE) $(srcdir)/compile_et.sh.in
$(Q) $(SUBSTITUTE) $(srcdir)/compile_et.sh.in compile_et
$(Q) $(CHMOD) +x compile_et
+DVI=texi2dvi
+DVIPS=dvips -o "$@"
+INFO=@MAKEINFO@
+HTML=makeinfo --html --no-split
+PS2PDF=ps2pdf
+
com_err.ps : com_err.dvi
com_err.dvi: com_err.texinfo
-com_err_abt.html: $(srcdir)/com_err.texinfo
- $(E) " TEXI2HTML $@"
- -$(Q) texi2html -split_chapter $(srcdir)/com_err.texinfo
- -$(Q) if test -d com_err ; then \
- mv com_err/* . ; rmdir com_err ; \
- fi
+com_err.info: $(srcdir)/com_err.texinfo
+ $(E) " MAKEINFO $@"
+ -$(Q) $(INFO) $(srcdir)/com_err.texinfo
+
+com_err.dvi: $(srcdir)/com_err.texinfo
+ $(E) " TEXI2DVI $@"
+ -$(Q) $(DVI) $(srcdir)/com_err.texinfo
+
+com_err.ps: com_err.dvi
+ $(E) " DVIPS $@"
+ -$(Q) $(DVIPS) com_err.dvi
+
+com_err.pdf: com_err.ps
+ $(E) " PS2PDF $@"
+ -$(Q) $(PS2PDF) com_err.ps
+
+com_err.html: $(srcdir)/com_err.texinfo
+ $(E) " MAKEINFO $@"
+ -$(Q) $(HTML) $(srcdir)/com_err.texinfo
com_err.pc: $(srcdir)/com_err.pc.in $(top_builddir)/config.status
$(E) " CONFIG.STATUS $@"
@@ -85,7 +105,7 @@ installdirs::
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
$(DESTDIR)$(includedir)/et $(DESTDIR)$(datadir)/et \
$(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) \
- $(DESTDIR)$(man3dir) $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(man3dir) $(DESTDIR)$(pkgconfigdir)
install:: compile_et libcom_err.a $(HFILES) installdirs com_err.pc
$(E) " INSTALL_DATA $(libdir)/libcom_err.a"
@@ -109,13 +129,13 @@ install:: compile_et libcom_err.a $(HFILES) installdirs com_err.pc
$(E) " INSTALL_DATA $(man1dir)/compile_et.1"
$(Q) $(INSTALL_DATA) $(srcdir)/compile_et.1 \
$(DESTDIR)$(man1dir)/compile_et.1
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/com_err.pc"
- $(Q) $(INSTALL_DATA) com_err.pc $(DESTDIR)$(libdir)/pkgconfig/com_err.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/com_err.pc"
+ $(Q) $(INSTALL_DATA) com_err.pc $(DESTDIR)$(pkgconfigdir)/com_err.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libcom_err.a \
$(DESTDIR)$(bindir)/compile_et \
- $(DESTDIR)$(libdir)/pkgconfig/com_err.pc
+ $(DESTDIR)$(pkgconfigdir)/com_err.pc
$(RM) -rf $(DESTDIR)$(includedir)/et $(DESTDIR)$(datadir)/et
check:: compile_et
@@ -133,7 +153,9 @@ check:: compile_et
clean::
$(RM) -f compile_et libcom_err.a libcom_err_p.a com_err.info
$(RM) -f $(OBJS) profiled/*
- $(RM) -f *~ \#* *.bak *.otl *.aux *.toc *.PS *.dvi *.ps TAGS *.ln *.html
+ $(RM) -f *~ \#* *.bak *.otl *.aux *.toc *.PS *.dvi *.ps TAGS *.ln \
+ *.html *.cp *.fn *.fns *.ky *.log *.pc *.pg *.toc *.tp *.vr \
+ *.pdf
$(RM) -f ../libcom_err.a ../libcom_err_p.a
mostlyclean:: clean
diff --git a/lib/et/com_err.c b/lib/et/com_err.c
index 4ea52def..d38998a2 100644
--- a/lib/et/com_err.c
+++ b/lib/et/com_err.c
@@ -85,8 +85,7 @@ void com_err (const char *whoami,
va_end(pvar);
}
-errf set_com_err_hook (new_proc)
- errf new_proc;
+errf set_com_err_hook(errf new_proc)
{
errf x = com_err_hook;
@@ -98,7 +97,7 @@ errf set_com_err_hook (new_proc)
return x;
}
-errf reset_com_err_hook () {
+errf reset_com_err_hook(void) {
errf x = com_err_hook;
com_err_hook = default_com_err_proc;
return x;
diff --git a/lib/et/com_err.pc.in b/lib/et/com_err.pc.in
index 772efa1c..86df8a2a 100644
--- a/lib/et/com_err.pc.in
+++ b/lib/et/com_err.pc.in
@@ -7,6 +7,6 @@ Name: com_err
Description: Common error description library
Version: @E2FSPROGS_VERSION@
Requires:
-Cflags: -I${includedir}/et
+Cflags: -I${includedir}/et -I${includedir}
Libs: -L${libdir} -lcom_err
Libs.private: @SEM_INIT_LIB@
diff --git a/lib/et/com_err.texinfo b/lib/et/com_err.texinfo
index 7e6a4944..c9d3fcaf 100644
--- a/lib/et/com_err.texinfo
+++ b/lib/et/com_err.texinfo
@@ -14,7 +14,7 @@
@c Software Foundation, and is under different copyright restrictions
@c from the rest of this package.)
-@setfilename com_err
+@setfilename com_err.info
@settitle A Common Error Description Library for UNIX
@ifinfo
@@ -24,14 +24,10 @@
@end direntry
@end ifinfo
-@iftex
-@tolerance 10000
+@c smallbook
-@c Mutate section headers...
-@begingroup
- @catcode#=6
- @gdef@secheading#1#2#3{@secheadingi {#3@enspace #1}}
-@endgroup
+@iftex
+@finalout
@end iftex
@ifinfo
@@ -61,6 +57,7 @@ notice identical to this one except for the removal of this paragraph
(this paragraph not being relevant to the printed manual).
@end ignore
+@end ifinfo
@setchapternewpage odd
@@ -114,7 +111,6 @@ from the remainder of this package.
@end titlepage
-
@node Top, Why com_err?, (dir), (dir)
@top A Common Error Description Library for UNIX
@@ -133,12 +129,10 @@ This manual documents the com_err library.
* Acknowledgements::
@end menu
-@end ifinfo
-
@page
@node Why com_err?, Error codes, Top, Top
-@section Why com_err?
+@chapter Why com_err?
In building application software packages, a programmer often has to
deal with a number of libraries, each of which can use a different
@@ -174,7 +168,7 @@ of the form @samp{unknown code foo 32}, where @samp{foo} would be the name
of the table.
@node Error codes, Error table source file, Why com_err?, Top
-@section Error codes
+@chapter Error codes
Error codes themselves are 32 bit (signed) integers, of which the high
order 24 bits are an identifier of which error table the error code is
@@ -203,7 +197,7 @@ much other software that assumes an ANSI-C environment base) without
significant effort.
@node Error table source file, The error-table compiler, Error codes, Top
-@section Error table source file
+@chapter Error table source file
The error table source file begins with the declaration of the table name,
as
@@ -241,7 +235,7 @@ error table might be:
@end example
@node The error-table compiler, Run-time support routines, Error table source file, Top
-@section The error-table compiler
+@chapter The error-table compiler
The error table compiler is named @code{compile_et}. It takes one
argument, the pathname of a file (ending in @samp{.et}, e.g.,
@@ -255,7 +249,7 @@ codes defined; the object module generated from the C code may be linked in
to a program which wishes to use the printed forms of the error codes.
@node Run-time support routines, Coding Conventions, The error-table compiler, Top
-@section Run-time support routines
+@chapter Run-time support routines
Any source file which uses the routines supplied with or produced by the
com_err package should include the header file @file{<com_err.h>}. It
@@ -306,9 +300,7 @@ left to circumstances which render @code{com_err} (below) unusable.
@end deftypefun
-@deftypefun
-void com_err (const char *@var{whoami}, long @var{error_code},
- const char *@var{format}, ...);
+@deftypefun void com_err (const char *@var{whoami}, long @var{error_code}, const char *@var{format}, ...);
This routine provides an alternate way to print error messages to
standard error; it allows the error message to be passed in as a
@@ -321,8 +313,7 @@ printed. @var{format} may not be omitted.
@end deftypefun
-@deftypefun
-void com_err_va (const char *@var{whoami}, long @var{error_code}, const char *@var{format}, va_list @var{args});
+@deftypefun void com_err_va (const char *@var{whoami}, long @var{error_code}, const char *@var{format}, va_list @var{args});
This routine provides an interface, equivalent to @code{com_err} above,
which may be used by higher-level variadic functions (functions which
@@ -330,7 +321,7 @@ accept variable numbers of arguments).
@end deftypefun
-@deftypefun void (*set_com_err_hook (void (*@var{proc}) (const char *@var{whoami}, long @var{error_code}, va_list @var{args}))) (const char *@var{whoami}, long @var{error_code}, va_list @var{args});
+@deftypefun void *set_com_err_hook (void (*@var{proc}) (const char *@var{whoami}, long @var{error_code}, va_list @var{args}) (const char *@var{whoami}, long @var{error_code}, va_list @var{args}));
@deftypefunx void reset_com_err_hook ();
@@ -390,7 +381,7 @@ the ANSI C library).
@end deftypefun
@node Coding Conventions, Building and Installation, Run-time support routines, Top
-@section Coding Conventions
+@chapter Coding Conventions
The following conventions are just some general stylistic conventions
to follow when writing robust libraries and programs. Conventions
@@ -501,7 +492,7 @@ error:
@end example
@node Building and Installation, Bug Reports, Coding Conventions, Top
-@section Building and Installation
+@chapter Building and Installation
The distribution of this package will probably be done as a compressed
``tar''-format file available via anonymous FTP from SIPB.MIT.EDU.
@@ -514,7 +505,7 @@ installed for use; @samp{com_err.3} and @samp{compile_et.1} can also be
installed as manual pages.
@node Bug Reports, Acknowledgements, Building and Installation, Top
-@section Bug Reports
+@chapter Bug Reports
The principal author of this library is: Ken
Raeburn, @t{raeburn@@MIT.EDU}.
@@ -524,7 +515,7 @@ Ts'o, and so bugs and comments should be sent to @t{tytso@@thunk.org}.
@node Acknowledgements, , Bug Reports, Top
-@section Acknowledgements
+@chapter Acknowledgements
I would like to thank: Bill Sommerfeld, for his help with some of this
documentation, and catching some of the bugs the first time around;
diff --git a/lib/et/error_message.c b/lib/et/error_message.c
index dc77b69f..5d8d2422 100644
--- a/lib/et/error_message.c
+++ b/lib/et/error_message.c
@@ -17,6 +17,9 @@
*/
#include "config.h"
+#if HAVE_SECURE_GETENV
+#define _GNU_SOURCE
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -102,8 +105,7 @@ typedef char *(*gettextf) (const char *);
static gettextf com_err_gettext = NULL;
-gettextf set_com_err_gettext (new_proc)
- gettextf new_proc;
+gettextf set_com_err_gettext(gettextf new_proc)
{
gettextf x = com_err_gettext;
@@ -209,7 +211,9 @@ static char *safe_getenv(const char *arg)
#endif
#endif
-#ifdef HAVE___SECURE_GETENV
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(arg);
#else
return getenv(arg);
diff --git a/lib/et/error_table.h b/lib/et/error_table.h
index 4f109911..24e4762a 100644
--- a/lib/et/error_table.h
+++ b/lib/et/error_table.h
@@ -18,7 +18,7 @@ struct et_list {
struct et_list *next;
const struct error_table *table;
};
-extern struct et_list * _et_list;
+extern struct et_list *_et_list, *_et_dynamic_list;
#define ERRCODE_RANGE 8 /* # of bits to shift table number */
#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
diff --git a/lib/et/et_name.c b/lib/et/et_name.c
index 6b59eddb..d9a3e874 100644
--- a/lib/et/et_name.c
+++ b/lib/et/et_name.c
@@ -21,8 +21,7 @@ static const char char_set[] =
static char buf[6];
-const char * error_table_name(num)
- errcode_t num;
+const char * error_table_name(errcode_t num)
{
int ch;
int i;
diff --git a/lib/et/texinfo.tex b/lib/et/texinfo.tex
index 838160c9..dddd0140 100644
--- a/lib/et/texinfo.tex
+++ b/lib/et/texinfo.tex
@@ -1,209 +1,373 @@
-%% TeX macros to handle texinfo files
-
-% Copyright (C) 1985, 1986, 1988 Richard M. Stallman
-
-% NO WARRANTY
-
-% BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
-%NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
-%WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
-%RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
-%WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-%BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-%FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
-%AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
-%DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
-%CORRECTION.
-
-% IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
-%STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
-%WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
-%LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
-%OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-%USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
-%DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
-%A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
-%PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
-%DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
-
-% GENERAL PUBLIC LICENSE TO COPY
-
-% 1. You may copy and distribute verbatim copies of this source file
-%as you receive it, in any medium, provided that you conspicuously
-%and appropriately publish on each copy a valid copyright notice
-%"Copyright (C) 1986 Richard M. Stallman"; and include
-%following the copyright notice a verbatim copy of the above disclaimer
-%of warranty and of this License.
-
-% 2. You may modify your copy or copies of this source file or
-%any portion of it, and copy and distribute such modifications under
-%the terms of Paragraph 1 above, provided that you also do the following:
-
-% a) cause the modified files to carry prominent notices stating
-% that you changed the files and the date of any change; and
-
-% b) cause the whole of any work that you distribute or publish,
-% that in whole or in part contains or is a derivative of this
-% program or any part thereof, to be licensed at no charge to all
-% third parties on terms identical to those contained in this
-% License Agreement (except that you may choose to grant more extensive
-% warranty protection to some or all third parties, at your option).
-
-% c) You may charge a distribution fee for the physical act of
-% transferring a copy, and you may at your option offer warranty
-% protection in exchange for a fee.
-
-%Mere aggregation of another unrelated program with this program (or its
-%derivative) on a volume of a storage or distribution medium does not bring
-%the other program under the scope of these terms.
-
-% 3. You may copy and distribute this program (or a portion or derivative
-%of it, under Paragraph 2) in object code or executable form under the terms
-%of Paragraphs 1 and 2 above provided that you also do one of the following:
-
-% a) accompany it with the complete corresponding machine-readable
-% source code, which must be distributed under the terms of
-% Paragraphs 1 and 2 above; or,
-
-% b) accompany it with a written offer, valid for at least three
-% years, to give any third party free (except for a nominal
-% shipping charge) a complete machine-readable copy of the
-% corresponding source code, to be distributed under the terms of
-% Paragraphs 1 and 2 above; or,
-
-% c) accompany it with the information you received as to where the
-% corresponding source code may be obtained. (This alternative is
-% allowed only for noncommercial distribution and only if you
-% received the program in object code or executable form alone.)
-
-%For an executable file, complete source code means all the source code for
-%all modules it contains; but, as a special exception, it need not include
-%source code for modules which are standard libraries that accompany the
-%operating system on which the executable file runs.
-
-% 4. You may not copy, sublicense, distribute or transfer this program
-%except as expressly provided under this License Agreement. Any attempt
-%otherwise to copy, sublicense, distribute or transfer this program is void and
-%your rights to use the program under this License agreement shall be
-%automatically terminated. However, parties who have received computer
-%software programs from you with this License Agreement will not have
-%their licenses terminated so long as such parties remain in full compliance.
-
-% 5. If you wish to incorporate parts of this program into other free
-%programs whose distribution conditions are different, write to the Free
-%Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet
-%worked out a simple rule that can be stated here, but we will often permit
-%this. We will be guided by the two goals of preserving the free status of
-%all derivatives of our free software and of promoting the sharing and reuse of
-%software.
-
-%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!
-
-\def\texinfoversion{1.18}
-\message{Loading texinfo package [Version \texinfoversion]:}
-\message{}
-
-% Save some parts of plain tex whose names we will redefine.
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2006-02-13.16}
+%
+% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor,
+% Boston, MA 02110-1301, USA.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction. (This has been our intent since Texinfo was invented.)
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
+% ftp://tug.org/tex/texinfo.tex
+% (and all CTAN mirrors, see http://www.ctan.org).
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org. Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem. Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution. For a simple
+% manual foo.texi, however, you can get away with this:
+% tex foo.texi
+% texindex foo.??
+% tex foo.texi
+% tex foo.texi
+% dvips foo.dvi -o # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent. You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
-\let\ptexlbrace=\{
-\let\ptexrbrace=\}
-\let\ptexdot=\.
-\let\ptexstar=\*
-\let\ptexend=\end
-\let\ptexbullet=\bullet
+
+\message{Loading texinfo [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]}%
+ \catcode`+=\active \catcode`\_=\active}
+
+\message{Basics,}
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros 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\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexstar=\*
\let\ptext=\t
-\let\ptexl=\l
-\let\ptexL=\L
-\def\tie{\penalty 10000\ } % Save plain tex definition of ~.
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
-\message{Basics,}
-\chardef\other=12
+% 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 % Pre-3.0.
+\else
+ \def\linenumber{l.\the\inputlineno:\space}
+\fi
-\hyphenation{ap-pen-dix}
-\hyphenation{mini-buf-fer mini-buf-fers}
-\hyphenation{eshell}
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi
+\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi
+
+% Since the category of space is not known, we have to be careful.
+\chardef\spacecat = 10
+\def\spaceisspace{\catcode`\ =\spacecat}
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+ ap-pen-dix bit-map bit-maps
+ data-base data-bases eshell fall-ing half-way long-est man-u-script
+ man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+ par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+ spell-ing spell-ings
+ stand-alone strong-est time-stamp time-stamps which-ever white-space
+ wide-spread wrap-around
+}
% Margin to add to right of even pages, to left of odd pages.
-\newdimen \bindingoffset \bindingoffset=0pt
-\newdimen \normaloffset \normaloffset=\hoffset
+\newdimen\bindingoffset
+\newdimen\normaloffset
\newdimen\pagewidth \newdimen\pageheight
-\pagewidth=\hsize \pageheight=\vsize
-%---------------------Begin change-----------------------
+% 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).
%
-% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986
+\def\finalout{\overfullrule=0pt}
+
+% @| 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).
%
-\newdimen\cornerlong \newdimen\cornerthick
-\newdimen \topandbottommargin
-\newdimen \outerhsize \newdimen \outervsize
-\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks
-\outerhsize=7in
-\outervsize=9.5in
-\topandbottommargin=.75in
+\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
+ }%
+ }%
+}
+
+% 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. We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
%
-%---------------------End change-----------------------
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+ \tracingstats2
+ \tracingpages1
+ \tracinglostchars2 % 2 gives us more in etex
+ \tracingparagraphs1
+ \tracingoutput1
+ \tracingmacros2
+ \tracingrestores1
+ \showboxbreadth\maxdimen \showboxdepth\maxdimen
+ \ifx\eTeXversion\undefined\else % etex gives us more logging
+ \tracingscantokens1
+ \tracingifs1
+ \tracinggroups1
+ \tracingnesting2
+ \tracingassigns1
+ \fi
+ \tracingcommands3 % 3 gives us more in etex
+ \errorcontextlines16
+}%
+
+% add check for \lastpenalty to plain's definitions. If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+ \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+ \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+ \removelastskip\penalty-200\bigskip\fi\fi}
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% 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 itself, but you have to call it yourself.
-\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}}
-\def\onepageout#1{\hoffset=\normaloffset
-\ifodd\pageno \advance\hoffset by \bindingoffset
-\else \advance\hoffset by -\bindingoffset\fi
-\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
- {\let\hsize=\pagewidth \makefootline}}
-\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
-
-
-% 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
- \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 }
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+ \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+ %
+ \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.
+ %
+ \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.
+ % We don't want .vr (or whatever) entries like this:
+ % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
+ % "\acronym" won't work when it's read back in;
+ % it needs to be
+ % {\code {{\tt \backslashcurfont }acronym}
+ \shipout\vbox{%
+ % Do this early so pdf references go to the beginning of the page.
+ \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+ %
+ \ifcropmarks \vbox to \outervsize\bgroup
+ \hsize = \outerhsize
+ \vskip-\topandbottommargin
+ \vtop to0pt{%
+ \line{\ewtop\hfil\ewtop}%
+ \nointerlineskip
+ \line{%
+ \vbox{\moveleft\cornerthick\nstop}%
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}%
+ }%
+ \vss}%
+ \vskip\topandbottommargin
+ \line\bgroup
+ \hfil % center the page within the outer (page) hsize.
+ \ifodd\pageno\hskip\bindingoffset\fi
+ \vbox\bgroup
+ \fi
+ %
+ \unvbox\headlinebox
+ \pagebody{#1}%
+ \ifdim\ht\footlinebox > 0pt
+ % Only leave this space if the footline is nonempty.
+ % (We lessened \vsize for it in \oddfootingxxx.)
+ % The \baselineskip=24pt in plain's \makefootline has no effect.
+ \vskip 2\baselineskip
+ \unvbox\footlinebox
+ \fi
+ %
+ \ifcropmarks
+ \egroup % end of \vbox\bgroup
+ \hfil\egroup % end of (centering) \line\bgroup
+ \vskip\topandbottommargin plus1fill minus1fill
+ \boxmaxdepth = \cornerthick
+ \vbox to0pt{\vss
+ \line{%
+ \vbox{\moveleft\cornerthick\nsbot}%
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}%
+ }%
+ \nointerlineskip
+ \line{\ewbot\hfil\ewbot}%
+ }%
+ \egroup % \vbox from first cropmarks clause
+ \fi
+ }% end of \shipout\vbox
+ }% end of group with \indexdummies
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\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)
@@ -215,317 +379,1891 @@
\def\nsbot{\vbox
{\hrule height\cornerlong depth\cornerthick width\cornerthick}}
-% Parse an argument, then pass it to #1.
-% The argument can be delimited with [...] or with "..." or braces
-% or it can be a whole line.
-% #1 should be a macro which expects
-% an ordinary undelimited TeX argument.
-
-\def\parsearg #1{\let\next=#1\begingroup\obeylines\futurelet\temp\parseargx}
+% 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{\parseargusing{}}
+\def\parseargusing#1#2{%
+ \def\next{#2}%
+ \begingroup
+ \obeylines
+ \spaceisspace
+ #1%
+ \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
-\def\parseargx{%
-\ifx \obeyedspace\temp \aftergroup\parseargdiscardspace \else%
-\aftergroup \parseargline %
-\fi \endgroup}
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ \argremovecomment #1\comment\ArgTerm%
+ }%
+}
-{\obeyspaces %
-\gdef\parseargdiscardspace {\begingroup\obeylines\futurelet\temp\parseargx}}
+% First remove any @comment, then any @c comment.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
-\gdef\obeyedspace{\ }
+% Each occurence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+% @end itemize @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+ \def\temp{#3}%
+ \ifx\temp\empty
+ % We cannot use \next here, as it holds the macro to run;
+ % thus we reuse \temp.
+ \let\temp\finishparsearg
+ \else
+ \let\temp\argcheckspaces
+ \fi
+ % Put the space token in:
+ \temp#1 #3\ArgTerm
+}
-\def\parseargline{\begingroup \obeylines \parsearglinex}
-{\obeylines %
-\gdef\parsearglinex #1^^M{\endgroup \next {#1}}}
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \next.
+% (Similarily, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\next\expandafter{#1}}
-\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+% \parseargdef\foo{...}
+% is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+%
+% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
+% favourite TeX trick. --kasal, 16nov03
-%% 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
+\def\parseargdef#1{%
+ \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+ \def#2{\parsearg#1}%
+ \def#1##1%
+}
-% @begin foo is the same as @foo, for now.
-\newhelp\EMsimple{Type <Return> to continue}
+% Several utility definitions with active space:
+{
+ \obeyspaces
+ \gdef\obeyedspace{ }
+
+ % 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.
+ %
+ \gdef\sepspaces{\obeyspaces\let =\tie}
+
+ % 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 \ ).
+ \gdef\unsepspaces{\let =\space}
+}
-\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}
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
-%% @end foo executes the definition of \Efoo.
-%% foo can be delimited by doublequotes or brackets.
+% Define the framework for environments in texinfo.tex. It's used like this:
+%
+% \envdef\foo{...}
+% \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo. \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches. The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as enviroments; they don't open a group. (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At runtime, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+ \def\temp{#1}%
+ \ifx\thisenv\temp
+ \else
+ \badenverr
+ \fi
+}
-\def\end{\parsearg\endxxx}
+% Evironment mismatch, #1 expected:
+\def\badenverr{%
+ \errhelp = \EMsimple
+ \errmessage{This command can appear only \inenvironment\temp,
+ not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+ \ifx#1\empty
+ out of any environment%
+ \else
+ in environment \expandafter\string#1%
+ \fi
+}
-\def\endxxx #1{%
-\expandafter\ifx\csname E#1\endcsname\relax
-\expandafter\ifx\csname #1\endcsname\relax
-\errmessage{Undefined command @end #1}\else
-\errorE{#1}\fi\fi
-\csname E#1\endcsname}
-\def\errorE#1{
-{\errhelp=\EMsimple \errmessage{@end #1 not within #1 environment}}}
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+ \if 1\csname iscond.#1\endcsname
+ \else
+ % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03
+ \expandafter\checkenv\csname#1\endcsname
+ \csname E#1\endcsname
+ \endgroup
+ \fi
+}
-% Single-spacing is done by various environments.
+\newhelp\EMsimple{Press RETURN to continue.}
-\newskip\singlespaceskip \singlespaceskip = \baselineskip
-\def\singlespace{%
-{\advance \baselineskip by -\singlespaceskip
-\kern \baselineskip}%
-\baselineskip=\singlespaceskip
-}
%% Simple single-character @ commands
% @@ prints an @
% Kludge this until the fonts are right (grr).
-\def\@{{\sf \char '100}}
+\def\@{{\tt\char64}}
-% Define @` and @' to be the same as ` and '
-% but suppressing ligatures.
-\def\`{{`}}
-\def\'{{'}}
+% 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}}
+\def\mylbrace {{\tt\char123}}
+\def\myrbrace {{\tt\char125}}
\let\{=\mylbrace
\let\}=\myrbrace
+\begingroup
+ % Definitions to produce \{ and \} commands for indices,
+ % and @{ and @} for the aux/toc files.
+ \catcode`\{ = \other \catcode`\} = \other
+ \catcode`\[ = 1 \catcode`\] = 2
+ \catcode`\! = 0 \catcode`\\ = \other
+ !gdef!lbracecmd[\{]%
+ !gdef!rbracecmd[\}]%
+ !gdef!lbraceatcmd[@{]%
+ !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @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 @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% 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
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence. (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo. Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+ L\kern-.36em
+ {\setbox0=\hbox{T}%
+ \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}%
+ \kern-.15em
+ \TeX
+}
+
+% 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\ }
+}
% @: forces normal size whitespace following.
\def\:{\spacefactor=1000 }
% @* forces a line break.
-\def\*{\hfil\break}
-
-% @. is an end-of-sentence period.
-\def\.{.\spacefactor=3000 }
+\def\*{\hfil\break\hbox{}\ignorespaces}
-% @w prevents a word break
-\def\w #1{\hbox{#1}}
+% @/ allows a line break.
+\let\/=\allowbreak
-% @group ... @end group forces ... to be all on one page.
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
-\def\group{\begingroup% \inENV ???
-\def \Egroup{\egroup\endgroup}
-\vbox\bgroup}
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
-% @br forces paragraph break
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
-\let\br = \par
+% @frenchspacing on|off says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+ \def\temp{#1}%
+ \ifx\temp\onword \plainfrenchspacing
+ \else\ifx\temp\offword \plainnonfrenchspacing
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @frenchspacing option `\temp', must be on/off}%
+ \fi\fi
+}
-% @dots{} output some dots
+% @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.
+%
+% Another complication is that the group might be very large. This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material. In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom. The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+ \ifnum\catcode`\^^M=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ \startsavinginserts
+ %
+ \setbox\groupbox = \vtop\bgroup
+ % 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
+}
+%
+% The \vtop 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. Thus, space below is not quite equal to space
+% above. But it's pretty close.
+\def\Egroup{%
+ % To get correct interline space between the last line of the group
+ % and the first line afterwards, we have to propagate \prevdepth.
+ \endgraf % Not \par, as it may have been set to \lisppar.
+ \global\dimen1 = \prevdepth
+ \egroup % End the \vtop.
+ % \dimen0 is the vertical size of the group's box.
+ \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox
+ % \dimen2 is how much space is left on the page (more or less).
+ \dimen2 = \pageheight \advance\dimen2 by -\pagetotal
+ % if the group doesn't fit on the current page, and it's a big big
+ % group, force a page break.
+ \ifdim \dimen0 > \dimen2
+ \ifdim \pagetotal < \vfilllimit\pageheight
+ \page
+ \fi
+ \fi
+ \box\groupbox
+ \prevdepth = \dimen1
+ \checkinserts
+}
+%
+% 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
+
+% Old definition--didn't work.
+%\parseargdef\need{\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\nobreak
+%\prevdepth=-1000pt
+%}}
+
+\parseargdef\need{%
+ % Ensure vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % If the @need value is less than one line space, it's useless.
+ \dimen0 = #1\mil
+ \dimen2 = \ht\strutbox
+ \advance\dimen2 by \dp\strutbox
+ \ifdim\dimen0 > \dimen2
+ %
+ % Do a \strut just to make the height of this box be normal, so the
+ % normal leading is inserted relative to the preceding line.
+ % And a page break here is fine.
+ \vtop to #1\mil{\strut\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
+ \fi
+}
-\def\dots{$\ldots$}
+% @br forces paragraph break (and is undocumented).
-% @page forces the start of a new page
+\let\br = \par
+% @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
-\def\exdent{\errmessage{@exdent in filled text}}
- % @lisp, etc, define \exdent locally from \internalexdent
+% 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.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
-{\obeyspaces
-\gdef\internalexdent{\parsearg\exdentzzz}}
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+ \leftline{\hskip\leftskip{\rm#1}}}}
-\def\exdentzzz #1{{\advance \leftskip by -\lispnarrowing
-\advance \hsize by -\leftskip
-\advance \hsize by -\rightskip
-\leftline{{\rm#1}}}}
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph. For more general purposes, use the \margin insertion
+% class. WHICH is `l' or `r'.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+ \nobreak
+ \kern-\strutdepth
+ \vtop to \strutdepth{%
+ \baselineskip=\strutdepth
+ \vss
+ % if you have multiple lines of stuff to put here, you'll need to
+ % make the vbox yourself of the appropriate size.
+ \ifx#1l%
+ \llap{\ignorespaces #2\hskip\inmarginspacing}%
+ \else
+ \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+ \fi
+ \null
+ }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \def\lefttext{#1}% have both texts
+ \def\righttext{#2}%
+ \else
+ \def\lefttext{#1}% have only one text
+ \def\righttext{#1}%
+ \fi
+ %
+ \ifodd\pageno
+ \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+ \else
+ \def\temp{\inleftmargin\lefttext}%
+ \fi
+ \temp
+}
% @include file insert text of that file as input.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+ \pushthisfilestack
+ \def\thisfile{#1}%
+ {%
+ \makevalueexpandable
+ \def\temp{\input #1 }%
+ \expandafter
+ }\temp
+ \popthisfilestack
+}
+\def\filenamecatcodes{%
+ \catcode`\\=\other
+ \catcode`~=\other
+ \catcode`^=\other
+ \catcode`_=\other
+ \catcode`|=\other
+ \catcode`<=\other
+ \catcode`>=\other
+ \catcode`+=\other
+ \catcode`-=\other
+}
-\def\include{\parsearg\includezzz}
-\def\includezzz #1{{\def\thisfile{#1}\input #1
-}}
+\def\pushthisfilestack{%
+ \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+ \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+ \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
-\def\thisfile{}
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+ the stack of filenames is empty.}}
-% @center line outputs that line, centered
+\def\thisfile{}
-\def\center{\parsearg\centerzzz}
-\def\centerzzz #1{{\advance\hsize by -\leftskip
-\advance\hsize by -\rightskip
-\centerline{#1}}}
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+ \ifhmode
+ \let\next\centerH
+ \else
+ \let\next\centerV
+ \fi
+ \next{\hfil \ignorespaces#1\unskip \hfil}%
+}
+\def\centerH#1{%
+ {%
+ \hfil\break
+ \advance\hsize by -\leftskip
+ \advance\hsize by -\rightskip
+ \line{#1}%
+ \break
+ }%
+}
+\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}}
% @sp n outputs n lines of vertical space
-\def\sp{\parsearg\spxxx}
-\def\spxxx #1{\par \vskip #1\baselineskip}
+\parseargdef\sp{\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{\parsearg \commentxxx}
-
-\def\commentxxx #1{}
+\def\comment{\begingroup \catcode`\^^M=\other%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\commentxxx}
+{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
\let\c=\comment
-\long\def\ignore #1\end ignore{}
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \defaultparindent = 0pt
+ \else
+ \defaultparindent = #1em
+ \fi
+ \fi
+ \parindent = \defaultparindent
+}
-\outer\def\ifset{\parsearg\ifsetxxx}
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \lispnarrowing = 0pt
+ \else
+ \lispnarrowing = #1em
+ \fi
+ \fi
+}
-\def\ifsetxxx #1#2\end ifset{%
-\expandafter\ifx\csname IF#1\endcsname\relax \else #2\fi}
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading. If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\noneword
+ \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+ \else\ifx\temp\insertword
+ \let\suppressfirstparagraphindent = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @firstparagraphindent option `\temp'}%
+ \fi\fi
+}
-\outer\def\ifclear{\parsearg\ifclearxxx}
+% Here is how we actually suppress indentation. Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+ \gdef\indent{%
+ \restorefirstparagraphindent
+ \indent
+ }%
+ \gdef\noindent{%
+ \restorefirstparagraphindent
+ \noindent
+ }%
+ \global\everypar = {%
+ \kern -\parindent
+ \restorefirstparagraphindent
+ }%
+}
-\def\ifclearxxx #1#2\end ifclear{%
-\expandafter\ifx\csname IF#1\endcsname\relax #2\fi}
+\gdef\restorefirstparagraphindent{%
+ \global \let \indent = \ptexindent
+ \global \let \noindent = \ptexnoindent
+ \global \everypar = {}%
+}
-% Some texinfo constructs that are trivial in tex
-\def\iftex{}
-\def\Eiftex{}
-\long\def\ifinfo #1\end ifinfo{}
-\long\def\menu #1\end menu{}
+% @asis just yields its argument. Used with @table, for example.
+%
\def\asis#1{#1}
-\def\node{\parsearg\nodezzz}
-\def\nodezzz#1{\nodexxx [#1,]}
-\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
-\let\lastnode=\relax
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}. So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+ \catcode`\_ = \active
+ \gdef\mathunderscore{%
+ \catcode`\_=\active
+ \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+ }
+}
+% Another complication: we want \\ (and @\) to output a \ character.
+% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
+% this is not advertised and we don't care. Texinfo does not
+% otherwise define @\.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+ \tex
+ \mathunderscore
+ \let\\ = \mathbackslash
+ \mathactive
+ $\finishmath
+}
+\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+ \catcode`^ = \active
+ \catcode`< = \active
+ \catcode`> = \active
+ \catcode`+ = \active
+ \gdef\mathactive{%
+ \let^ = \ptexhat
+ \let< = \ptexless
+ \let> = \ptexgtr
+ \let+ = \ptexplus
+ }
+}
-\def\donoderef{\ifx\lastnode\relax\else
-\expandafter\expandafter\expandafter\setref{\lastnode}\fi
-\let\lastnode=\relax}
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{$\ptexbullet$}
+\def\minus{$-$}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in a typewriter
+% font as three actual period characters.
+%
+\def\dots{%
+ \leavevmode
+ \hbox to 1.5em{%
+ \hskip 0pt plus 0.25fil
+ .\hfil.\hfil.%
+ \hskip 0pt plus 0.5fil
+ }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+ \dots
+ \spacefactor=\endofsentencespacefactor
+}
-\def\unnumbnoderef{\ifx\lastnode\relax\else
-\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
-\let\lastnode=\relax}
+% @comma{} is so commas can be inserted into text without messing up
+% Texinfo's parsing.
+%
+\let\comma = ,
+% @refill is a no-op.
\let\refill=\relax
-\let\setfilename=\comment
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate (before @setfilename).
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% @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{%
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \iflinks
+ \tryauxfile
+ % Open the new aux file. TeX will close it automatically at exit.
+ \immediate\openout\auxfile=\jobname.aux
+ \fi % \openindices needs to do some work in any case.
+ \openindices
+ \let\setfilename=\comment % Ignore extra @setfilename cmds.
+ %
+ % If texinfo.cnf is present on the system, read it.
+ % Useful for site-wide @afourpaper, etc.
+ \openin 1 texinfo.cnf
+ \ifeof 1 \else \input texinfo.cnf \fi
+ \closein 1
+ %
+ \comment % Ignore the actual filename.
+}
+
+% Called from \setfilename.
+%
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set). So we test for \relax and 0 as well as \undefined,
+% borrowed from ifpdf.sty.
+\ifx\pdfoutput\undefined
+\else
+ \ifx\pdfoutput\relax
+ \else
+ \ifcase\pdfoutput
+ \else
+ \pdftrue
+ \fi
+ \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places. Thus, we have to
+% double any backslashes. Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e. Not good.
+% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html
+% (and related messages, the final outcome is that it is up to the TeX
+% user to double the backslashes and otherwise make the string valid, so
+% that's what we do).
+
+% double active backslashes.
+%
+{\catcode`\@=0 \catcode`\\=\active
+ @gdef@activebackslashdouble{%
+ @catcode`@\=@active
+ @let\=@doublebackslash}
+}
+
+% To handle parens, we must adopt a different approach, since parens are
+% not active characters. hyperref.dtx (which has the same problem as
+% us) handles it with this amazing macro to replace tokens. I've
+% tinkered with it a little for texinfo, but it's definitely from there.
+%
+% #1 is the tokens to replace.
+% #2 is the replacement.
+% #3 is the control sequence with the string.
+%
+\def\HyPsdSubst#1#2#3{%
+ \def\HyPsdReplace##1#1##2\END{%
+ ##1%
+ \ifx\\##2\\%
+ \else
+ #2%
+ \HyReturnAfterFi{%
+ \HyPsdReplace##2\END
+ }%
+ \fi
+ }%
+ \xdef#3{\expandafter\HyPsdReplace#3#1\END}%
+}
+\long\def\HyReturnAfterFi#1\fi{\fi#1}
+
+% #1 is a control sequence in which to do the replacements.
+\def\backslashparens#1{%
+ \xdef#1{#1}% redefine it as its expansion; the definition is simply
+ % \lastnode when called from \setref -> \pdfmkdest.
+ \HyPsdSubst{(}{\realbackslash(}{#1}%
+ \HyPsdSubst{)}{\realbackslash)}{#1}%
+}
+
+\ifpdf
+ \input pdfcolor
+ \pdfcatalog{/PageMode /UseOutlines}%
+ \def\dopdfimage#1#2#3{%
+ \def\imagewidth{#2}%
+ \def\imageheight{#3}%
+ % without \immediate, pdftex seg faults when the same image is
+ % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.)
+ \ifnum\pdftexversion < 14
+ \immediate\pdfimage
+ \else
+ \immediate\pdfximage
+ \fi
+ \ifx\empty\imagewidth\else width \imagewidth \fi
+ \ifx\empty\imageheight\else height \imageheight \fi
+ \ifnum\pdftexversion<13
+ #1.pdf%
+ \else
+ {#1.pdf}%
+ \fi
+ \ifnum\pdftexversion < 14 \else
+ \pdfrefximage \pdflastximage
+ \fi}
+ \def\pdfmkdest#1{{%
+ % We have to set dummies so commands such as @code, and characters
+ % such as \, aren't expanded when present in a section title.
+ \atdummies
+ \activebackslashdouble
+ \def\pdfdestname{#1}%
+ \backslashparens\pdfdestname
+ \pdfdest name{\pdfdestname} xyz%
+ }}%
+ %
+ % used to mark target names; must be expandable.
+ \def\pdfmkpgn#1{#1}%
+ %
+ \let\linkcolor = \Blue % was Cyan, but that seems light?
+ \def\endlink{\Black\pdfendlink}
+ % Adding outlines to PDF; macros for calculating structure of outlines
+ % come from Petr Olsak
+ \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+ \else \csname#1\endcsname \fi}
+ \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+ \advance\tempnum by 1
+ \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+ %
+ % #1 is the section text, which is what will be displayed in the
+ % outline by the pdf viewer. #2 is the pdf expression for the number
+ % of subentries (or empty, for subsubsections). #3 is the node text,
+ % which might be empty if this toc entry had no corresponding node.
+ % #4 is the page number
+ %
+ \def\dopdfoutline#1#2#3#4{%
+ % Generate a link to the node text if that exists; else, use the
+ % page number. We could generate a destination for the section
+ % text in the case where a section has no node, but it doesn't
+ % seem worth the trouble, since most documents are normally structured.
+ \def\pdfoutlinedest{#3}%
+ \ifx\pdfoutlinedest\empty
+ \def\pdfoutlinedest{#4}%
+ \else
+ % Doubled backslashes in the name.
+ {\activebackslashdouble \xdef\pdfoutlinedest{#3}%
+ \backslashparens\pdfoutlinedest}%
+ \fi
+ %
+ % Also double the backslashes in the display string.
+ {\activebackslashdouble \xdef\pdfoutlinetext{#1}%
+ \backslashparens\pdfoutlinetext}%
+ %
+ \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+ }
+ %
+ \def\pdfmakeoutlines{%
+ \begingroup
+ % Thanh's hack / proper braces in bookmarks
+ \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
+ \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
+ %
+ % Read toc silently, to get counts of subentries for \pdfoutline.
+ \def\numchapentry##1##2##3##4{%
+ \def\thischapnum{##2}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsecentry##1##2##3##4{%
+ \advancenumber{chap\thischapnum}%
+ \def\thissecnum{##2}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsubsecentry##1##2##3##4{%
+ \advancenumber{sec\thissecnum}%
+ \def\thissubsecnum{##2}%
+ }%
+ \def\numsubsubsecentry##1##2##3##4{%
+ \advancenumber{subsec\thissubsecnum}%
+ }%
+ \def\thischapnum{0}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ %
+ % use \def rather than \let here because we redefine \chapentry et
+ % al. a second time, below.
+ \def\appentry{\numchapentry}%
+ \def\appsecentry{\numsecentry}%
+ \def\appsubsecentry{\numsubsecentry}%
+ \def\appsubsubsecentry{\numsubsubsecentry}%
+ \def\unnchapentry{\numchapentry}%
+ \def\unnsecentry{\numsecentry}%
+ \def\unnsubsecentry{\numsubsecentry}%
+ \def\unnsubsubsecentry{\numsubsubsecentry}%
+ \readdatafile{toc}%
+ %
+ % Read toc second time, this time actually producing the outlines.
+ % The `-' means take the \expnumber as the absolute number of
+ % subentries, which we calculated on our first read of the .toc above.
+ %
+ % We use the node names as the destinations.
+ \def\numchapentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+ \def\numsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+ \def\numsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+ \def\numsubsubsecentry##1##2##3##4{% count is always zero
+ \dopdfoutline{##1}{}{##3}{##4}}%
+ %
+ % PDF outlines are displayed using system fonts, instead of
+ % document fonts. Therefore we cannot use special characters,
+ % since the encoding is unknown. For example, the eogonek from
+ % Latin 2 (0xea) gets translated to a | character. Info from
+ % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+ %
+ % xx to do this right, we have to translate 8-bit characters to
+ % their "best" equivalent, based on the @documentencoding. Right
+ % now, I guess we'll just let the pdf reader have its way.
+ \indexnofonts
+ \setupdatafile
+ \catcode`\\=\active \otherbackslash
+ \input \jobname.toc
+ \endgroup
+ }
+ %
+ \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+ \ifx\PP\D\let\nextsp\relax
+ \else\let\nextsp\skipspaces
+ \ifx\p\space\else\addtokens{\filename}{\PP}%
+ \advance\filenamelength by 1
+ \fi
+ \fi
+ \nextsp}
+ \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax}
+ \ifnum\pdftexversion < 14
+ \let \startlink \pdfannotlink
+ \else
+ \let \startlink \pdfstartlink
+ \fi
+ % make a live url in pdf output.
+ \def\pdfurl#1{%
+ \begingroup
+ % it seems we really need yet another set of dummies; have not
+ % tried to figure out what each command should do in the context
+ % of @url. for now, just make @/ a no-op, that's the only one
+ % people have actually reported a problem with.
+ %
+ \normalturnoffactive
+ \def\@{@}%
+ \let\/=\empty
+ \makevalueexpandable
+ \leavevmode\Red
+ \startlink attr{/Border [0 0 0]}%
+ user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+ \endgroup}
+ \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+ \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+ \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+ \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+ \def\maketoks{%
+ \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+ \ifx\first0\adn0
+ \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+ \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+ \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+ \else
+ \ifnum0=\countA\else\makelink\fi
+ \ifx\first.\let\next=\done\else
+ \let\next=\maketoks
+ \addtokens{\toksB}{\the\toksD}
+ \ifx\first,\addtokens{\toksB}{\space}\fi
+ \fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next}
+ \def\makelink{\addtokens{\toksB}%
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+ \def\pdflink#1{%
+ \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+ \linkcolor #1\endlink}
+ \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+ \let\pdfmkdest = \gobble
+ \let\pdfurl = \gobble
+ \let\endlink = \relax
+ \let\linkcolor = \relax
+ \let\pdfmakeoutlines = \relax
+\fi % \ifx\pdfoutput
-\def\inforef #1{\inforefzzz #1,,,,**}
-\def\inforefzzz #1,#2,#3,#4**{See Info file \file{\losespace#3{}}, node `\losespace#1{}'}
-\def\losespace #1{#1}
\message{fonts,}
-% Font-change commands.
-
-%% Try out Computer Modern fonts at \magstephalf
-\font\tenrm=cmr10 scaled \magstephalf
-\font\tentt=cmtt10 scaled \magstephalf
-% 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.
-\font\tenbf=cmb10 scaled \magstephalf
-\font\tenit=cmti10 scaled \magstephalf
-\font\tensl=cmsl10 scaled \magstephalf
-\font\tensf=cmss10 scaled \magstephalf
-\def\li{\sf}
-\font\tensc=cmcsc10 scaled \magstephalf
-
-% Fonts for @defun, etc.
-\font\defbf=cmbx10 scaled \magstep1 %was 1314
-\let\deftt=\tentt
-\def\df{\let\tt=\deftt \defbf}
-
-% Font for title
-\font\titlerm = cmbx10 scaled \magstep5
-
-% Fonts for indices
-\font\indit=cmti9 \font\indrm=cmr9
-\def\indbf{\indrm} \def\indsl{\indit}
-\def\indexfonts{\let\it=\indit \let\sl=\indsl \let\bf=\indbf \let\rm=\indrm}
-
-% Fonts for headings
-\font\chaprm=cmbx10 scaled \magstep3
-\font\chapit=cmti10 scaled \magstep3
-\font\chapsl=cmsl10 scaled \magstep3
-\font\chaptt=cmtt10 scaled \magstep3
-\font\chapsf=cmss10 scaled \magstep3
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+ \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+ \csname ten#1\endcsname % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+% Default leading.
+\newdimen\textleading \textleading = 13.2pt
+
+% 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
+ }%
+}
+
+% 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}
+
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}
+\setfont\texttt\ttshape{10}{\mainmagstep}
+\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 names and args.
+\setfont\defbf\bfshape{10}{\magstep1}
+\setfont\deftt\ttshape{10}{\magstep1}
+\setfont\defttsl\ttslshape{10}{\magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}
+\setfont\smalltt\ttshape{9}{1000}
+\setfont\smallbf\bfshape{10}{900}
+\setfont\smallit\itshape{9}{1000}
+\setfont\smallsl\slshape{9}{1000}
+\setfont\smallsf\sfshape{9}{1000}
+\setfont\smallsc\scshape{10}{900}
+\setfont\smallttsl\ttslshape{10}{900}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}
+\setfont\smallertt\ttshape{8}{1000}
+\setfont\smallerbf\bfshape{10}{800}
+\setfont\smallerit\itshape{8}{1000}
+\setfont\smallersl\slshape{8}{1000}
+\setfont\smallersf\sfshape{8}{1000}
+\setfont\smallersc\scshape{10}{800}
+\setfont\smallerttsl\ttslshape{10}{800}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}
+\setfont\titleit\itbshape{10}{\magstep4}
+\setfont\titlesl\slbshape{10}{\magstep4}
+\setfont\titlett\ttbshape{12}{\magstep3}
+\setfont\titlettsl\ttslshape{10}{\magstep4}
+\setfont\titlesf\sfbshape{17}{\magstep1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\authorrm{\secrm}
+\def\authortt{\sectt}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\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{17}{1000}
\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\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
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}
+\setfont\ssecit\itbshape{10}{1315}
+\setfont\ssecsl\slbshape{10}{1315}
+\setfont\ssectt\ttbshape{12}{\magstephalf}
+\setfont\ssecttsl\ttslshape{10}{1315}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}
+\setfont\reducedtt\ttshape{10}{1000}
+\setfont\reducedbf\bfshape{10}{1000}
+\setfont\reducedit\itshape{10}{1000}
+\setfont\reducedsl\slshape{10}{1000}
+\setfont\reducedsf\sfshape{10}{1000}
+\setfont\reducedsc\scshape{10}{1000}
+\setfont\reducedttsl\ttslshape{10}{1000}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+
+% 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 except
+% in the main text, 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 because \STYLE needs to also set the
+% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower). These relative commands are used in
+% the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+\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
+ \def\curfontsize{text}%
+ \def\lsize{reduced}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+ \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+ \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+ \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+ \let\tenttsl=\titlettsl
+ \def\curfontsize{title}%
+ \def\lsize{chap}\def\lllsize{subsec}%
+ \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts\rm #1}}
+\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
+ \def\curfontsize{chap}%
+ \def\lsize{sec}\def\lllsize{text}%
+ \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
+ \def\curfontsize{sec}%
+ \def\lsize{subsec}\def\lllsize{reduced}%
+ \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
+ \def\curfontsize{ssec}%
+ \def\lsize{text}\def\lllsize{small}%
+ \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+ \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+ \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+ \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+ \let\tenttsl=\reducedttsl
+ \def\curfontsize{reduced}%
+ \def\lsize{small}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+ \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+ \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+ \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+ \let\tenttsl=\smallttsl
+ \def\curfontsize{small}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+ \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+ \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+ \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+ \let\tenttsl=\smallerttsl
+ \def\curfontsize{smaller}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{9.5pt}}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+% 8.5x11=86 smallbook=72 a4=90 a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+% 8.5x11=90+ smallbook=80 a4=90+ a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt. So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+% 8.5x11=71 smallbook=60 a4=75 a5=58
+%
+% I wish the USA used A4 paper.
+% --karl, 24jan03.
+
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\textfonts \rm
+
+% Define these so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
-\font\secrm=cmbx10 scaled \magstep2
-\font\secit=cmti10 scaled \magstep2
-\font\secsl=cmsl10 scaled \magstep2
-\font\sectt=cmtt10 scaled \magstep2
-\font\secsf=cmss10 scaled \magstep2
-\let\secbf=\secrm
-
-\font\ssecrm=cmbx10 scaled \magstep1
-\font\ssecit=cmti10 scaled \magstep1
-\font\ssecsl=cmsl10 scaled \magstep1
-\font\ssectt=cmtt10 scaled \magstep1
-\font\ssecsf=cmss10 scaled \magstep1
-\let\ssecbf=\ssecrm
-
-\def\textfonts{\let\rm=\tenrm\let\it=\tenit\let\sl=\tensl\let\bf=\tenbf%
-\let\sc=\tensc\let\sf=\tensf}
-\def\chapfonts{\let\rm=\chaprm\let\it=\chapit\let\sl=\chapsl\let\bf=\chapbf\let\tt=\chaptt\let\sf=\chapsf}
-\def\secfonts{\let\rm=\secrm\let\it=\secit\let\sl=\secsl\let\bf=\secbf\let\tt=\sectt\let\sf=\secsf}
-\def\subsecfonts{\let\rm=\ssecrm\let\it=\ssecit\let\sl=\ssecsl\let\bf=\ssecbf\let\tt=\ssectt\let\sf=\ssecsf}
% 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\bfshape{10}{\magstep1} % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}
+\setfont\shortconttt\ttshape{12}{1000}
+
%% Add scribe-like font environments, plus @l for inline lisp (usually sans
%% serif) and @ii for TeX italic
-\def\i#1{{\sl #1}}
-\let\var=\i
-\let\dfn=\i
-\let\emph=\i
-\let\cite=\i
+% \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
+ \ptexslash\fi\fi\fi}
+\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
+% like \smartslanted except unconditionally uses \ttsl.
+% @var is set to this for defun arguments.
+\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally use \sl. We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\let\var=\smartslanted
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% @b, explicit bold.
\def\b#1{{\bf #1}}
\let\strong=\b
-\def\t#1{{\tt \rawbackslash \frenchspacing #1}\null}
-\let\ttfont = \t
-\let\kbd=\t
-\let\code=\t
-\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null}
-\def\key #1{{\tt \uppercase{#1}}\null}
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% 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 = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\chardef\colonChar = `\:
+\chardef\commaChar = `\,
+\chardef\dotChar = `\.
+\chardef\exclamChar= `\!
+\chardef\questChar = `\?
+\chardef\semiChar = `\;
+%
+\catcode`@=11
+ \def\plainfrenchspacing{%
+ \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
+ \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m
+ \def\endofsentencespacefactor{1000}% for @. and friends
+ }
+ \def\plainnonfrenchspacing{%
+ \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+ \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+ \def\endofsentencespacefactor{3000}% for @. and friends
+ }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+\def\t#1{%
+ {\tt \rawbackslash \plainfrenchspacing #1}%
+ \null
+}
+\def\samp#1{`\tclose{#1}'\null}
+\setfont\keyrm\rmshape{8}{1000}
+\font\keysy=cmsy9
+\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+ \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+ \vbox{\hrule\kern-0.4pt
+ \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+ \kern-0.4pt\hrule}%
+ \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+% The old definition, with no lozenge:
+%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+% @file, @option are the same as @samp.
\let\file=\samp
+\let\option=\samp
+
+% @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
+ \plainfrenchspacing
+ #1%
+ }%
+ \null
+}
-\def\l#1{{\li #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.
-\def\r#1{{\rm #1}}
-\def\s#1{{\sc #1}}
-\def\ii#1{{\it #1}}
+% 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
+ %
+ \global\def\code{\begingroup
+ \catcode`\-=\active \catcode`\_=\active
+ \ifallowcodebreaks
+ \let-\codedash
+ \let_\codeunder
+ \else
+ \let-\realdash
+ \let_\realunder
+ \fi
+ \codex
+ }
+}
-\def\titlefont#1{{\titlerm #1}}
+\def\realdash{-}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{%
+ % this is all so @math{@code{var_name}+1} can work. In math mode, _
+ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+ % will therefore expand the active definition of _, which is us
+ % (inside @code that is), therefore an endless loop.
+ \ifusingtt{\ifmmode
+ \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+ \else\normalunderscore \fi
+ \discretionary{}{}{}}%
+ {\_}%
+}
+\def\codex #1{\tclose{#1}\endgroup}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__. This is undesirable in
+% some manuals, especially if they don't have long identifiers in
+% general. @allowcodebreaks provides a way to control this.
+%
+\newif\ifallowcodebreaks \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\keywordtrue
+ \allowcodebreakstrue
+ \else\ifx\txiarg\keywordfalse
+ \allowcodebreaksfalse
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @allowcodebreaks option `\txiarg'}%
+ \fi\fi
+}
-\def\titlepage{\begingroup \parindent=0pt \hbox{}%
-\let\oldpage=\page
-\def\page{\oldpage \hbox{}}}
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+% `example' (@kbd uses ttsl only inside of @example and friends),
+% or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\worddistinct
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+ \else\ifx\txiarg\wordexample
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+ \else\ifx\txiarg\wordcode
+ \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @kbdinputstyle option `\txiarg'}%
+ \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct.'
+\kbdinputstyle distinct
+
+\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{\kbdfont\look}}\fi
+\else{\tclose{\kbdfont\look}}\fi}
+
+% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
+\let\indicateurl=\code
+\let\env=\code
+\let\command=\code
+
+% @uref (abbreviation for `urlref') takes an optional (comma-separated)
+% second argument specifying the text to display and an optional third
+% arg as text to display instead of (rather than in addition to) the url
+% itself. First (mandatory) arg is the url. Perhaps eventually put in
+% a hypertex \special here.
+%
+\def\uref#1{\douref #1,,,\finish}
+\def\douref#1,#2,#3,#4\finish{\begingroup
+ \unsepspaces
+ \pdfurl{#1}%
+ \setbox0 = \hbox{\ignorespaces #3}%
+ \ifdim\wd0 > 0pt
+ \unhbox0 % third arg given, show only that
+ \else
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \ifpdf
+ \unhbox0 % PDF: 2nd arg given, show only it
+ \else
+ \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
+ \fi
+ \else
+ \code{#1}% only url given, so show it
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
-\def\Etitlepage{\endgroup\page\HEADINGSon}
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+ \def\email#1{\doemail#1,,\finish}
+ \def\doemail#1,#2,#3\finish{\begingroup
+ \unsepspaces
+ \pdfurl{mailto:#1}%
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+ \endlink
+ \endgroup}
+\else
+ \let\email=\uref
+\fi
-% Make altmode in file print out right
+% 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}
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}} % roman font
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+%
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+ {\selectfonts\lsize #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+%
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+ {\plainfrenchspacing #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+%
+% Although only regular is the truly official Euro symbol, we ignore
+% that. The Euro is designed to be slightly taller than the regular
+% font height.
+%
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+%
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+%
+% Also doesn't work in math. Do we need to do math with euro symbols?
+% Hope not.
+%
+%
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+ % We set the font at each command, rather than predefining it in
+ % \textfonts and the other font-switching commands, so that
+ % installations which never need the symbol don't have to have the
+ % font installed.
+ %
+ % There is only one designed size (nominal 10pt), so we always scale
+ % that to the current nominal size.
+ %
+ % By the way, simply using "at 1em" works for cmr10 and the like, but
+ % does not work for cmbx10 and other extended/shrunken fonts.
+ %
+ \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+ %
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+ \else
+ % regular:
+ \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+ \fi
+ \thiseurofont
+}
+
+% @registeredsymbol - R in a circle. The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+ $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+ \hfil\crcr\Orb}}%
+ }$%
+}
+
+% Laurent Siebenmann reports \Orb undefined with:
+% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38
+% so we'll define it if necessary.
+%
+\ifx\Orb\undefined
+\def\Orb{\mathhexbox20D}
+\fi
-\catcode `\^^[=\active \def^^[{$\diamondsuit$}
\message{page headings,}
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+ % Open one extra group, as we want to close it in the middle of \Etitlepage.
+ \begingroup
+ \parindent=0pt \textfonts
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % 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
+ \let\page = \oldpage
+ \page
+ \null
+ }%
+}
+
+\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
+ %
+ % Need this before the \...aftertitlepage checks so that if they are
+ % in effect the toc pages will come out with page numbers.
+ \HEADINGSon
+ %
+ % If they want short, they certainly want long too.
+ \ifsetshortcontentsaftertitlepage
+ \shortcontents
+ \contents
+ \global\let\shortcontents = \relax
+ \global\let\contents = \relax
+ \fi
+ %
+ \ifsetcontentsaftertitlepage
+ \contents
+ \global\let\contents = \relax
+ \global\let\shortcontents = \relax
+ \fi
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines
+ \let\tt=\authortt}
+
+\parseargdef\title{%
+ \checkenv\titlepage
+ \leftline{\titlefonts\rm #1}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+ \checkenv\titlepage
+ {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+ \def\temp{\quotation}%
+ \ifx\thisenv\temp
+ \def\quotationauthor{#1}% printed in \Equotation.
+ \else
+ \checkenv\titlepage
+ \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+ {\authorfont \leftline{#1}}%
+ \fi
+}
+
+
%%% 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
+\newtoks\evenheadline % headline on even pages
+\newtoks\oddheadline % headline on odd pages
+\newtoks\evenfootline % footline on even pages
+\newtoks\oddfootline % footline on 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}}
+% 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
@@ -534,92 +2272,123 @@
% @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{%
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\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{%
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\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}}}
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
-\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
-\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\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}}}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+ \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+ %
+ % Leave some space for the footline. Hopefully ok to assume
+ % @evenfooting will not be used by itself.
+ \global\advance\pageheight by -\baselineskip
+ \global\advance\vsize by -\baselineskip
+}
-\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 @.
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
-% @headings on turns them on.
-% @headings off turns them off.
-% By default, they are off.
+
+% @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{
+\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,
-% Put current file name in lower left corner,
-% Put chapter name on inside top of right hand pages, document
+% 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\HEADINGSon{
-\pagealignmacro
+\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}}
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\undefined
+\def\today{%
+ \number\day\space
+ \ifcase\month
+ \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+ \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+ \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+ \fi
+ \space\number\year}
+\fi
-\message{tables,}
+% @settitle line... specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
-% Tables -- @table, @ftable, @item(x), @kitem(x), @xitem(x).
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
% default indentation of table text
\newdimen\tableindent \tableindent=.8in
@@ -631,178 +2400,804 @@ July\or August\or September\or October\or November\or December\fi
% used internally for \itemindent minus \itemmargin
\newdimen\itemmax
-% Note @table and @ftable define @item, @itemx, etc., with these defs.
+% Note @table, @ftable, 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).
-\def\internalBitem{\smallbreak \parsearg\itemzzz}
-\def\internalBitemx{\par \parsearg\itemzzz}
+\newif\ifitemxneedsnegativevskip
-\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
-\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \par \parsearg\xitemzzz}
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
-\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
-\def\internalBkitemx{\par \parsearg\kitemzzz}
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
-\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}\itemzzz {#1}}
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemindicate{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % 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. However, if
+ % what follows is an environment such as @example, there will be no
+ % \parskip glue; then the negative vskip we just inserted would
+ % cause the example and the item to crash together. So we use this
+ % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+ % \parskip glue after all. Section titles are handled this way also.
+ %
+ \penalty 10001
+ \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.
+ \noindent
+ % Do this with kerns and \unhbox so that if there is a footnote in
+ % the item text, it can migrate to the main vertical list and
+ % eventually be printed.
+ \nobreak\kern-\tableindent
+ \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+ \unhbox0
+ \nobreak\kern\dimen0
+ \endgroup
+ \itemxneedsnegativevskiptrue
+ \fi
+}
-\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}\itemzzz {#1}}
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
-\def\itemzzz #1{\begingroup %
-\advance \hsize by -\rightskip %
-\advance \hsize by -\leftskip %
-\setbox0=\hbox{\itemfont{#1}}%
-\itemindex{#1}%
-\parskip=0in %
-\noindent %
-\ifdim \wd0>\itemmax %
-\vadjust{\penalty 10000}%
-\hbox to \hsize{\hskip -\tableindent\box0\hss}\ %
-\else %
-\hbox to 0pt{\hskip -\tableindent\box0\hss}%
-\fi %
-\endgroup %
-}
-
-\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\dontindex #1{}
-\def\fnitemindex #1{\doind {fn}{\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}% Neccessary 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 %
-\parindent = 0pt
-\parskip = \smallskipamount
-\ifdim \parskip=0pt \parskip=2pt \fi%
-\def\Etable{\endgraf\endgroup\afterenvbreak}%
-\let\item = \internalBitem %
-\let\itemx = \internalBitemx %
-\let\kitem = \internalBkitem %
-\let\kitemx = \internalBkitemx %
-\let\xitem = \internalBxitem %
-\let\xitemx = \internalBxitemx %
+% @table, @ftable, @vtable.
+\envdef\table{%
+ \let\itemindex\gobble
+ \tablecheck{table}%
+}
+\envdef\ftable{%
+ \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+ \tablecheck{ftable}%
+}
+\envdef\vtable{%
+ \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+ \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+ \ifnum \the\catcode`\^^M=\active
+ \endgroup
+ \errmessage{This command won't work in this context; perhaps the problem is
+ that we are \inenvironment\thisenv}%
+ \def\next{\doignore{#1}}%
+ \else
+ \let\next\tablex
+ \fi
+ \next
+}
+\def\tablex#1{%
+ \def\itemindicate{#1}%
+ \parsearg\tabley
+}
+\def\tabley#1{%
+ {%
+ \makevalueexpandable
+ \edef\temp{\noexpand\tablez #1\space\space\space}%
+ \expandafter
+ }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+ \aboveenvbreak
+ \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+ \ifnum 0#2>0 \tableindent=#2\mil \fi
+ \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+ \itemmax=\tableindent
+ \advance \itemmax by -\itemmargin
+ \advance \leftskip by \tableindent
+ \exdentamount=\tableindent
+ \parindent = 0pt
+ \parskip = \smallskipamount
+ \ifdim \parskip=0pt \parskip=2pt \fi
+ \let\item = \internalBitem
+ \let\itemx = \internalBitemx
}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
% This is the counter used by @enumerate, which is really @itemize
\newcount \itemno
-\def\itemize{\parsearg\itemizezzz}
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+ \aboveenvbreak
+ \itemmax=\itemindent
+ \advance\itemmax by -\itemmargin
+ \advance\leftskip by \itemindent
+ \exdentamount=\itemindent
+ \parindent=0pt
+ \parskip=\smallskipamount
+ \ifdim\parskip=0pt \parskip=2pt \fi
+ \def\itemcontents{#1}%
+ % @itemize with no arg is equivalent to @itemize @bullet.
+ \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+ \let\item=\itemizeitem
+}
-\def\itemizezzz #1{\itemizey {#1}{\Eitemize}}
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+ \advance\itemno by 1 % for enumerations
+ {\let\par=\endgraf \smallbreak}% reasonable place to break
+ {%
+ % If the document has an @itemize directly after a section title, a
+ % \nobreak will be last on the list, and \sectionheading will have
+ % done a \vskip-\parskip. In that case, we don't want to zero
+ % parskip, or the item text will crash with the heading. On the
+ % other hand, when there is normal text preceding the item (as there
+ % usually is), we do want to zero parskip, or there would be too much
+ % space. In that case, we won't have a \nobreak before. At least
+ % that's the theory.
+ \ifnum\lastpenalty<10000 \parskip=0in \fi
+ \noindent
+ \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+ \vadjust{\penalty 1200}}% not good to break after first line of item.
+ \flushcr
+}
-\def\itemizey #1#2{%
-\aboveenvbreak %
-\begingroup %
-\itemno = 0 %
-\itemmax=\itemindent %
-\advance \itemmax by -\itemmargin %
-\advance \leftskip by \itemindent %
-\parindent = 0pt
-\parskip = \smallskipamount
-\ifdim \parskip=0pt \parskip=2pt \fi%
-\def#2{\endgraf\endgroup\afterenvbreak}%
-\def\itemcontents{#1}%
-\let\item=\itemizeitem}
+% \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}}%
-\def\bullet{$\ptexbullet$}
-\def\minus{$-$}
+% 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'.
+%
+\envparseargdef\enumerate{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ % 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 <number>.
+ %
+ \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
+}
-\def\enumerate{\itemizey{\the\itemno.}\Eenumerate\flushcr}
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
-% Definition of @item while inside @itemize.
+% 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 \doitemize, 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
+ \doitemize{#1.}\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}
+
+
+% @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.
+
+% 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 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.
+%
+\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
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1. We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+ \global\advance\colcount by 1
+ \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
+ \else
+ \global\advance\colcount by 1
+ \setbox0=\hbox{#1\unskip\space}% 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
+ % Put the argument back for the \pickupwholefraction call, so
+ % we'll always have a period there to be parsed.
+ \def\go{\pickupwholefraction#1}%
+ \else
+ \let\go = \setuptable
+ \fi%
+ \fi
+ \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold.
+% Assignments have to be global since we are inside the implicit group
+% of an alignment entry. Note that \everycr resets \everytab.
+\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}%
+%
+% A \tab used to include \hskip1sp. But then the space in a template
+% line is not enough. That is bad. So let's go back to just `&' until
+% we encounter the problem it was intended to solve again.
+% --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab % insert after every tab.
+%
+\envdef\multitable{%
+ \vskip\parskip
+ \startsavinginserts
+ %
+ % @item within a multitable starts a normal row.
+ % We use \def instead of \let so that if one of the multitable entries
+ % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+ % \endtemplate) expanding \doitemize.
+ \def\item{\crcr}%
+ %
+ \tolerance=9500
+ \hbadness=9500
+ \setmultitablespacing
+ \parskip=\multitableparskip
+ \parindent=\multitableparindent
+ \overfullrule=0pt
+ \global\colcount=0
+ %
+ \everycr = {%
+ \noalign{%
+ \global\everytab={}%
+ \global\colcount=0 % Reset the column counter.
+ % Check for saved footnotes, etc.
+ \checkinserts
+ % Keeps underfull box messages off when table breaks over pages.
+ %\filbreak
+ % 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.
+ }%
+ }%
+ %
+ \parsearg\domultitable
+}
+\def\domultitable#1{%
+ % To parse everything between @multitable and @item:
+ \setuptable#1 \endsetuptable
+ %
+ % 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
+ \multistrut
+ \vtop{%
+ % Use the current \colcount to find the correct column width:
+ \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 the 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.
+ %
+ % Make sure we don't inherit \rightskip from the outer environment.
+ \rightskip=0pt
+ \ifnum\colcount=1
+ % The first column will be indented with the surrounding text.
+ \advance\hsize by\leftskip
+ \else
+ \ifsetpercent \else
+ % If user has not 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 highlighting sequences respectively
+ % marking characters.
+ \noindent\ignorespaces##\unskip\multistrut
+ }\cr
+}
+\def\Emultitable{%
+ \crcr
+ \egroup % end the \halign
+ \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+ \def\multistrut{\strut}% just use the standard line spacing
+ %
+ % Compute \multitablelinespace (if not defined by user) for use in
+ % \multitableparskip calculation. We used define \multistrut based on
+ % this, but (ironically) that caused the spacing to be off.
+ % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+%% 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.
+\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{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed. They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested. But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+ \expandafter\let\csname #1\endcsname = \relax
+ \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+ % Scan in ``verbatim'' mode:
+ \obeylines
+ \catcode`\@ = \other
+ \catcode`\{ = \other
+ \catcode`\} = \other
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \spaceisspace
+ %
+ % Count number of #1's that we've seen.
+ \doignorecount = 0
+ %
+ % Swallow text until we reach the matching `@end #1'.
+ \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+ \obeylines %
+ %
+ \gdef\dodoignore#1{%
+ % #1 contains the command name as a string, e.g., `ifinfo'.
+ %
+ % Define a command to find the next `@end #1'.
+ \long\def\doignoretext##1^^M@end #1{%
+ \doignoretextyyy##1^^M@#1\_STOP_}%
+ %
+ % And this command to find another #1 command, at the beginning of a
+ % line. (Otherwise, we would consider a line `@c @ifset', for
+ % example, to count as an @ifset for nesting.)
+ \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+ %
+ % And now expand that command.
+ \doignoretext ^^M%
+ }%
+}
+
+\def\doignoreyyy#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty % Nothing found.
+ \let\next\doignoretextzzz
+ \else % Found a nested condition, ...
+ \advance\doignorecount by 1
+ \let\next\doignoretextyyy % ..., look for another.
+ % If we're here, #1 ends with ^^M\ifinfo (for example).
+ \fi
+ \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+ \ifnum\doignorecount = 0 % We have just found the outermost @end.
+ \let\next\enddoignore
+ \else % Still inside a nested condition.
+ \advance\doignorecount by -1
+ \let\next\doignoretext % Look for the next @end.
+ \fi
+ \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+ % Ignore anything after the last `@end #1'; this matters in verbatim
+ % environments, where otherwise the newline after an ignored conditional
+ % would result in a blank line in the output.
+ \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @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.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ {%
+ \makevalueexpandable
+ \def\temp{#2}%
+ \edef\next{\gdef\makecsname{SET#1}}%
+ \ifx\temp\empty
+ \next{}%
+ \else
+ \setzzz#2\endsetzzz
+ \fi
+ }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+ {%
+ \makevalueexpandable
+ \global\expandafter\let\csname SET#1\endcsname=\relax
+ }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+ \catcode`\- = \active \catcode`\_ = \active
+ %
+ \gdef\makevalueexpandable{%
+ \let\value = \expandablevalue
+ % We don't want these characters active, ...
+ \catcode`\-=\other \catcode`\_=\other
+ % ..., but we might end up with active ones in the argument if
+ % we're called from @code, as @code{@value{foo-bar_}}, though.
+ % So \let them to their normal equivalents.
+ \let-\realdash \let_\normalunderscore
+ }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file. This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+%
+\def\expandablevalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ {[No value for ``#1'']}%
+ \message{Variable `#1', used in @value, is not set.}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get special treatment of `@end ifset,' call \makeond and the redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+ {%
+ \makevalueexpandable
+ \let\next=\empty
+ \expandafter\ifx\csname SET#2\endcsname\relax
+ #1% If not set, redefine \next.
+ \fi
+ \expandafter
+ }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
-\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 300}}%
-\flushcr}
\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}}
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
% \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 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}}
+%
+\def\newindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{% % Define @#1index
+ \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}
+%
+\def\newcodeindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{%
+ \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\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\xdef\csname#1index\endcsname{% % Define \xxxindex
-\noexpand\docodeindex {#2}}%
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+ % Only do \closeout if we haven't already done it, else we'll end up
+ % closing the target index.
+ \expandafter \ifx\csname donesynindex#2\endcsname \undefined
+ % The \closeout helps reduce unnecessary open files; the limit on the
+ % Acorn RISC OS is a mere 16 files.
+ \expandafter\closeout\csname#2indfile\endcsname
+ \expandafter\let\csname\donesynindex#2\endcsname = 1
+ \fi
+ % redefine \fooindfile:
+ \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+ \expandafter\let\csname#2indfile\endcsname=\temp
+ % redefine \fooindex:
+ \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
}
% Define \doindex, the driver for all \fooindex macros.
@@ -822,74 +3217,394 @@ July\or August\or September\or October\or November\or December\fi
\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+% Take care of Texinfo commands that can appear in an index entry.
+% Since there are some commands we want to expand, and others we don't,
+% we have to laboriously prevent expansion for those that we don't.
+%
\def\indexdummies{%
-\def\bf{\realbackslash bf }%
-\def\rm{\realbackslash rm }%
-\def\sl{\realbackslash sl }%
-\def\dots{\realbackslash dots }%
-\def\copyright{\realbackslash copyright }%
+ \escapechar = `\\ % use backslash in output files.
+ \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+ \def\ {\realbackslash\space }%
+ % Need these in case \tex is in effect and \{ is a \delimiter again.
+ % But can't use \lbracecmd and \rbracecmd because texindex assumes
+ % braces and backslashes are used only as delimiters.
+ \let\{ = \mylbrace
+ \let\} = \myrbrace
+ %
+ % Do the redefinitions.
+ \commondummies
}
-% \indexnofonts no-ops all font-change commands.
-% This is used when outputting the strings to sort the index by.
-\def\indexdummyfont#1{#1}
-\def\indexnofonts{%
-\let\code=\indexdummyfont
-\let\samp=\indexdummyfont
-\let\kbd=\indexdummyfont
-\let\key=\indexdummyfont
-\let\var=\indexdummyfont
+% For the aux and toc files, @ is the escape character. So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files). When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+ \def\@{@@}%
+ \def\ {@ }%
+ \let\{ = \lbraceatcmd
+ \let\} = \rbraceatcmd
+ %
+ % Do the redefinitions.
+ \commondummies
+ \otherbackslash
+}
+
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+ %
+ % \definedummyword defines \#1 as \string\#1\space, thus effectively
+ % preventing its expansion. This is used only for control% words,
+ % not control letters, because the \space would be incorrect for
+ % control characters, but is needed to separate the control word
+ % from whatever follows.
+ %
+ % For control letters, we have \definedummyletter, which omits the
+ % space.
+ %
+ % These can be used both for control words that take an argument and
+ % those that do not. If it is followed by {arg} in the input, then
+ % that will dutifully get written to the index (or wherever).
+ %
+ \def\definedummyword ##1{\def##1{\string##1\space}}%
+ \def\definedummyletter##1{\def##1{\string##1}}%
+ \let\definedummyaccent\definedummyletter
+ %
+ \commondummiesnofonts
+ %
+ \definedummyletter\_%
+ %
+ % Non-English letters.
+ \definedummyword\AA
+ \definedummyword\AE
+ \definedummyword\L
+ \definedummyword\OE
+ \definedummyword\O
+ \definedummyword\aa
+ \definedummyword\ae
+ \definedummyword\l
+ \definedummyword\oe
+ \definedummyword\o
+ \definedummyword\ss
+ \definedummyword\exclamdown
+ \definedummyword\questiondown
+ \definedummyword\ordf
+ \definedummyword\ordm
+ %
+ % Although these internal commands shouldn't show up, sometimes they do.
+ \definedummyword\bf
+ \definedummyword\gtr
+ \definedummyword\hat
+ \definedummyword\less
+ \definedummyword\sf
+ \definedummyword\sl
+ \definedummyword\tclose
+ \definedummyword\tt
+ %
+ \definedummyword\LaTeX
+ \definedummyword\TeX
+ %
+ % Assorted special characters.
+ \definedummyword\bullet
+ \definedummyword\comma
+ \definedummyword\copyright
+ \definedummyword\registeredsymbol
+ \definedummyword\dots
+ \definedummyword\enddots
+ \definedummyword\equiv
+ \definedummyword\error
+ \definedummyword\euro
+ \definedummyword\expansion
+ \definedummyword\minus
+ \definedummyword\pounds
+ \definedummyword\point
+ \definedummyword\print
+ \definedummyword\result
+ %
+ % We want to disable all macros so that they are not expanded by \write.
+ \macrolist
+ %
+ \normalturnoffactive
+ %
+ % Handle some cases of @value -- where it does not contain any
+ % (non-fully-expandable) commands.
+ \makevalueexpandable
}
-% 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.
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+%
+\def\commondummiesnofonts{%
+ % Control letters and accents.
+ \definedummyletter\!%
+ \definedummyaccent\"%
+ \definedummyaccent\'%
+ \definedummyletter\*%
+ \definedummyaccent\,%
+ \definedummyletter\.%
+ \definedummyletter\/%
+ \definedummyletter\:%
+ \definedummyaccent\=%
+ \definedummyletter\?%
+ \definedummyaccent\^%
+ \definedummyaccent\`%
+ \definedummyaccent\~%
+ \definedummyword\u
+ \definedummyword\v
+ \definedummyword\H
+ \definedummyword\dotaccent
+ \definedummyword\ringaccent
+ \definedummyword\tieaccent
+ \definedummyword\ubaraccent
+ \definedummyword\udotaccent
+ \definedummyword\dotless
+ %
+ % Texinfo font commands.
+ \definedummyword\b
+ \definedummyword\i
+ \definedummyword\r
+ \definedummyword\sc
+ \definedummyword\t
+ %
+ % Commands that take arguments.
+ \definedummyword\acronym
+ \definedummyword\cite
+ \definedummyword\code
+ \definedummyword\command
+ \definedummyword\dfn
+ \definedummyword\emph
+ \definedummyword\env
+ \definedummyword\file
+ \definedummyword\kbd
+ \definedummyword\key
+ \definedummyword\math
+ \definedummyword\option
+ \definedummyword\pxref
+ \definedummyword\ref
+ \definedummyword\samp
+ \definedummyword\strong
+ \definedummyword\tie
+ \definedummyword\uref
+ \definedummyword\url
+ \definedummyword\var
+ \definedummyword\verb
+ \definedummyword\w
+ \definedummyword\xref
+}
-{\catcode`\@=0 \catcode`\\=\other
-@gdef@realbackslash{\}}
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names. It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+ % Accent commands should become @asis.
+ \def\definedummyaccent##1{\let##1\asis}%
+ % We can just ignore other control letters.
+ \def\definedummyletter##1{\let##1\empty}%
+ % Hopefully, all control words can become @asis.
+ \let\definedummyword\definedummyaccent
+ %
+ \commondummiesnofonts
+ %
+ % Don't no-op \tt, since it isn't a user-level command
+ % and is used in the definitions of the active chars like <, >, |, etc.
+ % Likewise with the other plain tex font commands.
+ %\let\tt=\asis
+ %
+ \def\ { }%
+ \def\@{@}%
+ % how to handle braces?
+ \def\_{\normalunderscore}%
+ %
+ % Non-English letters.
+ \def\AA{AA}%
+ \def\AE{AE}%
+ \def\L{L}%
+ \def\OE{OE}%
+ \def\O{O}%
+ \def\aa{aa}%
+ \def\ae{ae}%
+ \def\l{l}%
+ \def\oe{oe}%
+ \def\o{o}%
+ \def\ss{ss}%
+ \def\exclamdown{!}%
+ \def\questiondown{?}%
+ \def\ordf{a}%
+ \def\ordm{o}%
+ %
+ \def\LaTeX{LaTeX}%
+ \def\TeX{TeX}%
+ %
+ % Assorted special characters.
+ % (The following {} will end up in the sort string, but that's ok.)
+ \def\bullet{bullet}%
+ \def\comma{,}%
+ \def\copyright{copyright}%
+ \def\registeredsymbol{R}%
+ \def\dots{...}%
+ \def\enddots{...}%
+ \def\equiv{==}%
+ \def\error{error}%
+ \def\euro{euro}%
+ \def\expansion{==>}%
+ \def\minus{-}%
+ \def\pounds{pounds}%
+ \def\point{.}%
+ \def\print{-|}%
+ \def\result{=>}%
+ %
+ % We need to get rid of all macros, leaving only the arguments (if present).
+ % Of course this is not nearly correct, but it is the best we can do for now.
+ % makeinfo does not expand macros in the argument to @deffn, which ends up
+ % writing an index entry, and texindex isn't prepared for an index sort entry
+ % that starts with \.
+ %
+ % Since macro invocations are followed by braces, we can just redefine them
+ % to take a single TeX argument. The case of a macro invocation that
+ % goes to end-of-line is not handled.
+ %
+ \macrolist
+}
\let\indexbackslash=0 %overridden during \printindex.
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
-\def\doind #1#2{%
-{\indexdummies % Must do this here, since \bf, etc expand at this stage
-\count10=\lastpenalty %
-\escapechar=`\\%
-{\let\folio=0% 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 in the indx.
-%
-% 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}%
-}%
-% 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}}}%
-\temp }%
-\penalty\count10}}
-
-\def\dosubind #1#2#3{%
-{\indexdummies % Must do this here, since \bf, etc expand at this stage
-\count10=\lastpenalty %
-\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}}
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% Workhorse for all \fooindexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+ \iflinks
+ {%
+ % Store the main index entry text (including the third arg).
+ \toks0 = {#2}%
+ % If third arg is present, precede it with a space.
+ \def\thirdarg{#3}%
+ \ifx\thirdarg\empty \else
+ \toks0 = \expandafter{\the\toks0 \space #3}%
+ \fi
+ %
+ \edef\writeto{\csname#1indfile\endcsname}%
+ %
+ \ifvmode
+ \dosubindsanitize
+ \else
+ \dosubindwrite
+ \fi
+ }%
+ \fi
+}
+
+% Write the entry in \toks0 to the index file:
+%
+\def\dosubindwrite{%
+ % Put the index entry in the margin if desired.
+ \ifx\SETmarginindex\relax\else
+ \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+ \fi
+ %
+ % Remember, we are within a group.
+ \indexdummies % Must do this here, since \bf, etc expand at this stage
+ \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
+ % so it will be output as is; and it will print as backslash.
+ %
+ % Process the index entry with all font commands turned off, to
+ % get the string to sort by.
+ {\indexnofonts
+ \edef\temp{\the\toks0}% need full expansion
+ \xdef\indexsorttmp{\temp}%
+ }%
+ %
+ % Set up the complete index entry, with both the sort key and
+ % the original text, including any font commands. We write
+ % three arguments to \entry to the .?? file (four in the
+ % subentry case), texindex reduces to two when writing the .??s
+ % sorted result.
+ \edef\temp{%
+ \write\writeto{%
+ \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
+ }%
+ \temp
+}
+
+% Take care of unwanted page breaks:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again. Otherwise, the whatsit generated by the
+% \write will make \lastskip zero. The result is that sequences
+% like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode. We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip. \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip. The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+% ..., ready, GO:
+%
+\def\dosubindsanitize{%
+ % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+ \skip0 = \lastskip
+ \edef\lastskipmacro{\the\lastskip}%
+ \count255 = \lastpenalty
+ %
+ % If \lastskip is nonzero, that means the last item was a
+ % skip. And since a skip is discardable, that means this
+ % -\skip0 glue we're inserting is preceded by a
+ % non-discardable item, therefore it is not a potential
+ % breakpoint, therefore no \nobreak needed.
+ \ifx\lastskipmacro\zeroskipmacro
+ \else
+ \vskip-\skip0
+ \fi
+ %
+ \dosubindwrite
+ %
+ \ifx\lastskipmacro\zeroskipmacro
+ % If \lastskip was zero, perhaps the last item was a penalty, and
+ % perhaps it was >=10000, e.g., a \nobreak. In that case, we want
+ % to re-insert the same penalty (values >10000 are used for various
+ % signals); since we just inserted a non-discardable item, any
+ % following glue (such as a \parskip) would be a breakpoint. For example:
+ %
+ % @deffn deffn-whatever
+ % @vindex index-whatever
+ % Description.
+ % would allow a break between the index-whatever whatsit
+ % and the "Description." paragraph.
+ \ifnum\count255>9999 \penalty\count255 \fi
+ \else
+ % On the other hand, if we had a nonzero \lastskip,
+ % this make-up glue would be preceded by a non-discardable item
+ % (the whatsit from the \write), so we must insert a \nobreak.
+ \nobreak\vskip\skip0
+ \fi
+}
% The index entry written in the file actually looks like
% \entry {sortstring}{page}{topic}
@@ -906,7 +3621,7 @@ July\or August\or September\or October\or November\or December\fi
% \secondary {subtopic}{pagelist}
% for each subtopic.
-% Define the user-accessible indexing commands
+% Define the user-accessible indexing commands
% @findex, @vindex, @kindex, @cindex.
\def\findex {\fnindex}
@@ -923,284 +3638,682 @@ July\or August\or September\or October\or November\or December\fi
% 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{\tex %
-\catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
-\catcode`\$=\other\catcode`\_=\other
-\catcode`\~=\other
-\def\indexbackslash{\rawbackslashxx}
-\indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
-\begindoublecolumns
-\openin 1 \jobname.#1s
-\ifeof 1 \else \closein 1 \input \jobname.#1s
-\fi
-\enddoublecolumns
-\Etex}
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+ \dobreak \chapheadingskip{10000}%
+ %
+ \smallfonts \rm
+ \tolerance = 9500
+ \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+ %
+ % See if the index file exists and is nonempty.
+ % Change catcode of @ here so that if the index file contains
+ % \initial {@}
+ % as its first line, TeX doesn't complain about mismatched braces
+ % (because it thinks @} is a control sequence).
+ \catcode`\@ = 11
+ \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.
+ \putwordIndexNonexistent
+ \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
+ \putwordIndexIsEmpty
+ \else
+ % 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.
+ \def\indexbackslash{\backslashcurfont}%
+ \catcode`\\ = 0
+ \escapechar = `\\
+ \begindoublecolumns
+ \input \jobname.#1s
+ \enddoublecolumns
+ \fi
+ \fi
+ \closein 1
+\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{{%
+ % Some minor font changes for the special characters.
+ \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+ %
+ % Remove any glue we may have, we'll be inserting our own.
+ \removelastskip
+ %
+ % We like breaks before the index initials, so insert a bonus.
+ \nobreak
+ \vskip 0pt plus 3\baselineskip
+ \penalty 0
+ \vskip 0pt plus -3\baselineskip
+ %
+ % Typeset the initial. Making this add up to a whole number of
+ % baselineskips increases the chance of the dots lining up from column
+ % to column. It still won't often be perfect, because of the stretch
+ % we need before each entry, but it's better.
+ %
+ % No shrink because it confuses \balancecolumns.
+ \vskip 1.67\baselineskip plus .5\baselineskip
+ \leftline{\secbf #1}%
+ % Do our best not to break after the initial.
+ \nobreak
+ \vskip .33\baselineskip plus .1\baselineskip
+}}
-\outer\def\initial #1{%
-{\let\tentt=\sectt \let\sf=\sectt
-\ifdim\lastskip<\initialskipamount
-\removelastskip \penalty-200 \vskip \initialskipamount\fi
-\line{\secbf#1\hfill}\kern 2pt\penalty3000}}
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin. It is used for index
+% and table of contents entries. The paragraph is indented by \leftskip.
+%
+% A straightforward implementation would start like this:
+% \def\entry#1#2{...
+% But this frozes the catcodes in the argument, and can cause problems to
+% @code, which sets - active. This problem was fixed by a kludge---
+% ``-'' was active throughout whole index, but this isn't really right.
+%
+% The right solution is to prevent \entry from swallowing the whole text.
+% --kasal, 21nov03
+\def\entry{%
+ \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
+ %
+ % A bit of stretch before each entry for the benefit of balancing
+ % columns.
+ \vskip 0pt plus1pt
+ %
+ % Swallow the left brace of the text (first parameter):
+ \afterassignment\doentry
+ \let\temp =
+}
+\def\doentry{%
+ \bgroup % Instead of the swallowed brace.
+ \noindent
+ \aftergroup\finishentry
+ % And now comes the text of the entry.
+}
+\def\finishentry#1{%
+ % #1 is the page number.
+ %
+ % 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{#1}%
+ \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.
+ \ifpdf
+ \pdfgettoks#1.%
+ \ \the\toksA
+ \else
+ \ #1%
+ \fi
+ \fi
+ \par
+ \endgroup
+}
-\outer\def\entry #1#2{
-{\parfillskip=0in \parskip=0in \parindent=0in
-\hangindent=1in \hangafter=1%
-\noindent\hbox{#1}\leaders\Dotsbox\hskip 0pt plus 1filll #2\par
-}}
+% 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}\leaders\Dotsbox\hskip 0pt plus 1filll#2\par
+\def\secondary#1#2{{%
+ \parfillskip=0in
+ \parskip=0in
+ \hangindent=1in
+ \hangafter=1
+ \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+ \ifpdf
+ \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+ \else
+ #2
+ \fi
+ \par
}}
-%% Define two-column mode, which is used in indexes.
-%% Adapted from the TeXBook, page 416
-\catcode `\@=11
+% 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 = {%
+ %
+ % Here is a possibility not foreseen in manmac: if we accumulate a
+ % whole lot of material, we might end up calling this \output
+ % routine twice in a row (see the doublecol-lose test, which is
+ % essentially a couple of indexes with @setchapternewpage off). In
+ % that case we just ship out what is in \partialpage with the normal
+ % output routine. Generally, \partialpage will be empty when this
+ % runs and this will be a no-op. See the indexspread.tex test case.
+ \ifvoid\partialpage \else
+ \onepageout{\pagecontents\partialpage}%
+ \fi
+ %
+ \global\setbox\partialpage = \vbox{%
+ % Unvbox the main output page.
+ \unvbox\PAGE
+ \kern-\topskip \kern\baselineskip
+ }%
+ }%
+ \eject % run that output routine to set \partialpage
+ %
+ % Use the double-column output routine for subsequent pages.
+ \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 in one place.
+ %
+ % 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
+}
-\newdimen\doublecolumnhsize \doublecolumnhsize = 3.11in
-\newdimen\doublecolumnvsize \doublecolumnvsize = 19.1in
+% The double-column output routine for all double-column pages except
+% the last.
+%
+\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@ = \vsize
+ \divide\dimen@ by 2
+ \advance\dimen@ by -\ht\partialpage
+ %
+ % box0 will be the left-hand column, box2 the right.
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar
+ \unvbox255
+ \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+ \unvbox\partialpage
+ %
+ \hsize = \doublecolumnhsize
+ \wd0=\hsize \wd2=\hsize
+ \hbox to\pagewidth{\box0\hfil\box2}%
+}
+%
+% All done with double columns.
+\def\enddoublecolumns{%
+ \output = {%
+ % Split the last of the double-column material. Leave it on the
+ % current page, no automatic page break.
+ \balancecolumns
+ %
+ % If we end up splitting too much material for the current page,
+ % though, there will be another page break right after this \output
+ % invocation ends. Having called \balancecolumns once, we do not
+ % want to call it again. Therefore, reset \output to its normal
+ % definition right away. (We hope \balancecolumns will never be
+ % called on to balance too much material, but if it is, this makes
+ % the output somewhat more palatable.)
+ \global\output = {\onepageout{\pagecontents\PAGE}}%
+ }%
+ \eject
+ \endgroup % started in \begindoublecolumns
+ %
+ % \pagegoal was set to the doubled \vsize above, since we restarted
+ % the current page. We're now back to normal single-column
+ % typesetting, so reset \pagegoal to the normal \vsize (after the
+ % \endgroup where \vsize got restored).
+ \pagegoal = \vsize
+}
+%
+% Called at the end of the double column material.
+\def\balancecolumns{%
+ \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+ \dimen@ = \ht0
+ \advance\dimen@ by \topskip
+ \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by 2 % target to split to
+ %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
+ \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@ by 1pt
+ \repeat
+ }%
+ %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
+ \setbox0=\vbox to\dimen@{\unvbox1}%
+ \setbox2=\vbox to\dimen@{\unvbox3}%
+ %
+ \pagesofar
+}
+\catcode`\@ = \other
-\def\begindoublecolumns{\begingroup
- \output={\global\setbox\partialpage=\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}\eject
- \output={\doublecolumnout} \hsize=\doublecolumnhsize \vsize=\doublecolumnvsize}
-\def\enddoublecolumns{\output={\balancecolumns}\eject
- \endgroup \pagegoal=\vsize}
-\def\doublecolumnout{\splittopskip=\topskip \splitmaxdepth=\maxdepth
- \dimen@=\pageheight \advance\dimen@ by-\ht\partialpage
- \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
- \onepageout\pagesofar \unvbox255 \penalty\outputpenalty}
-\def\pagesofar{\unvbox\partialpage %
- \hsize=\doublecolumnhsize % have to restore this since output routine
-% changes it to set cropmarks (P. A. MacKay, 12 Nov. 1986)
- \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
-\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen@=\ht0
- \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
- \divide\dimen@ by2 \splittopskip=\topskip
- {\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
-\newcount \subsecno
-\newcount \subsubsecno
+% Chapters, sections, etc.
+
+% \unnumberedno is an oxymoron, of course. But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number". We avoid collisions with chapter
+% numbers by starting them at 10000. (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\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
-\openout \contentsfile = \jobname.toc
+\newcount\appendixno \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+ \ifnum\appendixno=`A A%
+ \else\ifnum\appendixno=`B B%
+ \else\ifnum\appendixno=`C C%
+ \else\ifnum\appendixno=`D D%
+ \else\ifnum\appendixno=`E E%
+ \else\ifnum\appendixno=`F F%
+ \else\ifnum\appendixno=`G G%
+ \else\ifnum\appendixno=`H H%
+ \else\ifnum\appendixno=`I I%
+ \else\ifnum\appendixno=`J J%
+ \else\ifnum\appendixno=`K K%
+ \else\ifnum\appendixno=`L L%
+ \else\ifnum\appendixno=`M M%
+ \else\ifnum\appendixno=`N N%
+ \else\ifnum\appendixno=`O O%
+ \else\ifnum\appendixno=`P P%
+ \else\ifnum\appendixno=`Q Q%
+ \else\ifnum\appendixno=`R R%
+ \else\ifnum\appendixno=`S S%
+ \else\ifnum\appendixno=`T T%
+ \else\ifnum\appendixno=`U U%
+ \else\ifnum\appendixno=`V V%
+ \else\ifnum\appendixno=`W W%
+ \else\ifnum\appendixno=`X X%
+ \else\ifnum\appendixno=`Y Y%
+ \else\ifnum\appendixno=`Z Z%
+ % The \the is necessary, despite appearances, because \appendixletter is
+ % expanded while writing the .toc file. \char\appendixno is not
+ % expandable, thus it is written literally, thus all appendixes come out
+ % with the same letter (or @) in the toc without it.
+ \else\char\the\appendixno
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
% 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
-%
-}
-
-\outer\def\chapter{\parsearg\chapterzzz}
-\def\chapterzzz #1{\seccheck{chapter}%
-\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \chapno by 1 \message{Chapter \the\chapno}%
-\chapmacro {#1}{\the\chapno}%
-\gdef\thissection{#1}\gdef\thischapter{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-}
-
-\outer\def\appendix{\parsearg\appendixzzz}
-\def\appendixzzz #1{\seccheck{appendix}%
-\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \appendixno by 1 \message{Appendix \appendixletter}%
-\chapmacro {#1}{Appendix \appendixletter}%
-\gdef\thischapter{#1}\gdef\thissection{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash chapentry {#1}{Appendix \appendixletter}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-}
-
-\outer\def\unnumbered{\parsearg\unnumberedzzz}
-\def\unnumberedzzz #1{\seccheck{unnumbered}%
-\secno=0 \subsecno=0 \subsubsecno=0 \message{(#1)}
-\unnumbchapmacro {#1}%
-\gdef\thischapter{#1}\gdef\thissection{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-}
-
-\outer\def\section{\parsearg\sectionzzz}
-\def\sectionzzz #1{\seccheck{section}%
-\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
-\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash secentry %
-{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-\penalty 10000 %
-}
-
-\outer\def\appendixsection{\parsearg\appendixsectionzzz}
-\outer\def\appendixsec{\parsearg\appendixsectionzzz}
-\def\appendixsectionzzz #1{\seccheck{appendixsection}%
-\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
-\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash secentry %
-{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}
-
-\outer\def\unnumberedsec{\parsearg\unnumberedseczzz}
-\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}
-
-\outer\def\subsection{\parsearg\subsectionzzz}
-\def\subsectionzzz #1{\seccheck{subsection}%
-\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
-\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash subsecentry %
-{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\donoderef %
-\penalty 10000 %
-}
-
-\outer\def\appendixsubsec{\parsearg\appendixsubseczzz}
-\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
-\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
-\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash subsecentry %
-{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}
-
-\outer\def\unnumberedsubsec{\parsearg\unnumberedsubseczzz}
-\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}
-
-\outer\def\subsubsection{\parsearg\subsubsectionzzz}
-\def\subsubsectionzzz #1{\seccheck{subsubsection}%
-\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
-\subsubsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\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\appendixsubsubseczzz}
-\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
-\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
-\subsubsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash subsubsecentry{#1}%
-{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%\
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
-}
-
-\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
-\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
-\plainsecheading {#1}\gdef\thissection{#1}%
-\let\rawbackslash=\relax%
-\let\frenchspacing=\relax%
-\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
-\escapechar=`\\%
-\write \contentsfile \temp %
-\unnumbnoderef %
-\penalty 10000 %
+% page headings and footings can use it. @section does likewise.
+% However, they are not reliable, because we don't use marks.
+\def\thischapter{}
+\def\thissection{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@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
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achive this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unmlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+ % Compute the abs. sec. level:
+ \absseclevel=#2
+ \advance\absseclevel by \secbase
+ % Make sure \absseclevel doesn't fall outside the range:
+ \ifnum \absseclevel < 0
+ \absseclevel = 0
+ \else
+ \ifnum \absseclevel > 3
+ \absseclevel = 3
+ \fi
+ \fi
+ % The heading type:
+ \def\headtype{#1}%
+ \if \headtype U%
+ \ifnum \absseclevel < \unmlevel
+ \chardef\unmlevel = \absseclevel
+ \fi
+ \else
+ % Check for appendix sections:
+ \ifnum \absseclevel = 0
+ \edef\chapheadtype{\headtype}%
+ \else
+ \if \headtype A\if \chapheadtype N%
+ \errmessage{@appendix... within a non-appendix chapter}%
+ \fi\fi
+ \fi
+ % Check for numbered within unnumbered:
+ \ifnum \absseclevel > \unmlevel
+ \def\headtype{U}%
+ \else
+ \chardef\unmlevel = 3
+ \fi
+ \fi
+ % Now print the heading:
+ \if \headtype U%
+ \ifcase\absseclevel
+ \unnumberedzzz{#3}%
+ \or \unnumberedseczzz{#3}%
+ \or \unnumberedsubseczzz{#3}%
+ \or \unnumberedsubsubseczzz{#3}%
+ \fi
+ \else
+ \if \headtype A%
+ \ifcase\absseclevel
+ \appendixzzz{#3}%
+ \or \appendixsectionzzz{#3}%
+ \or \appendixsubseczzz{#3}%
+ \or \appendixsubsubseczzz{#3}%
+ \fi
+ \else
+ \ifcase\absseclevel
+ \chapterzzz{#3}%
+ \or \seczzz{#3}%
+ \or \numberedsubseczzz{#3}%
+ \or \numberedsubsubseczzz{#3}%
+ \fi
+ \fi
+ \fi
+ \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered. Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v. By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+ % section resetting is \global in case the chapter is in a group, such
+ % as an @include file.
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\chapno by 1
+ %
+ % Used for \float.
+ \gdef\chaplevelprefix{\the\chapno.}%
+ \resetallfloatnos
+ %
+ \message{\putwordChapter\space \the\chapno}%
+ %
+ % Write the actual heading.
+ \chapmacro{#1}{Ynumbered}{\the\chapno}%
+ %
+ % So @section and the like are numbered underneath this chapter.
+ \global\let\section = \numberedsec
+ \global\let\subsection = \numberedsubsec
+ \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\appendixno by 1
+ \gdef\chaplevelprefix{\appendixletter.}%
+ \resetallfloatnos
+ %
+ \def\appendixnum{\putwordAppendix\space \appendixletter}%
+ \message{\appendixnum}%
+ %
+ \chapmacro{#1}{Yappendix}{\appendixletter}%
+ %
+ \global\let\section = \appendixsec
+ \global\let\subsection = \appendixsubsec
+ \global\let\subsubsection = \appendixsubsubsec
+}
+
+\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\unnumberedno by 1
+ %
+ % Since an unnumbered has no number, no prefix for figures.
+ \global\let\chaplevelprefix = \empty
+ \resetallfloatnos
+ %
+ % 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<toks register> to achieve this: TeX expands \the<toks> only once,
+ % simply yielding the contents of <toks register>. (We also do this for
+ % the toc entries.)
+ \toks0 = {#1}%
+ \message{(\the\toks0)}%
+ %
+ \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+ %
+ \global\let\section = \unnumberedsec
+ \global\let\subsection = \unnumberedsubsec
+ \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+ % Well, we could do the following in a group, but that would break
+ % an assumption that \chapmacro is called at the outermost level.
+ % Thus we are safer this way: --kasal, 24feb04
+ \let\centerparametersmaybe = \centerparameters
+ \unnmhead0{#1}%
+ \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno}%
}
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynumbered}%
+ {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% 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.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
% Define @majorheading, @heading and @subheading
-\outer\def\majorheading #1{%
-{\advance\chapheadingskip by 10pt \chapbreak }%
-{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 200}
+% 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.
+
-\outer\def\chapheading #1{\chapbreak %
-{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 200}
+\def\majorheading{%
+ {\advance\chapheadingskip by 10pt \chapbreak }%
+ \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+ {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+ \bigskip \par\penalty 200\relax
+ \suppressfirstparagraphindent
+}
-\let\heading=\secheadingi
-\let\subheading=\subsecheadingi
-\let\subsubheading=\subsubsecheadingi
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
% These macros generate a chapter, section, etc. heading only
% (including whitespace, linebreaking, etc. around it),
@@ -1209,12 +4322,10 @@ July\or August\or September\or October\or November\or December\fi
%%% 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 \chapheadingskip = 30pt plus 8pt minus 4pt
+\newskip\chapheadingskip
\def\chapbreak{\dobreak \chapheadingskip {-4000}}
\def\chappager{\par\vfill\supereject}
@@ -1222,856 +4333,2894 @@ July\or August\or September\or October\or November\or December\fi
\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
-\def\CHAPPAGoff{
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
\global\let\pchapsepmacro=\chapbreak
\global\let\pagealignmacro=\chappager}
-\def\CHAPPAGon{
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
\global\let\pchapsepmacro=\chappager
-\global\let\pagealignmacro=\chappager}
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
-\def\CHAPPAGodd{
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
\global\let\pchapsepmacro=\chapoddpage
-\global\let\pagealignmacro=\chapoddpage}
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
\CHAPPAGon
-\def\CHAPFplain{
-\global\let\chapmacro=\chfplain
-\global\let\unnumbchapmacro=\unnchfplain}
-
-\def\chfplain #1#2{%
-\pchapsepmacro %
-{\chapfonts \line{\chaprm #2.\enspace #1\hfill}}\bigskip \par\penalty 5000 %
+% Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+\def\Yappendixkeyword{Yappendix}
+%
+\def\chapmacro#1#2#3{%
+ \pchapsepmacro
+ {%
+ \chapfonts \rm
+ %
+ % Have to define \thissection before calling \donoderef, because the
+ % xref code eventually uses it. On the other hand, it has to be called
+ % after \pchapsepmacro, or the headline will change too soon.
+ \gdef\thissection{#1}%
+ \gdef\thischaptername{#1}%
+ %
+ % Only insert the separating space if we have a chapter/appendix
+ % number, and don't print the unnumbered ``number''.
+ \def\temptype{#2}%
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unnchap}%
+ \gdef\thischapter{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+ \def\toctype{omit}%
+ \gdef\thischapter{}%
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+ \def\toctype{app}%
+ % We don't substitute the actual chapter name into \thischapter
+ % because we don't want its macros evaluated now. And we don't
+ % use \thissection because that changes with each section.
+ %
+ \xdef\thischapter{\putwordAppendix{} \appendixletter:
+ \noexpand\thischaptername}%
+ \else
+ \setbox0 = \hbox{#3\enspace}%
+ \def\toctype{numchap}%
+ \xdef\thischapter{\putwordChapter{} \the\chapno:
+ \noexpand\thischaptername}%
+ \fi\fi\fi
+ %
+ % Write the toc entry for this chapter. Must come before the
+ % \donoderef, because we include the current node name in the toc
+ % entry, and \donoderef resets it to empty.
+ \writetocentry{\toctype}{#1}{#3}%
+ %
+ % For pdftex, we have to write out the node definition (aka, make
+ % the pdfdest) after any page break, but before the actual text has
+ % been typeset. If the destination for the pdf outline is after the
+ % text, then jumping from the outline may wind up with the text not
+ % being visible, for instance under high magnification.
+ \donoderef{#2}%
+ %
+ % Typeset the actual heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent=\wd0 \centerparametersmaybe
+ \unhbox0 #1\par}%
+ }%
+ \nobreak\bigskip % no page break after a chapter title
+ \nobreak
}
-\def\unnchfplain #1{%
-\pchapsepmacro %
-{\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 10000 %
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+ \advance\rightskip by 3\rightskip
+ \leftskip = \rightskip
+ \parfillskip = 0pt
}
-\CHAPFplain % The default
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff. We'll see. --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
\def\unnchfopen #1{%
-\chapoddpage {\chapfonts \line{\chaprm #1\hfill}}\bigskip \par\penalty 10000 %
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\nobreak
}
-
\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\nobreak
+}
+\def\CHAPFopen{%
+ \global\let\chapmacro=\chfopen
+ \global\let\centerchapmacro=\centerchfopen}
-\def\CHAPFopen{
-\global\let\chapmacro=\chfopen
-\global\let\unnumbchapmacro=\unnchfopen}
-
-% Parameter controlling skip before section headings.
-\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt
-\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+% Section titles. These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
-\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt
-\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
-\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
-\def\plainsecheading #1{\secheadingi {#1}}
-\def\secheadingi #1{{\advance \secheadingskip by \parskip %
-\secheadingbreak}%
-{\secfonts \line{\secrm #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
-\def\subsecheading #1#2#3#4{{\advance \subsecheadingskip by \parskip %
-\subsecheadingbreak}%
-{\secfonts \line{\secrm#2.#3.#4\enspace #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
-\def\subsubsecfonts{\subsecfonts} % Maybe this should change
+% Print any size, any type, section title.
+%
+% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is
+% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
+% section number.
+%
+\def\sectionheading#1#2#3#4{%
+ {%
+ % Switch to the right set of fonts.
+ \csname #2fonts\endcsname \rm
+ %
+ % Insert space above the heading.
+ \csname #2headingbreak\endcsname
+ %
+ % Only insert the space after the number if we have a section number.
+ \def\sectionlevel{#2}%
+ \def\temptype{#3}%
+ %
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unn}%
+ \gdef\thissection{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ % for @headings -- no section number, don't include in toc,
+ % and don't redefine \thissection.
+ \setbox0 = \hbox{}%
+ \def\toctype{omit}%
+ \let\sectionlevel=\empty
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{app}%
+ \gdef\thissection{#1}%
+ \else
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{num}%
+ \gdef\thissection{#1}%
+ \fi\fi\fi
+ %
+ % Write the toc entry (before \donoderef). See comments in \chapmacro.
+ \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+ %
+ % Write the node reference (= pdf destination for pdftex).
+ % Again, see comments in \chapmacro.
+ \donoderef{#3}%
+ %
+ % Interline glue will be inserted when the vbox is completed.
+ % That glue will be a valid breakpoint for the page, since it'll be
+ % preceded by a whatsit (usually from the \donoderef, or from the
+ % \writetocentry if there was no node). We don't want to allow that
+ % break, since then the whatsits could end up on page n while the
+ % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000.
+ \nobreak
+ %
+ % Output the actual section heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent=\wd0 % zero if no section number
+ \unhbox0 #1}%
+ }%
+ % Add extra space after the heading -- half of whatever came above it.
+ % Don't allow stretch, though.
+ \kern .5 \csname #2headingskip\endcsname
+ %
+ % Do not let the kern be a potential breakpoint, as it would be if it
+ % was followed by glue.
+ \nobreak
+ %
+ % We'll almost certainly start a paragraph next, so don't let that
+ % glue accumulate. (Not a breakpoint because it's preceded by a
+ % discardable item.)
+ \vskip-\parskip
+ %
+ % This is purely so the last item on the list is a known \penalty >
+ % 10000. This is so \startdefun can avoid allowing breakpoints after
+ % section headings. Otherwise, it would insert a valid breakpoint between:
+ %
+ % @section sec-whatever
+ % @deffn def-whatever
+ \penalty 10001
+}
-\def\subsubsecheading #1#2#3#4#5{{\advance \subsecheadingskip by \parskip %
-\subsecheadingbreak}%
-{\secfonts \line{\secrm#2.#3.#4.#5\enspace #1\hfill}}%
-\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
-\message{toc printing,}
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
-\def\Dotsbox{\hbox to 1em{\hss.\hss}} % Used by index macros
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this. The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything. This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+ \edef\writetoctype{#1}%
+ \ifx\writetoctype\omitkeyword \else
+ \iftocfileopened\else
+ \immediate\openout\tocfile = \jobname.toc
+ \global\tocfileopenedtrue
+ \fi
+ %
+ \iflinks
+ {\atdummies
+ \edef\temp{%
+ \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+ \temp
+ }%
+ \fi
+ \fi
+ %
+ % Tell \shipout to create a pdf destination on each page, if we're
+ % writing pdf. These are used in the table of contents. We can't
+ % just write one on every page because the title pages are numbered
+ % 1 and 2 (the page numbers aren't printed), and so are the first
+ % two pages of the document. Thus, we'd have two destinations named
+ % `1', and two named `2'.
+ \ifpdf \global\pdfmakepagedesttrue \fi
+}
-\def\finishcontents{%
-\ifnum\pageno>0 %
-\pagealignmacro %
-\immediate\closeout \contentsfile%
-\pageno=-1 % Request roman numbered pages
-\fi}
-\outer\def\contents{%
-\finishcontents %
-\unnumbchapmacro{Table of Contents}
-\def\thischapter{Table of Contents}
-{\catcode`\\=0
-\catcode`\{=1 % Set up to handle contents files properly
-\catcode`\}=2
-\catcode`\@=11
-\input \jobname.toc
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care. This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+%
+\def\activecatcodes{%
+ \catcode`\"=\active
+ \catcode`\$=\active
+ \catcode`\<=\active
+ \catcode`\>=\active
+ \catcode`\\=\active
+ \catcode`\^=\active
+ \catcode`\_=\active
+ \catcode`\|=\active
+ \catcode`\~=\active
}
-\vfill \eject}
-\outer\def\summarycontents{%
-\finishcontents %
-\unnumbchapmacro{Summary Table of Contents}
-\def\thischapter{Summary Table of Contents}
-{\catcode`\\=0
-\catcode`\{=1 % Set up to handle contents files properly
-\catcode`\}=2
-\catcode`\@=11
-\def\smallbreak{}
-\def\secentry ##1##2##3##4{}
-\def\subsecentry ##1##2##3##4##5{}
-\def\subsubsecentry ##1##2##3##4##5##6{}
-\def\unnumbsecentry ##1##2{}
-\def\unnumbsubsecentry ##1##2{}
-\def\unnumbsubsubsecentry ##1##2{}
-\let\medbreak=\smallbreak
-\input \jobname.toc
-}
-\vfill \eject}
-\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+ \setupdatafile
+ \activecatcodes
+ \input \jobname.toc
+}
-% 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, ...
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
-\def\chapentry #1#2#3{%
-\medbreak
-\line{#2.\space#1\leaders\hbox to 1em{\hss.\hss}\hfill #3}
+% Prepare to read what we've written to \tocfile.
+%
+\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 <tege@matematik.su.se>
+ \contentsalignmacro
+ \immediate\closeout\tocfile
+ %
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \def\thischapter{}%
+ \chapmacro{#1}{Yomitfromtoc}{}%
+ %
+ \savepageno = \pageno
+ \begingroup % Set up to handle contents files properly.
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+ %
+ % Roman numerals for page numbers.
+ \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
}
-\def\unnumbchapentry #1#2{%
-\medbreak
-\line{#1\leaders\Dotsbox\hfill #2}
-}
-\def\secentry #1#2#3#4{%
-\line{\enspace\enspace#2.#3\space#1\leaders\Dotsbox\hfill#4}
+% Normal (long) toc.
+\def\contents{%
+ \startcontents{\putwordTOC}%
+ \openin 1 \jobname.toc
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \ifeof 1 \else
+ \pdfmakeoutlines
+ \fi
+ \closein 1
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
}
-\def\unnumbsecentry #1#2{%
-\line{\enspace\enspace#1\leaders\Dotsbox\hfill #2}
+% And just the chapters.
+\def\summarycontents{%
+ \startcontents{\putwordShortTOC}%
+ %
+ \let\numchapentry = \shortchapentry
+ \let\appentry = \shortchapentry
+ \let\unnchapentry = \shortunnchapentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf
+ \let\sl=\shortcontsl \let\tt=\shortconttt
+ \rm
+ \hyphenpenalty = 10000
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\numsecentry##1##2##3##4{}
+ \let\appsecentry = \numsecentry
+ \let\unnsecentry = \numsecentry
+ \let\numsubsecentry = \numsecentry
+ \let\appsubsecentry = \numsecentry
+ \let\unnsubsecentry = \numsecentry
+ \let\numsubsubsecentry = \numsecentry
+ \let\appsubsubsecentry = \numsecentry
+ \let\unnsubsubsecentry = \numsecentry
+ \openin 1 \jobname.toc
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \closein 1
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
}
+\let\shortcontents = \summarycontents
-\def\subsecentry #1#2#3#4#5{%
-\line{\enspace\enspace\enspace\enspace
-#2.#3.#4\space#1\leaders\Dotsbox\hfill #5}
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+ % This space should be enough, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % But use \hss just in case.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in by \shortchapentry above.)
+ %
+ % We'd like to right-justify chapter numbers, but that looks strange
+ % with appendix letters. And right-justifying numbers and
+ % left-justifying letters looks strange when there is less than 10
+ % chapters. Have to read the whole toc once to know how many chapters
+ % there are before deciding ...
+ \hbox to 1em{#1\hss}%
}
-\def\unnumbsubsecentry #1#2{%
-\line{\enspace\enspace\enspace\enspace#1\leaders\Dotsbox\hfill #2}
-}
+% 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, ...
-\def\subsubsecentry #1#2#3#4#5#6{%
-\line{\enspace\enspace\enspace\enspace\enspace\enspace
-#2.#3.#4.#5\space#1\leaders\Dotsbox\hfill #6}
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+%
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
}
-\def\unnumbsubsubsecentry #1#2{%
-\line{\enspace\enspace\enspace\enspace\enspace\enspace#1\leaders\Dotsbox\hfill #2}
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+ % We use M since it's probably the widest letter.
+ \setbox0 = \hbox{\putwordAppendix{} M}%
+ \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% 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\bgroup#2\egroup}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip plus.1\baselineskip
}
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% 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}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
\message{environments,}
+% @foo ... @end foo.
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% 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.
+%
+\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}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\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}
+%
+\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}
+%
+\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`\"=12
-\catcode`\|=12
-\catcode`\<=12
-\catcode`\>=12
-\escapechar=`\\
-%
-\let\{=\ptexlbrace
-\let\}=\ptexrbrace
-\let\.=\ptexdot
-\let\*=\ptexstar
-\def\@={@}%
-\let\bullet=\ptexbullet
-\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl
-\let\L=\ptexL
-%
-\let\Etex=\endgroup}
-
-% Define @lisp ... @endlisp.
-% @lisp does a \begingroup so it can rebind things,
-% including the definition of @endlisp (which normally is erroneous).
+\envdef\tex{%
+ \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+ \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+ \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+ \catcode `\%=14
+ \catcode `\+=\other
+ \catcode `\"=\other
+ \catcode `\|=\other
+ \catcode `\<=\other
+ \catcode `\>=\other
+ \escapechar=`\\
+ %
+ \let\b=\ptexb
+ \let\bullet=\ptexbullet
+ \let\c=\ptexc
+ \let\,=\ptexcomma
+ \let\.=\ptexdot
+ \let\dots=\ptexdots
+ \let\equiv=\ptexequiv
+ \let\!=\ptexexclam
+ \let\i=\ptexi
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \let\{=\ptexlbrace
+ \let\+=\tabalign
+ \let\}=\ptexrbrace
+ \let\/=\ptexslash
+ \let\*=\ptexstar
+ \let\t=\ptext
+ \let\frenchspacing=\plainfrenchspacing
+ %
+ \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+ \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+ \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (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
-% phr: changed space to \null, to avoid overfull hbox problems.
-{\obeyspaces%
-\gdef\lisppar{\null\endgraf}}
-
-% Cause \obeyspaces to make each Space cause a word-separation
-% rather than the default which is that it acts punctuation.
-% This is because space in tt font looks funny.
-{\obeyspaces %
-\gdef\sepspaces{\def {\ }}}
-
-\newskip\aboveenvskipamount \aboveenvskipamount= 0pt
-\def\aboveenvbreak{{\advance\aboveenvskipamount by \parskip
-\endgraf \ifdim\lastskip<\aboveenvskipamount
-\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}}
-
-\def\afterenvbreak{\endgraf \ifdim\lastskip<\aboveenvskipamount
-\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}
-
-\def\lisp{\aboveenvbreak\begingroup\inENV %This group ends at the end of the @lisp body
-\hfuzz=12truept % Don't be fussy
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-% Single space lines
-\singlespace %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-\let\par=\lisppar
-\def\Elisp{\endgroup\afterenvbreak}%
-\parskip=0pt \advance \rightskip by \lispnarrowing
-\advance \leftskip by \lispnarrowing
-\parindent=0pt
-\let\exdent=\internalexdent
-\obeyspaces \obeylines \tt \rawbackslash
-\def\next##1{}\next}
-
-
-\let\example=\lisp
-\def\Eexample{\Elisp}
-
-\let\smallexample=\lisp
-\def\Esmallexample{\Elisp}
-
-% Macro for 9 pt. examples, necessary to print with 5" lines.
-% From Pavel@xerox. This is not really used unless the
-% @smallbook command is given.
-
-\def\smalllispx{\aboveenvbreak\begingroup\inENV
-% This group ends at the end of the @lisp body
-\hfuzz=12truept % Don't be fussy
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-% Single space lines
-\singlespace %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-\let\par=\lisppar
-\def\Esmalllisp{\endgroup\afterenvbreak}%
-\parskip=0pt \advance \rightskip by \lispnarrowing
-\advance \leftskip by \lispnarrowing
-\parindent=0pt
-\let\exdent=\internalexdent
-\obeyspaces \obeylines \ninett \rawbackslash
-\def\next##1{}\next}
-
-% This is @display; same as @lisp except use roman font.
-
-\def\display{\begingroup\inENV %This group ends at the end of the @display body
-\aboveenvbreak
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-% Single space lines
-\singlespace %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-\let\par=\lisppar
-\def\Edisplay{\endgroup\afterenvbreak}%
-\parskip=0pt \advance \rightskip by \lispnarrowing
-\advance \leftskip by \lispnarrowing
-\parindent=0pt
-\let\exdent=\internalexdent
-\obeyspaces \obeylines
-\def\next##1{}\next}
-
-% This is @format; same as @lisp except use roman font and don't narrow margins
-
-\def\format{\begingroup\inENV %This group ends at the end of the @format body
-\aboveenvbreak
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-\singlespace %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-\let\par=\lisppar
-\def\Eformat{\endgroup\afterenvbreak}
-\parskip=0pt \parindent=0pt
-\obeyspaces \obeylines
-\def\next##1{}\next}
-
-% @flushleft and @flushright
-
-\def\flushleft{\begingroup\inENV %This group ends at the end of the @format body
-\aboveenvbreak
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-% This also causes @ to work when the directive name
-% is terminated by end of line.
-\let\par=\lisppar
-\def\Eflushleft{\endgroup\afterenvbreak}%
-\parskip=0pt \parindent=0pt
-\obeyspaces \obeylines
-\def\next##1{}\next}
-
-\def\flushright{\begingroup\inENV %This group ends at the end of the @format body
-\aboveenvbreak
-% Make spaces be word-separators rather than space tokens.
-\sepspaces %
-% The following causes blank lines not to be ignored
-% by adding a space to the end of each line.
-% This also causes @ to work when the directive name
-% is terminated by end of line.
-\let\par=\lisppar
-\def\Eflushright{\endgroup\afterenvbreak}%
-\parskip=0pt \parindent=0pt
-\advance \leftskip by 0pt plus 1fill
-\obeyspaces \obeylines
-\def\next##1{}\next}
-
-% @quotation - narrow the margins.
-
-\def\quotation{\begingroup\inENV %This group ends at the end of the @quotation body
-{\parskip=0pt % because we will skip by \parskip too, later
-\aboveenvbreak}%
-\singlespace
-\parindent=0pt
-\def\Equotation{\par\endgroup\afterenvbreak}%
-\advance \rightskip by \lispnarrowing
-\advance \leftskip by \lispnarrowing}
+% 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}
-\message{defuns,}
-% Define formatter for defuns
-% First, allow user to change definition object font (\df) internally
-\def\setdeffont #1 {\csname DEF#1\endcsname}
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
-\newskip\defbodyindent \defbodyindent=36pt
-\newskip\defargsindent \defargsindent=50pt
-\newskip\deftypemargin \deftypemargin=12pt
-\newskip\deflastargmargin \deflastargmargin=18pt
+% 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{{%
+ % =10000 instead of <10000 because of a special case in \itemzzz and
+ % \sectionheading, q.v.
+ \ifnum \lastpenalty=10000 \else
+ \advance\envskipamount by \parskip
+ \endgraf
+ \ifdim\lastskip<\envskipamount
+ \removelastskip
+ % it's not a good place to break if the last penalty was \nobreak
+ % or better ...
+ \ifnum\lastpenalty<10000 \penalty-50 \fi
+ \vskip\envskipamount
+ \fi
+ \fi
+}}
-\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}
-{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
-\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
-\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
-
-% 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\&#1}\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{{\tt\char`\[}} \def\rbrb{{\tt\char`\]}}
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+\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
+
+\envdef\cartouche{%
+ \ifhmode\par\fi % can't be in the midst of a paragraph.
+ \startsavinginserts
+ \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 18.4pt % allow for 3pt kerns on either
+ % side, and for 6pt waste from
+ % each corner char, and rule thickness
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing = t%
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \kern3pt
+ \hsize=\cartinner
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+ \comment % For explanation, see the end of \def\group.
+}
+\def\Ecartouche{%
+ \ifhmode\par\fi
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+ \checkinserts
+}
-% 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{%
-\leftskip = 0in %
-\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
-\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}%
-\tolerance=10000 \hbadness=10000 % Make all lines underfull and no complaints
-{\df #1}\enskip % Generate function name
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+ \aboveenvbreak
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \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
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \let\exdent=\nofillexdent
}
-% 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.
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+% @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+ \ifx\SETdispenvsize\smallword
+ \smallexamplefonts \rm
+ \fi
+}
+\def\setsmalldispenv{%
+ \ifx\SETdispenvsize\nosmallword
+ \else
+ \smallexamplefonts \rm
+ \fi
+}
-\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 \leftskip=\defbodyindent %
-\begingroup\obeylines\activeparens\spacesplit#3}
+% We often define two environments, @foo and @smallfoo.
+% Let's do it by one command:
+\def\makedispenv #1#2{
+ \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}
+ \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}
+ \expandafter\let\csname E#1\endcsname \afterenvbreak
+ \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
-\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 \leftskip=\defbodyindent %
-\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+% Define two synonyms:
+\def\maketwodispenvs #1#2#3{
+ \makedispenv{#1}{#3}
+ \makedispenv{#2}{#3}
+}
-% 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.
+% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvs {lisp}{example}{%
+ \nonfillstart
+ \tt
+ \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+ \gobble % eat return
+}
-{\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}}
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenv {display}{%
+ \nonfillstart
+ \gobble
+}
-% So much for the things common to all kinds of definitions.
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenv{format}{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
-% Define @defun.
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+\let\Eflushleft = \afterenvbreak
-% First, define the processing that is wanted for arguments of \defun
-% Use this to expand the args and terminate the paragraph they make up
+% @flushright.
+%
+\envdef\flushright{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \advance\leftskip by 0pt plus 1fill
+ \gobble
+}
+\let\Eflushright = \afterenvbreak
-\def\defunargs #1{\functionparens \sl #1%
-\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
-\interlinepenalty=10000
-\endgraf\vskip -\parskip \penalty 10000}
-% Do complete processing of one @defun or @defunx line already parsed.
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins. We keep \parskip nonzero in general, since
+% we're doing normal filling. So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\envdef\quotation{%
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \parindent=0pt
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \parsearg\quotationlabel
+}
-% @deffn Command forward-char nchars
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+ \par
+ \ifx\quotationauthor\undefined\else
+ % indent a bit.
+ \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+ \fi
+ {\parskip=0pt \afterenvbreak}%
+}
-\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty \else
+ {\bf #1: }%
+ \fi
+}
-\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
-\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup}
-% @defun == @deffn Function
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too. Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+ \do\ \do\\\do\{\do\}\do\$\do\&%
+ \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+ \do\<\do\>\do\|\do\@\do+\do\"%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+ \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% [Knuth] pp. 380,381,391
+% Disable Spanish ligatures ?` and !` of \tt font
+\begingroup
+ \catcode`\`=\active\gdef`{\relax\lq}
+\endgroup
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+ \tt % easiest (and conventionally used) font for verbatim
+ \def\par{\leavevmode\endgraf}%
+ \catcode`\`=\active
+ \tabeightspaces
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+}
-\def\defun{\defparsebody\Edefun\defunx\defunheader}
+% Setup for the @verbatim environment
+%
+% Real tab expansion
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+\def\starttabbox{\setbox0=\hbox\bgroup}
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabexpand{%
+ \catcode`\^^I=\active
+ \def^^I{\leavevmode\egroup
+ \dimen0=\wd0 % the width so far, or since the previous tab
+ \divide\dimen0 by\tabw
+ \multiply\dimen0 by\tabw % compute previous multiple of \tabw
+ \advance\dimen0 by\tabw % advance to next multiple of \tabw
+ \wd0=\dimen0 \box0 \starttabbox
+ }%
+ }
+\endgroup
+\def\setupverbatim{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ % Easiest (and conventionally used) font for verbatim
+ \tt
+ \def\par{\leavevmode\egroup\box0\endgraf}%
+ \catcode`\`=\active
+ \tabexpand
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+ \everypar{\starttabbox}%
+}
-\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Function}%
-\defunargs {#2}\endgroup %
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters. Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+% \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+ \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+ \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+% \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+ \catcode`\ =\active
+ \obeylines %
+ % ignore everything up to the first ^^M, that's the newline at the end
+ % of the @verbatim input line itself. Otherwise we get an extra blank
+ % line in the output.
+ \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+ % We really want {...\end verbatim} in the body of the macro, but
+ % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+ \setupverbatim\doverbatim
}
+\let\Everbatim = \afterenvbreak
-% @defmac == @deffn Macro
-\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+ {%
+ \makevalueexpandable
+ \setupverbatim
+ \input #1
+ \afterenvbreak
+ }%
+}
-\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Macro}%
-\defunargs {#2}\endgroup %
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is very desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+ \begingroup
+ \parindent = 0pt % paragraph indentation looks wrong on title page
+ \scanexp\copyingtext
+ \endgroup
}
-% @defspec == @deffn Special Form
+\message{defuns,}
+% @defun etc.
-\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
-\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
-\begingroup\defname {#1}{Special form}%
-\defunargs {#2}\endgroup %
+% Start the processing of @deffn:
+\def\startdefun{%
+ \ifnum\lastpenalty<10000
+ \medbreak
+ \else
+ % If there are two @def commands in a row, we'll have a \nobreak,
+ % which is there to keep the function description together with its
+ % header. But if there's nothing but headers, we need to allow a
+ % break somewhere. Check specifically for penalty 10002, inserted
+ % by \defargscommonending, instead of 10000, since the sectioning
+ % commands also insert a nobreak penalty, and we don't want to allow
+ % a break between a section heading and a defun.
+ %
+ \ifnum\lastpenalty=10002 \penalty2000 \fi
+ %
+ % Similarly, after a section heading, do not allow a break.
+ % But do insert the glue.
+ \medskip % preceded by discardable penalty, so not a breakpoint
+ \fi
+ %
+ \parindent=0in
+ \advance\leftskip by \defbodyindent
+ \exdentamount=\defbodyindent
}
-% 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\dodefunx#1{%
+ % First, check whether we are in the right environment:
+ \checkenv#1%
+ %
+ % As above, allow line break if we have multiple x headers in a row.
+ % It's not a great place, though.
+ \ifnum\lastpenalty=10002 \penalty3000 \fi
+ %
+ % And now, it's time to reuse the body of the original defun:
+ \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
-% @defmethod, and so on
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+ \begingroup
+ % call \deffnheader:
+ #1#2 \endheader
+ % common ending:
+ \interlinepenalty = 10000
+ \advance\rightskip by 0pt plus 1fil
+ \endgraf
+ \nobreak\vskip -\parskip
+ \penalty 10002 % signal to \startdefun and \dodefunx
+ % Some of the @defun-type tags do not enable magic parentheses,
+ % rendering the following check redundant. But we don't optimize.
+ \checkparencounts
+ \endgroup
+}
-% @defop {Funny Method} foo-class frobnicate argument
+\def\Edefun{\endgraf\medbreak}
-\def\defop #1 {\def\defoptype{#1}%
-\defmethparsebody\Edefop\defopx\defopheader}
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remainnig is to define \deffnheader.
+%
+\def\makedefun#1{%
+ \expandafter\let\csname E#1\endcsname = \Edefun
+ \edef\temp{\noexpand\domakedefun
+ \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+ \temp
+}
-\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 %
+% \domakedefun \deffn \deffnx \deffnheader
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+ \envdef#1{%
+ \startdefun
+ \parseargusing\activeparens{\printdefunline#3}%
+ }%
+ \def#2{\dodefunx#1}%
+ \def#3%
}
-% @defmethod == @defop Method
+%%% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
-\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
-\def\defmethodheader #1#2#3{\dosubind {fn}{\code{#2}}{on #1}% entry in function index
-\begingroup\defname {#2}{Operation on #1}%
-\defunargs {#3}\endgroup %
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+ % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+ \dosubind{fn}{\code{#3}}{#1}%
+ \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
}
-% @defcv {Class Option} foo-class foo-flag
+%%% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
-\def\defcv #1 {\def\defcvtype{#1}%
-\defmethparsebody\Edefcv\defcvx\defcvheader}
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
-\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 %
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{fn}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
}
-% @defivar == @defcv {Instance Variable}
+%%% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
-\def\defivar{\defmethparsebody\Edefivar\defivarx\defivarheader}
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
-\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 %
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{vr}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
}
-% These definitions are run if you use @defmethodx, etc.,
-% anywhere other than immediately after a @defmethod, etc.
+%%% Untyped variables:
-\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}}
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
-% Now @defvar
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
-% 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\vskip -\parskip \penalty 10000}
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
-% @defvr Counter foo-count
+%%% Type:
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+ \doind{tp}{\code{#2}}%
+ \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
-\def\defvr{\defmethparsebody\Edefvr\defvrx\defvrheader}
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+ % Get the values of \leftskip and \rightskip as they were outside the @def...
+ \advance\leftskip by -\defbodyindent
+ %
+ % How we'll format the type name. Putting it in brackets helps
+ % distinguish it from the body text that may end up on the next line
+ % just below it.
+ \def\temp{#1}%
+ \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+ %
+ % Figure out line sizes for the paragraph shape.
+ % The first line needs space for \box0; but if \rightskip is nonzero,
+ % we need only space for the part of \box0 which exceeds it:
+ \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip
+ % The continuations:
+ \dimen2=\hsize \advance\dimen2 by -\defargsindent
+ % (plain.tex says that \dimen1 should be used only as global.)
+ \parshape 2 0in \dimen0 \defargsindent \dimen2
+ %
+ % Put the type name to the right margin.
+ \noindent
+ \hbox to 0pt{%
+ \hfil\box0 \kern-\hsize
+ % \hsize has to be shortened this way:
+ \kern\leftskip
+ % Intentionally do not respect \rightskip, since we need the space.
+ }%
+ %
+ % Allow all lines to be underfull without complaint:
+ \tolerance=10000 \hbadness=10000
+ \exdentamount=\defbodyindent
+ {%
+ % defun fonts. We use typewriter by default (used to be bold) because:
+ % . we're printing identifiers, they should be in tt in principle.
+ % . in languages with many accents, such as Czech or French, it's
+ % common to leave accents off identifiers. The result looks ok in
+ % tt, but exceedingly strange in rm.
+ % . we don't want -- and --- to be treated as ligatures.
+ % . this still does not fix the ?` and !` ligatures, but so far no
+ % one has made identifiers using them :).
+ \df \tt
+ \def\temp{#2}% return value type
+ \ifx\temp\empty\else \tclose{\temp} \fi
+ #3% output function name
+ }%
+ {\rm\enskip}% hskip 0.5 em of \tenrm
+ %
+ \boldbrax
+ % arguments will be output next, if any.
+}
-\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
-\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name. This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable. Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+ % use sl by default (not ttsl),
+ % tt for the names.
+ \df \sl \hyphenchar\font=0
+ %
+ % On the other hand, if an argument has two dashes (for instance), we
+ % want a way to get ttsl. Let's try @var for that.
+ \let\var=\ttslanted
+ #1%
+ \sl\hyphenchar\font=45
+}
-% @defvar == @defvr Variable
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+ \catcode`\(=\active \catcode`\)=\active
+ \catcode`\[=\active \catcode`\]=\active
+ \catcode`\&=\active
+}
-\def\defvar{\defparsebody\Edefvar\defvarx\defvarheader}
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
-\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
-\begingroup\defname {#1}{Variable}%
-\defvarargs {#2}\endgroup %
+% 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.
+{
+ \activeparens
+ \global\let(=\lparen \global\let)=\rparen
+ \global\let[=\lbrack \global\let]=\rbrack
+ \global\let& = \&
+
+ \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+ \gdef\magicamp{\let&=\amprm}
}
-% @defopt == @defvr {User Option}
+\newcount\parencount
-\def\defopt{\defparsebody\Edefopt\defoptx\defoptheader}
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+ \ifampseen
+ % At the first level, print parens in roman,
+ % otherwise use the default font.
+ \ifnum \parencount=1 \rm \fi
+ \else
+ % The \sf parens (in \boldbrax) actually are a little bolder than
+ % the contained text. This is especially needed for [ and ] .
+ \sf
+ \fi
+}
+\def\infirstlevel#1{%
+ \ifampseen
+ \ifnum\parencount=1
+ #1%
+ \fi
+ \fi
+}
+\def\bfafterword#1 {#1 \bf}
-\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
-\begingroup\defname {#1}{User Option}%
-\defvarargs {#2}\endgroup %
+\def\opnr{%
+ \global\advance\parencount by 1
+ {\parenfont(}%
+ \infirstlevel \bfafterword
+}
+\def\clnr{%
+ {\parenfont)}%
+ \infirstlevel \sl
+ \global\advance\parencount by -1
}
-% This definition is run if you use @defvarx
-% anywhere other than immediately after a @defvar or @defvarx.
+\newcount\brackcount
+\def\lbrb{%
+ \global\advance\brackcount by 1
+ {\bf[}%
+}
+\def\rbrb{%
+ {\bf]}%
+ \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+ \ifnum\parencount=0 \else \badparencount \fi
+ \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+\def\badparencount{%
+ \errmessage{Unbalanced parentheses in @def}%
+ \global\parencount=0
+}
+\def\badbrackcount{%
+ \errmessage{Unbalanced square braces in @def}%
+ \global\brackcount=0
+}
-\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
-\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
-\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
-% Now define @deftp
-% Args are printed in bold, a slight difference from @defvar.
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\undefined
+ \newwrite\macscribble
+ \def\scantokens#1{%
+ \toks0={#1}%
+ \immediate\openout\macscribble=\jobname.tmp
+ \immediate\write\macscribble{\the\toks0}%
+ \immediate\closeout\macscribble
+ \input \jobname.tmp
+ }
+\fi
+
+\def\scanmacro#1{%
+ \begingroup
+ \newlinechar`\^^M
+ \let\xeatspaces\eatspaces
+ % Undo catcode changes of \startcontents and \doprintindex
+ % When called from @insertcopying or (short)caption, we need active
+ % backslash to get it printed correctly. Previously, we had
+ % \catcode`\\=\other instead. We'll see whether a problem appears
+ % with macro expansion. --kasal, 19aug04
+ \catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+ % ... and \example
+ \spaceisspace
+ %
+ % Append \endinput to make sure that TeX does not see the ending newline.
+ %
+ % I've verified that it is necessary both for e-TeX and for ordinary TeX
+ % --kasal, 29nov03
+ \scantokens{#1\endinput}%
+ \endgroup
+}
-\def\deftpargs #1{\bf \defvarargs{#1}}
+\def\scanexp#1{%
+ \edef\temp{\noexpand\scanmacro{#1}}%
+ \temp
+}
-% @deftp Class window height width ...
+\newcount\paramno % Count of parameters
+\newtoks\macname % Macro name
+\newif\ifrecursive % Is it recursive?
+
+% List of all defined macros in the form
+% \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+ \toks0 = \expandafter{\macrolist\definedummyword#1}%
+ \xdef\macrolist{\the\toks0}%
+}
-\def\deftp{\defmethparsebody\Edeftp\deftpx\deftpheader}
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+% \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+%
+\def\cslet#1#2{%
+ \expandafter\let
+ \csname#1\expandafter\endcsname
+ \csname#2\endcsname
+}
-\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
-\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
-% This definition is run if you use @deftpx, etc
-% anywhere other than immediately after a @deftp, etc.
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
-\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \.
+
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+
+\def\scanctxt{%
+ \catcode`\"=\other
+ \catcode`\+=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\@=\other
+ \catcode`\^=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\~=\other
+}
-\message{cross reference,}
-% Define cross-reference macros
-\newwrite \auxfile
+\def\scanargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+ \catcode`\^^M=\other
+}
-% \setref{foo} defines a cross-reference point named foo.
+\def\macrobodyctxt{%
+ \scanctxt
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\^^M=\other
+ \usembodybackslash
+}
-\def\setref#1{%
-\dosetq{#1-pg}{Ypagenumber}%
-\dosetq{#1-snt}{Ysectionnumberandtype}}
+\def\macroargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+}
-\def\unnumbsetref#1{%
-\dosetq{#1-pg}{Ypagenumber}%
-\dosetq{#1-snt}{Ynothing}}
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
-% \xref and \pxref generate cross references to specified points.
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+ \getargs{#1}% now \macname is the macname and \argl the arglist
+ \ifx\argl\empty % no arguments
+ \paramno=0%
+ \else
+ \expandafter\parsemargdef \argl;%
+ \fi
+ \if1\csname ismacro.\the\macname\endcsname
+ \message{Warning: redefining \the\macname}%
+ \else
+ \expandafter\ifx\csname \the\macname\endcsname \relax
+ \else \errmessage{Macro name \the\macname\space already defined}\fi
+ \global\cslet{macsave.\the\macname}{\the\macname}%
+ \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+ \addtomacrolist{\the\macname}%
+ \fi
+ \begingroup \macrobodyctxt
+ \ifrecursive \expandafter\parsermacbody
+ \else \expandafter\parsemacbody
+ \fi}
+
+\parseargdef\unmacro{%
+ \if1\csname ismacro.#1\endcsname
+ \global\cslet{#1}{macsave.#1}%
+ \global\expandafter\let \csname ismacro.#1\endcsname=0%
+ % Remove the macro name from \macrolist:
+ \begingroup
+ \expandafter\let\csname#1\endcsname \relax
+ \let\definedummyword\unmacrodo
+ \xdef\macrolist{\macrolist}%
+ \endgroup
+ \else
+ \errmessage{Macro #1 not defined}%
+ \fi
+}
-\def\pxref #1{see \xrefX [#1,,,,,,,]}
-\def\xref #1{See \xrefX [#1,,,,,,,]}
-\def\xrefX [#1,#2,#3,#4,#5,#6]{%
-\setbox1=\hbox{\i{\losespace#5{}}}%
-\setbox0=\hbox{\losespace#3{}}%
-\ifdim \wd0 =0pt \setbox0=\hbox{\losespace#1{}}\fi%
-\ifdim \wd1 >0pt%
-section \unhbox0{} in \unhbox1%
-\else%
-\refx{#1-snt} [\unhbox0], page\tie \refx{#1-pg}%
-\fi }
+% Called by \do from \dounmacro on each macro. The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+ \ifx #1\relax
+ % remove this
+ \else
+ \noexpand\definedummyword \noexpand#1%
+ \fi
+}
-% \dosetq is the interface for calls from other macros
+% This makes use of the obscure feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname #1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+
+% Parse the optional {params} list. Set up \paramno and \paramlist
+% so \defmacro knows what to do. Define \macarg.blah for each blah
+% in the params list, to be ##N where N is the position in that list.
+% That gets used by \mbodybackslash (above).
+
+% We need to get `macro parameter char #' into several definitions.
+% The technique used is stolen from LaTeX: let \hash be something
+% unexpandable, insert that wherever you need a #, and then redefine
+% it to # just before using the token list produced.
+%
+% The same technique is used to protect \eatspaces till just before
+% the macro is used.
+
+\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
+ \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
+\def\parsemargdefxxx#1,{%
+ \if#1;\let\next=\relax
+ \else \let\next=\parsemargdefxxx
+ \advance\paramno by 1%
+ \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+ {\xeatspaces{\hash\the\paramno}}%
+ \edef\paramlist{\paramlist\hash\the\paramno,}%
+ \fi\next}
+
+% These two commands read recursive and nonrecursive macro bodies.
+% (They're different since rec and nonrec macros end differently.)
+
+\long\def\parsemacbody#1@end macro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+\long\def\parsermacbody#1@end rmacro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+
+% This defines the macro itself. There are six cases: recursive and
+% nonrecursive macros of zero, one, and many arguments.
+% Much magic with \expandafter here.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in; @include reads the file inside a group.
+\def\defmacro{%
+ \let\hash=##% convert placeholders to macro parameter chars
+ \ifrecursive
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\scanmacro{\temp}}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup\noexpand\scanmacro{\temp}}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{\egroup\noexpand\scanmacro{\temp}}%
+ \fi
+ \else
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \expandafter\noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \fi
+ \fi}
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+% \braceorline decides whether the next nonwhitespace character is a
+% {. If so it reads up to the closing }, if not, it reads the whole
+% line. Whatever was read is then fed to the next control sequence
+% as an argument (by \parsebrace or \parsearg)
+\def\braceorline#1{\let\next=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+ \ifx\nchar\bgroup\else
+ \expandafter\parsearg
+ \fi \next}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign. Just make them active and then expand them all to nothing.
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+ {%
+ \expandafter\let\obeyedspace=\empty
+ \addtomacrolist{#1}%
+ \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+ }%
+ \next
+}
-\def\dosetq #1#2{{\let\folio=0%
-\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
+\message{cross references,}
-\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
+\newwrite\auxfile
-% Things to be expanded by \internalsetq
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
-\def\Ypagenumber{\folio}
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references. The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross, , , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
-\def\Ynothing{}
+\let\nwnode=\node
+\let\lastnode=\empty
-\def\Ysectionnumberandtype{%
-\ifnum\secno=0 chapter\xreftie\the\chapno %
-\else \ifnum \subsecno=0 section\xreftie\the\chapno.\the\secno %
-\else \ifnum \subsubsecno=0 %
-section\xreftie\the\chapno.\the\secno.\the\subsecno %
-\else %
-section\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
-\fi \fi \fi }
-
-\gdef\xreftie{'tie}
-
-% Define @refx to reference a specific cross-reference string.
-
-\def\refx#1{%
-{%
-\expandafter\ifx\csname X#1\endcsname\relax
-% If not defined, say something at least.
-\expandafter\gdef\csname X#1\endcsname {$<$undefined$>$}%
-\message {WARNING: Cross-reference "#1" used but not yet defined}%
-\message {}%
-\fi %
-\csname X#1\endcsname %It's defined, so just use it.
-}}
+% Write a cross-reference definition for the current node. #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+ \ifx\lastnode\empty\else
+ \setref{\lastnode}{#1}%
+ \global\let\lastnode=\empty
+ \fi
+}
-% Read the last existing aux file, if any. No error if none exists.
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \thissection,
+% or the anchor name.
+% 2) NAME-snt - section number and type, passed as the SNT arg, or
+% empty for anchors.
+% 3) NAME-pg - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat. In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+ \pdfmkdest{#1}%
+ \iflinks
+ {%
+ \atdummies % preserve commands, but don't expand them
+ \edef\writexrdef##1##2{%
+ \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+ ##1}{##2}}% these are parameters of \writexrdef
+ }%
+ \toks0 = \expandafter{\thissection}%
+ \immediate \writexrdef{title}{\the\toks0 }%
+ \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+ \writexrdef{pg}{\folio}% will be written later, during \shipout
+ }%
+ \fi
+}
-% This is the macro invoked by entries in the aux file.
-\def\xrdef #1#2{
-{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}}
+% @xref, @pxref, and @ref generate cross-references. 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
+ \unsepspaces
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printedrefname{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual\unskip}%
+ \setbox0=\hbox{\printedrefname\unskip}%
+ \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\printedrefname{\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\printedrefname{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printedrefname{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printedrefname{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % Make link in pdf output.
+ \ifpdf
+ \leavevmode
+ \getfilename{#4}%
+ {\turnoffactive
+ % See comments at \activebackslashdouble.
+ {\activebackslashdouble \xdef\pdfxrefdest{#1}%
+ \backslashparens\pdfxrefdest}%
+ %
+ \ifnum\filenamelength>0
+ \startlink attr{/Border [0 0 0]}%
+ goto file{\the\filename.pdf} name{\pdfxrefdest}%
+ \else
+ \startlink attr{/Border [0 0 0]}%
+ goto name{\pdfmkpgn{\pdfxrefdest}}%
+ \fi
+ }%
+ \linkcolor
+ \fi
+ %
+ % Float references are printed completely differently: "Figure 1.2"
+ % instead of "[somenode], p.3". We distinguish them by the
+ % LABEL-title being set to a magic string.
+ {%
+ % Have to otherify everything special to allow the \csname to
+ % include an _ in the xref name, etc.
+ \indexnofonts
+ \turnoffactive
+ \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+ \csname XR#1-title\endcsname
+ }%
+ \iffloat\Xthisreftitle
+ % If the user specified the print name (third arg) to the ref,
+ % print it instead of our usual "Figure 1.2".
+ \ifdim\wd0 = 0pt
+ \refx{#1-snt}{}%
+ \else
+ \printedrefname
+ \fi
+ %
+ % if the user also gave the printed manual name (fifth arg), append
+ % "in MANUALNAME".
+ \ifdim \wd1 > 0pt
+ \space \putwordin{} \cite{\printedmanual}%
+ \fi
+ \else
+ % node/anchor (non-float) references.
+ %
+ % 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{} ``\printedrefname'' \putwordin{} \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
+ % Only output a following space if the -snt ref is nonempty; for
+ % @unnumbered and @anchor, it won't be.
+ \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+ \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+ }%
+ % output the `[mynode]' via a macro so it can be overridden.
+ \xrefprintnodename\printedrefname
+ %
+ % But we always want a comma and a space:
+ ,\space
+ %
+ % output the `page 3'.
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output. It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents. Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
-{
-\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
-\catcode `\=\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
-\catcode`\|=\other
-\catcode`\<=\other
-\catcode`\>=\other
-\catcode `\$=\other
-\catcode `\#=\other
-\catcode `\&=\other
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+ \ifnum\secno=0
+ \putwordChapter@tie \the\chapno
+ \else \ifnum\subsecno=0
+ \putwordSection@tie \the\chapno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+\def\Yappendix{%
+ \ifnum\secno=0
+ \putwordAppendix@tie @char\the\appendixno{}%
+ \else \ifnum\subsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie
+ @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
-% the aux file uses ' as the escape.
-% 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.
+% 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{%
+ {%
+ \indexnofonts
+ \otherbackslash
+ \expandafter\global\expandafter\let\expandafter\thisrefX
+ \csname XR#1\endcsname
+ }%
+ \ifx\thisrefX\relax
+ % If not defined, say something at least.
+ \angleleft un\-de\-fined\angleright
+ \iflinks
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \thisrefX
+ \fi
+ #2% Output the suffix in any case.
+}
-\catcode `\{=1 \catcode `\}=2
-\catcode `\%=\other
-\catcode `\'=0
-\catcode `\\=\other
+% This is the macro invoked by entries in the aux file. Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions). But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+ \expandafter\gdef\csname XR#1\endcsname{#2}% remember this xref value.
+ %
+ % Was that xref control sequence that we just defined for a float?
+ \expandafter\iffloat\csname XR#1\endcsname
+ % it was a float, and we have the (safe) float type in \iffloattype.
+ \expandafter\let\expandafter\floatlist
+ \csname floatlist\iffloattype\endcsname
+ %
+ % Is this the first time we've seen this float type?
+ \expandafter\ifx\floatlist\relax
+ \toks0 = {\do}% yes, so just \do
+ \else
+ % had it before, so preserve previous elements in list.
+ \toks0 = \expandafter{\floatlist\do}%
+ \fi
+ %
+ % Remember this xref in the control sequence \floatlistFLOATTYPE,
+ % for later use in \listoffloats.
+ \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0{#1}}%
+ \fi
+}
-'openin 1 'jobname.aux
-'ifeof 1 'else 'closein 1 'input 'jobname.aux
-'fi
+% Read the last existing aux file, if any. No error if none exists.
+%
+\def\tryauxfile{%
+ \openin 1 \jobname.aux
+ \ifeof 1 \else
+ \readdatafile{aux}%
+ \global\havexrefstrue
+ \fi
+ \closein 1
}
-% Open the new aux file. Tex will close it automatically at exit.
+\def\setupdatafile{%
+ \catcode`\^^@=\other
+ \catcode`\^^A=\other
+ \catcode`\^^B=\other
+ \catcode`\^^C=\other
+ \catcode`\^^D=\other
+ \catcode`\^^E=\other
+ \catcode`\^^F=\other
+ \catcode`\^^G=\other
+ \catcode`\^^H=\other
+ \catcode`\^^K=\other
+ \catcode`\^^L=\other
+ \catcode`\^^N=\other
+ \catcode`\^^P=\other
+ \catcode`\^^Q=\other
+ \catcode`\^^R=\other
+ \catcode`\^^S=\other
+ \catcode`\^^T=\other
+ \catcode`\^^U=\other
+ \catcode`\^^V=\other
+ \catcode`\^^W=\other
+ \catcode`\^^X=\other
+ \catcode`\^^Z=\other
+ \catcode`\^^[=\other
+ \catcode`\^^\=\other
+ \catcode`\^^]=\other
+ \catcode`\^^^=\other
+ \catcode`\^^_=\other
+ % It was suggested to set the catcode of ^ to 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
+ %
+ % Special characters. Should be turned off anyway, but...
+ \catcode`\~=\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 % avoid \+ for paranoia even though we've turned it off
+ %
+ % This is to support \ in node names and titles, since the \
+ % characters end up in a \csname. It's easier than
+ % leaving it active and making its active definition an actual \
+ % character. What I don't understand is why it works in the *value*
+ % of the xrdef. Seems like it should be a catcode12 \, and that
+ % should not typeset properly. But it works, so I'm moving on for
+ % now. --karl, 15jan04.
+ \catcode`\\=\other
+ %
+ % Make the characters 128-255 be printing characters.
+ {%
+ \count1=128
+ \def\loop{%
+ \catcode\count1=\other
+ \advance\count1 by 1
+ \ifnum \count1<256 \loop \fi
+ }%
+ }%
+ %
+ % @ is our escape character in .aux files, and we need braces.
+ \catcode`\{=1
+ \catcode`\}=2
+ \catcode`\@=0
+}
-\openout \auxfile=\jobname.aux
+\def\readdatafile#1{%
+\begingroup
+ \setupdatafile
+ \input\jobname.#1
+\endgroup}
-% Footnotes.
+\message{insertions,}
+% including 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 }
-\let\ptexfootnote=\footnote
+% @footnotestyle is meaningful for info output only.
+\let\footnotestyle=\comment
{\catcode `\@=11
-\gdef\footnote{\global\advance \footnoteno by \@ne
-\edef\thisfootno{$^{\the\footnoteno}$}%
-\let\@sf\empty
-\ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
-\thisfootno\@sf\parsearg\footnotezzz}
-
-\gdef\footnotezzz #1{\insert\footins{
-\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
-\footstrut\hang\textindent{\thisfootno}#1\strut}}
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \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}\ptexslash\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \dofootnote
+}%
+% 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) fails inside footnotes because the tokens are fixed when
+% the footnote is read. --karl, 16nov96.
+%
+\gdef\dofootnote{%
+ \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.
+ \hsize=\pagewidth
+ \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
+ %
+ \smallfonts \rm
+ %
+ % Because we use hanging indentation in footnotes, a @noindent appears
+ % to exdent this text, so make it be a no-op. makeinfo does not use
+ % hanging indentation so @noindent can still be needed within footnote
+ % text after an @example or the like (not that this is good style).
+ \let\noindent = \relax
+ %
+ % Hang the footnote text off the number. Use \everypar in case the
+ % footnote extends for more than one paragraph.
+ \everypar = {\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
+}
}%end \catcode `\@=11
-% End of control word definitions.
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished. Otherwise, the insertion
+% would be lost.
+% Similarily, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes. --kasal, 16nov03.
-\message{and turning on texinfo input format.}
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+ \ifx \insert\ptexinsert
+ \let\insert\saveinsert
+ \else
+ \let\checkinserts\relax
+ \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+ \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+ \afterassignment\next
+ % swallow the left brace
+ \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+ \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+ {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+ \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-)
+ \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+ \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+ \next
+}
+\def\newsaveinsX #1{%
+ \csname newbox\endcsname #1%
+ \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+ \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
-\newindex{cp}
-\newcodeindex{fn}
-\newcodeindex{vr}
-\newcodeindex{tp}
-\newcodeindex{ky}
-\newcodeindex{pg}
-% Set some numeric style parameters, for 8.5 x 11 format.
+% @image. We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front. If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+ % Do not bother showing banner with epsf.tex v2.7k (available in
+ % doc/epsf.tex and on ctan).
+ \def\epsfannounce{\toks0 = }%
+ \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+ work. It is also included in the Texinfo distribution, or you can get
+ it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+ \ifx\epsfbox\undefined
+ \ifwarnednoepsf \else
+ \errhelp = \noepsfhelp
+ \errmessage{epsf.tex not found, images will be ignored}%
+ \global\warnednoepsftrue
+ \fi
+ \else
+ \imagexxx #1,,,,,\finish
+ \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing this stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+ \catcode`\^^M = 5 % in case we're inside an example
+ \normalturnoffactive % allow _ et al. in names
+ % If the image is by itself, center it.
+ \ifvmode
+ \imagevmodetrue
+ \nobreak\bigskip
+ % Usually we'll have text after the image which will insert
+ % \parskip glue, so insert it here too to equalize the space
+ % above and below.
+ \nobreak\vskip\parskip
+ \nobreak
+ \line\bgroup
+ \fi
+ %
+ % Output the image.
+ \ifpdf
+ \dopdfimage{#1}{#2}{#3}%
+ \else
+ % \epsfbox itself resets \epsf?size at each figure.
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+ \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+ \epsfbox{#1.eps}%
+ \fi
+ %
+ \ifimagevmode \egroup \bigbreak \fi % space after the image
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc. We don't actually implement floating yet, we always include the
+% float "here". But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
-\hsize = 6.5in
-\parindent 15pt
-\parskip 18pt plus 1pt
-\baselineskip 15pt
-\advance\topskip by 1.2cm
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc. Can't contain commas. If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label. Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored. It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+ \let\thiscaption=\empty
+ \let\thisshortcaption=\empty
+ %
+ % don't lose footnotes inside @float.
+ %
+ % BEWARE: when the floats start float, we have to issue warning whenever an
+ % insert appears inside a float which could possibly float. --kasal, 26may04
+ %
+ \startsavinginserts
+ %
+ % We can't be used inside a paragraph.
+ \par
+ %
+ \vtop\bgroup
+ \def\floattype{#1}%
+ \def\floatlabel{#2}%
+ \def\floatloc{#3}% we do nothing with this yet.
+ %
+ \ifx\floattype\empty
+ \let\safefloattype=\empty
+ \else
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ \fi
+ %
+ % If label is given but no type, we handle that as the empty type.
+ \ifx\floatlabel\empty \else
+ % We want each FLOATTYPE to be numbered separately (Figure 1,
+ % Table 1, Figure 2, ...). (And if no label, no number.)
+ %
+ \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+ \global\advance\floatno by 1
+ %
+ {%
+ % This magic value for \thissection is output by \setref as the
+ % XREFLABEL-title value. \xrefX uses it to distinguish float
+ % labels (which have a completely different output format) from
+ % node and anchor labels. And \xrdef uses it to construct the
+ % lists of floats.
+ %
+ \edef\thissection{\floatmagic=\safefloattype}%
+ \setref{\floatlabel}{Yfloat}%
+ }%
+ \fi
+ %
+ % start with \parskip glue, I guess.
+ \vskip\parskip
+ %
+ % Don't suppress indentation if a float happens to start a section.
+ \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption: Foo 1.1
+% @float Foo & @caption{Cap}: Foo: Cap
+% @float Foo & no caption: Foo
+% @float ,lbl & Caption{Cap}: 1.1: Cap
+% @float ,lbl & no caption: 1.1
+% @float & @caption{Cap}: Cap
+% @float & no caption:
+%
+\def\Efloat{%
+ \let\floatident = \empty
+ %
+ % In all cases, if we have a float type, it comes first.
+ \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+ %
+ % If we have an xref label, the number comes next.
+ \ifx\floatlabel\empty \else
+ \ifx\floattype\empty \else % if also had float type, need tie first.
+ \appendtomacro\floatident{\tie}%
+ \fi
+ % the number.
+ \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+ \fi
+ %
+ % Start the printed caption with what we've constructed in
+ % \floatident, but keep it separate; we need \floatident again.
+ \let\captionline = \floatident
+ %
+ \ifx\thiscaption\empty \else
+ \ifx\floatident\empty \else
+ \appendtomacro\captionline{: }% had ident, so need a colon between
+ \fi
+ %
+ % caption text.
+ \appendtomacro\captionline{\scanexp\thiscaption}%
+ \fi
+ %
+ % If we have anything to print, print it, with space before.
+ % Eventually this needs to become an \insert.
+ \ifx\captionline\empty \else
+ \vskip.5\parskip
+ \captionline
+ %
+ % Space below caption.
+ \vskip\parskip
+ \fi
+ %
+ % If have an xref label, write the list of floats info. Do this
+ % after the caption, to avoid chance of it being a breakpoint.
+ \ifx\floatlabel\empty \else
+ % Write the text that goes in the lof to the aux file as
+ % \floatlabel-lof. Besides \floatident, we include the short
+ % caption if specified, else the full caption if specified, else nothing.
+ {%
+ \atdummies
+ %
+ % since we read the caption text in the macro world, where ^^M
+ % is turned into a normal character, we have to scan it back, so
+ % we don't write the literal three characters "^^M" into the aux file.
+ \scanexp{%
+ \xdef\noexpand\gtemp{%
+ \ifx\thisshortcaption\empty
+ \thiscaption
+ \else
+ \thisshortcaption
+ \fi
+ }%
+ }%
+ \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+ \ifx\gtemp\empty \else : \gtemp \fi}}%
+ }%
+ \fi
+ \egroup % end of \vtop
+ %
+ % place the captured inserts
+ %
+ % BEWARE: when the floats start floating, we have to issue warning
+ % whenever an insert appears inside a float which could possibly
+ % float. --kasal, 26may04
+ %
+ \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+ \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use. Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+ \ifx#1\relax
+ % Haven't seen this figure type before.
+ \csname newcount\endcsname #1%
+ %
+ % Remember to reset this floatno at the next chap.
+ \expandafter\gdef\expandafter\resetallfloatnos
+ \expandafter{\resetallfloatnos #1=0 }%
+ \fi
+ \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value. We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref. That is, the magic
+% \thissection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string. If so, #2 will be the
+% (safe) float type for this float. We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+ \def\temp{#1}%
+ \def\iffloattype{#2}%
+ \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+ \def\floattype{#1}% floattype
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ %
+ % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+ \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+ \ifhavexrefs
+ % if the user said @listoffloats foo but never @float foo.
+ \message{\linenumber No `\safefloattype' floats to list.}%
+ \fi
+ \else
+ \begingroup
+ \leftskip=\tocindent % indent these entries like a toc
+ \let\do=\listoffloatsdo
+ \csname floatlist\safefloattype\endcsname
+ \endgroup
+ \fi
+}
+
+% This is called on each entry in a list of floats. We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file. We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+ % Can't fully expand XR#1-lof because it can contain anything. Just
+ % pass the control sequence. On the other hand, XR#1-pg is just the
+ % page number, and we want to fully expand that so we can get a link
+ % in pdf output.
+ \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+ %
+ % use the same \entry macro we use to generate the TOC and index.
+ \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+ \writeentry
+}}
+
+\message{localization,}
+% and i18n.
+
+% @documentlanguage is usually given very early, just after
+% @setfilename. If done too late, it may not override everything
+% properly. Single argument is the language abbreviation.
+% It would be nice if we could set up a hyphenation file here.
+%
+\parseargdef\documentlanguage{%
+ \tex % read txi-??.tex file in plain TeX.
+ % Read the file if it exists.
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \errhelp = \nolanghelp
+ \errmessage{Cannot read language file txi-#1.tex}%
+ \else
+ \input txi-#1.tex
+ \fi
+ \closein 1
+ \endgroup
+}
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty. Maybe you need to install it? In the current directory
+should work if nowhere else does.}
+
+
+% @documentencoding should change something in TeX eventually, most
+% likely, but for now just recognize it.
+\let\documentencoding = \comment
+
+
+% Page size parameters.
+%
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\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
-
-% Use @smallbook to reset parameters for 7x9.5 format
-\def\smallbook{
-\global\lispnarrowing = 0.3in
-\global\baselineskip 12pt
-\global\parskip 3pt plus 1pt
-\global\hsize = 5in
-\global\doublecolumnhsize=2.4in \global\doublecolumnvsize=15.0in
-\global\vsize=7.5in
-\global\tolerance=700
-\global\hfuzz=1pt
-
-\global\pagewidth=\hsize
-\global\pageheight=\vsize
-\global\font\ninett=cmtt9
-
-\global\let\smalllisp=\smalllispx
-\global\let\smallexample=\smalllispx
-\global\def\Esmallexample{\Esmalllisp}
-}
-
-%% 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}
+\vbadness = 10000
+
+% Don't be so finicky about underfull hboxes, either.
+\hbadness = 2000
+
+% 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. We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+ \ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+ \else
+ \emergencystretch = .15\hsize
+ \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading. The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+ \voffset = #3\relax
+ \topskip = #6\relax
+ \splittopskip = \topskip
+ %
+ \vsize = #1\relax
+ \advance\vsize by \topskip
+ \outervsize = \vsize
+ \advance\outervsize by 2\topandbottommargin
+ \pageheight = \vsize
+ %
+ \hsize = #2\relax
+ \outerhsize = \hsize
+ \advance\outerhsize by 0.5in
+ \pagewidth = \hsize
+ %
+ \normaloffset = #4\relax
+ \bindingoffset = #5\relax
+ %
+ \ifpdf
+ \pdfpageheight #7\relax
+ \pdfpagewidth #8\relax
+ \fi
+ %
+ \setleading{\textleading}
+ %
+ \parindent = \defaultparindent
+ \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % If page is nothing but text, make it come out even.
+ \internalpagesizes{46\baselineskip}{6in}%
+ {\voffset}{.25in}%
+ {\bindingoffset}{36pt}%
+ {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+ \parskip = 2pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.5in}{5in}%
+ {\voffset}{.25in}%
+ {\bindingoffset}{16pt}%
+ {9.25in}{7in}%
+ %
+ \lispnarrowing = 0.3in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+ \parskip = 1.5pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.4in}{4.8in}%
+ {-.2in}{-.4in}%
+ {0pt}{14pt}%
+ {9in}{6in}%
+ %
+ \lispnarrowing = 0.25in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % Double-side printing via postscript on Laserjet 4050
+ % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+ % To change the settings for a different printer or situation, adjust
+ % \normaloffset until the front-side and back-side texts align. Then
+ % do the same for \bindingoffset. You can set these for testing in
+ % your texinfo source file like this:
+ % @tex
+ % \global\normaloffset = -6mm
+ % \global\bindingoffset = 10mm
+ % @end tex
+ \internalpagesizes{51\baselineskip}{160mm}
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{44pt}%
+ {297mm}{210mm}%
+ %
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+ \parskip = 2pt plus 1pt minus 0.1pt
+ \textleading = 12.5pt
+ %
+ \internalpagesizes{160mm}{120mm}%
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{8pt}%
+ {210mm}{148mm}%
+ %
+ \lispnarrowing = 0.2in
+ \tolerance = 800
+ \hfuzz = 1.2pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 2mm
+ \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{237mm}{150mm}%
+ {\voffset}{4.6mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ %
+ % Must explicitly reset to 0 because we call \afourpaper.
+ \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{241mm}{165mm}%
+ {\voffset}{-2.95mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+ \globaldefs = 1
+ %
+ \parskip = 3pt plus 2pt minus 1pt
+ \setleading{\textleading}%
+ %
+ \dimen0 = #1
+ \advance\dimen0 by \voffset
+ %
+ \dimen2 = \hsize
+ \advance\dimen2 by \normaloffset
+ %
+ \internalpagesizes{#1}{\hsize}%
+ {\voffset}{\normaloffset}%
+ {\bindingoffset}{44pt}%
+ {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
+
+% 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
+\catcode`\$=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+\def\normaldollar{$}%$ font-lock fix
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), 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\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font. Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts. But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
% Turn off all special characters except @
-% (and those which the user can use as if they were ordinary)
-% Define certain chars to be always in tt font.
+% (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}}
+\def\activedoublequote{{\tt\char34}}
\let"=\activedoublequote
\catcode`\~=\active
-\def~{{\tt \char '176}}
+\def~{{\tt\char126}}
\chardef\hat=`\^
\catcode`\^=\active
\def^{{\tt \hat}}
+
\catcode`\_=\active
-\def_{{\tt \char '137}}
+\def_{\ifusingtt\normalunderscore\_}
+\let\realunder=_
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+
\catcode`\|=\active
-\def|{{\tt \char '174}}
+\def|{{\tt\char124}}
\chardef \less=`\<
\catcode`\<=\active
\def<{{\tt \less}}
\chardef \gtr=`\>
\catcode`\>=\active
\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+\catcode`\$=\active
+\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+
+% 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}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+ \normalturnoffactive
+ \otherbackslash
+}
\catcode`\@=0
-% \rawbackslashxx output one backslash character in current font
-{\catcode`\\=\other
-@gdef@rawbackslashxx{\}}
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work
-% \rawbackslash redefines \ as input to do \rawbackslashxx.
-{\catcode`\\=\active
-@gdef@rawbackslash{@let\=@rawbackslashxx }}
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
-% \normalbackslash outputs one backslash in fixed width font.
-\def\normalbackslash{{\tt\rawbackslashxx}}
+% In texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active
+@def@normalbackslash{{@tt@backslashcurfont}}
+% On startup, @fixbackslash assigns:
+% @let \ = @normalbackslash
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+%
+@def@normalturnoffactive{%
+ @let\=@normalbackslash
+ @let"=@normaldoublequote
+ @let~=@normaltilde
+ @let^=@normalcaret
+ @let_=@normalunderscore
+ @let|=@normalverticalbar
+ @let<=@normalless
+ @let>=@normalgreater
+ @let+=@normalplus
+ @let$=@normaldollar %$ font-lock fix
+ @unsepspaces
+}
+
+% 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 turn back 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
+}
% Say @foo, not \foo, in error messages.
-\escapechar=`\@
+@escapechar = `@@
-%% 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
+% These look ok in all fonts, so just make them not special.
+@catcode`@& = @other
+@catcode`@# = @other
+@catcode`@% = @other
-\catcode 17=0 @c Define control-q
-\catcode`\\=\active
-@let\=@normalbackslash
-@textfonts
-@rm
+@c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c page-delimiter: "^\\\\message"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@ignore
+ arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 8840a321..c4a83298 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -79,6 +79,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
res_gdt.o \
rw_bitmaps.o \
swapfs.o \
+ symlink.o \
tdb.o \
undo_io.o \
unix_io.o \
@@ -153,6 +154,7 @@ SRCS= ext2_err.c \
$(srcdir)/res_gdt.c \
$(srcdir)/rw_bitmaps.c \
$(srcdir)/swapfs.c \
+ $(srcdir)/symlink.c \
$(srcdir)/tdb.c \
$(srcdir)/test_io.c \
$(srcdir)/tst_badblocks.c \
@@ -180,7 +182,7 @@ ELF_SO_VERSION = 2
ELF_IMAGE = libext2fs
ELF_MYDIR = ext2fs
ELF_INSTALL_DIR = $(root_libdir)
-ELF_OTHER_LIBS = -L../.. -lcom_err
+ELF_OTHER_LIBS = -lcom_err
BSDLIB_VERSION = 2.1
BSDLIB_IMAGE = libext2fs
@@ -198,6 +200,7 @@ all:: ext2fs.pc
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
@@ -219,90 +222,100 @@ ext2fs.pc: $(srcdir)/ext2fs.pc.in $(top_builddir)/config.status
$(E) " CONFIG.STATUS $@"
$(Q) cd $(top_builddir); CONFIG_FILES=lib/ext2fs/ext2fs.pc ./config.status
-tst_badblocks: tst_badblocks.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_badblocks: tst_badblocks.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
- $(Q) $(CC) -o tst_badblocks tst_badblocks.o $(STATIC_LIBEXT2FS) \
- $(LIBCOM_ERR)
+ $(Q) $(CC) -o tst_badblocks tst_badblocks.o $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_icount: $(srcdir)/icount.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_icount: $(srcdir)/icount.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_icount $(srcdir)/icount.c -DDEBUG $(ALL_CFLAGS) \
- $(STATIC_LIBEXT2FS) $(LIBCOM_ERR)
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_iscan: tst_iscan.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_iscan: tst_iscan.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
- $(Q) $(CC) -o tst_iscan tst_iscan.o $(STATIC_LIBEXT2FS) $(LIBCOM_ERR)
+ $(Q) $(CC) -o tst_iscan tst_iscan.o $(ALL_LDFLAGS) $(STATIC_LIBEXT2FS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_getsize: tst_getsize.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_getsize: tst_getsize.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_getsize tst_getsize.o $(STATIC_LIBEXT2FS) \
- $(LIBCOM_ERR)
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_ismounted: $(srcdir)/ismounted.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_ismounted: $(srcdir)/ismounted.c $(STATIC_LIBEXT2FS) \
+ $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_ismounted $(srcdir)/ismounted.c \
$(STATIC_LIBEXT2FS) -DDEBUG $(ALL_CFLAGS) \
- $(LIBCOM_ERR)
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_byteswap: tst_byteswap.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_byteswap: tst_byteswap.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_byteswap tst_byteswap.o $(STATIC_LIBEXT2FS) \
- $(LIBCOM_ERR)
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_bitops: tst_bitops.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_bitops: tst_bitops.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_bitops tst_bitops.o $(ALL_CFLAGS) \
- $(STATIC_LIBEXT2FS) $(LIBCOM_ERR)
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_getsectsize: tst_getsectsize.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+tst_getsectsize: tst_getsectsize.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o tst_sectgetsize tst_getsectsize.o \
- $(STATIC_LIBEXT2FS) $(LIBCOM_ERR)
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
tst_types.o: $(srcdir)/tst_types.c ext2_types.h
tst_types: tst_types.o ext2_types.h
$(E) " LD $@"
- $(Q) $(CC) -o tst_types tst_types.o
+ $(Q) $(CC) -o tst_types tst_types.o $(ALL_LDFLAGS) $(SYSLIBS)
tst_super_size.o: $(srcdir)/tst_super_size.c $(srcdir)/ext2_fs.h
tst_super_size: tst_super_size.o
$(E) " LD $@"
- $(Q) $(CC) -o tst_super_size tst_super_size.o
+ $(Q) $(CC) -o tst_super_size tst_super_size.o $(ALL_LDFLAGS) $(SYSLIBS)
tst_fs_struct.o: $(srcdir)/tst_fs_struct.c $(srcdir)/ext2fs.h
tst_fs_struct: tst_fs_struct.o
$(E) " LD $@"
- $(Q) $(CC) -o tst_fs_struct tst_fs_struct.o
+ $(Q) $(CC) -o tst_fs_struct tst_fs_struct.o $(SYSLIBS)
tst_inode_size.o: $(srcdir)/tst_inode_size.c $(srcdir)/ext2_fs.h
tst_inode_size: tst_inode_size.o
$(E) " LD $@"
- $(Q) $(CC) -o tst_inode_size tst_inode_size.o
+ $(Q) $(CC) -o tst_inode_size tst_inode_size.o $(ALL_LDFLAGS) $(SYSLIBS)
ext2_tdbtool: tdbtool.o
$(E) " LD $@"
- $(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o
+ $(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o $(ALL_LDFLAGS) $(SYSLIBS)
extent_dbg.c: $(srcdir)/extent_dbg.ct
$(E) " MK_CMDS $<"
$(Q) $(MK_CMDS) $(srcdir)/extent_dbg.ct
debug_cmds.c debug_cmds.h: $(top_srcdir)/debugfs/debug_cmds.ct
- $(E) " MK_CMDS $<@"
+ $(E) " MK_CMDS $<"
$(Q) $(MK_CMDS) $(top_srcdir)/debugfs/debug_cmds.ct
+extent_cmds.c extent_cmds.h: $(top_srcdir)/debugfs/extent_cmds.ct
+ $(E) " MK_CMDS $<"
+ $(Q) $(MK_CMDS) $(top_srcdir)/debugfs/extent_cmds.ct
+
DEBUG_OBJS= debug_cmds.o debugfs.o util.o ncheck.o icheck.o ls.o \
lsdel.o dump.o set_fields.o logdump.o htree.o unused.o \
- e2freefrag.o filefrag.o
+ e2freefrag.o filefrag.o extent_inode.o extent_cmds.o zap.o
debugfs.o: $(top_srcdir)/debugfs/debugfs.c
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+extent_inode.o: $(top_srcdir)/debugfs/extent_inode.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+
util.o: $(top_srcdir)/debugfs/util.c
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
@@ -343,6 +356,10 @@ unused.o: $(top_srcdir)/debugfs/unused.c
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+zap.o: $(top_srcdir)/debugfs/zap.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+
e2freefrag.o: $(top_srcdir)/misc/e2freefrag.c
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -DDEBUGFS -I$(top_srcdir)/debugfs -c $< -o $@
@@ -355,36 +372,47 @@ tst_bitmaps_cmd.c: tst_bitmaps_cmd.ct
$(E) " MK_CMDS $@"
$(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/tst_bitmaps_cmd.ct
-tst_bitmaps: tst_bitmaps.o tst_bitmaps_cmd.o $(STATIC_LIBEXT2FS) $(DEPLIBSS) \
- $(DEPLIBCOM_ERR)
+tst_bitmaps: tst_bitmaps.o tst_bitmaps_cmd.o $(srcdir)/blkmap64_rb.c \
+ $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBSS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o $@ tst_bitmaps.o tst_bitmaps_cmd.o \
+ -DDEBUG_RB $(srcdir)/blkmap64_rb.c $(ALL_CFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBSS) $(STATIC_LIBCOM_ERR) \
+ $(SYSLIBS)
+
+tst_extents: $(srcdir)/extent.c $(DEBUG_OBJS) $(DEPSTATIC_LIBSS) \
+ $(STATIC_LIBE2P) $(DEPLIBUUID) $(DEPLIBBLKID) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
- $(Q) $(CC) -o $@ tst_bitmaps.o tst_bitmaps_cmd.o $(ALL_CFLAGS) \
- $(STATIC_LIBEXT2FS) $(LIBSS) $(LIBCOM_ERR)
+ $(Q) $(CC) -o tst_extents $(srcdir)/extent.c \
+ $(ALL_CFLAGS) -DDEBUG $(DEBUG_OBJS) $(STATIC_LIBSS) \
+ $(STATIC_LIBE2P) $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS) -I $(top_srcdir)/debugfs
-tst_extents: $(srcdir)/extent.c extent_dbg.c $(DEBUG_OBJS) $(DEPLIBSS) \
- $(LIBE2P) $(DEPLIBUUID) $(DEPLIBBLKID) $(DEPLIBCOM_ERR)
+tst_inline: $(srcdir)/inline.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(E) " LD $@"
- $(Q) $(CC) -o tst_extents $(srcdir)/extent.c extent_dbg.c \
- $(ALL_CFLAGS) -DDEBUG $(DEBUG_OBJS) $(LIBSS) $(LIBE2P) \
- $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) $(LIBCOM_ERR) \
- -I $(top_srcdir)/debugfs
+ $(Q) $(CC) -o tst_inline $(srcdir)/inline.c $(ALL_CFLAGS) -DDEBUG \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
-tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) \
+tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR) $(STATIC_LIBE2P) \
$(top_srcdir)/lib/e2p/e2p.h
$(E) " LD $@"
$(Q) $(CC) -o tst_csum $(srcdir)/csum.c -DDEBUG \
- $(ALL_CFLAGS) $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(LIBE2P)
+ $(ALL_CFLAGS) $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) \
+ $(STATIC_LIBE2P) $(SYSLIBS)
-tst_crc32c: $(srcdir)/crc32c.c
+tst_crc32c: $(srcdir)/crc32c.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
$(Q) $(CC) $(BUILD_LDFLAGS) $(ALL_CFLAGS) -o tst_crc32c $(srcdir)/crc32c.c \
- -DUNITTEST
+ -DUNITTEST $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) \
+ $(SYSLIBS)
mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
$(E) " LD $@"
- $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS)
+ $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG \
+ $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) $(SYSLIBS)
check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
- tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps
+ tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
+ tst_inline
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_bitops
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_badblocks
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_iscan
@@ -393,6 +421,7 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_super_size
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inode_size
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_csum
+ LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inline
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32c
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) \
./tst_bitmaps -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
@@ -403,11 +432,14 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) \
./tst_bitmaps -t 3 -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
+ LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) \
+ ./tst_bitmaps -l -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
+ diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
installdirs::
$(E) " MKINSTALLDIRS $(libdir) $(includedir)/ext2fs"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
- $(DESTDIR)$(includedir)/ext2fs $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(includedir)/ext2fs $(DESTDIR)$(pkgconfigdir)
install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc
$(E) " INSTALL_DATA $(libdir)/libext2fs.a"
@@ -422,12 +454,12 @@ install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc
echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
$(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
done
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/ext2fs.pc"
- $(Q) $(INSTALL_DATA) ext2fs.pc $(DESTDIR)$(libdir)/pkgconfig/ext2fs.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/ext2fs.pc"
+ $(Q) $(INSTALL_DATA) ext2fs.pc $(DESTDIR)$(pkgconfigdir)/ext2fs.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libext2fs.a \
- $(DESTDIR)$(libdir)/pkgconfig/ext2fs.pc
+ $(DESTDIR)$(pkgconfigdir)/ext2fs.pc
$(RM) -rf $(DESTDIR)$(includedir)/ext2fs
clean::
@@ -435,8 +467,9 @@ clean::
tst_badblocks tst_iscan ext2_err.et ext2_err.c ext2_err.h \
tst_byteswap tst_ismounted tst_getsize tst_sectgetsize \
tst_bitops tst_types tst_icount tst_super_size tst_csum \
- tst_bitmaps tst_bitmaps_out tst_bitmaps_cmd.c \
- ext2_tdbtool mkjournal debug_cmds.c \
+ tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
+ tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
+ ext2_tdbtool mkjournal debug_cmds.c extent_cmds.c \
../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
crc32c_table.h gen_crc32ctable tst_crc32c
@@ -453,7 +486,7 @@ $(OBJS): subdirs
gen_crc32ctable: $(srcdir)/gen_crc32ctable.c
$(E) " CC $@"
- $(Q) $(BUILD_CC) $(BUILD_CFLAGS) -o gen_crc32ctable \
+ $(Q) $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o gen_crc32ctable \
$(srcdir)/gen_crc32ctable.c
crc32c_table.h: gen_crc32ctable
@@ -544,10 +577,11 @@ block.o: $(srcdir)/block.c $(top_builddir)/lib/config.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
bmap.o: $(srcdir)/bmap.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
- $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
- $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
- $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/bitops.h
check_desc.o: $(srcdir)/check_desc.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
@@ -642,7 +676,7 @@ fileio.o: $(srcdir)/fileio.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
finddev.o: $(srcdir)/finddev.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
@@ -862,6 +896,12 @@ swapfs.o: $(srcdir)/swapfs.c $(top_builddir)/lib/config.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
+symlink.o: $(srcdir)/symlink.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
tdb.o: $(srcdir)/tdb.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/tdb.h
test_io.o: $(srcdir)/test_io.c $(top_builddir)/lib/config.h \
diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c
index 775dfcc4..0acbc4e6 100644
--- a/lib/ext2fs/alloc.c
+++ b/lib/ext2fs/alloc.c
@@ -27,50 +27,17 @@
#include "ext2fs.h"
/*
- * Check for uninit block bitmaps and deal with them appropriately
+ * Clear the uninit block bitmap flag if necessary
*/
-static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map,
- dgrp_t group)
+static void clear_block_uninit(ext2_filsys fs, dgrp_t group)
{
- blk_t i;
- blk64_t blk, super_blk, old_desc_blk, new_desc_blk;
- int old_desc_blocks;
-
if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
!(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
return;
- blk = (group * fs->super->s_blocks_per_group) +
- fs->super->s_first_data_block;
-
- ext2fs_super_and_bgd_loc2(fs, group, &super_blk,
- &old_desc_blk, &new_desc_blk, 0);
-
- if (fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_META_BG)
- old_desc_blocks = fs->super->s_first_meta_bg;
- else
- old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
-
- for (i=0; i < fs->super->s_blocks_per_group; i++, blk++)
- ext2fs_fast_unmark_block_bitmap2(map, blk);
-
- blk = (group * fs->super->s_blocks_per_group) +
- fs->super->s_first_data_block;
- for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) {
- if ((blk == super_blk) ||
- (old_desc_blk && old_desc_blocks &&
- (blk >= old_desc_blk) &&
- (blk < old_desc_blk + old_desc_blocks)) ||
- (new_desc_blk && (blk == new_desc_blk)) ||
- (blk == ext2fs_block_bitmap_loc(fs, group)) ||
- (blk == ext2fs_inode_bitmap_loc(fs, group)) ||
- (blk >= ext2fs_inode_table_loc(fs, group) &&
- (blk < ext2fs_inode_table_loc(fs, group)
- + fs->inode_blocks_per_group)))
- ext2fs_fast_mark_block_bitmap2(map, blk);
- }
+ /* uninit block bitmaps are now initialized in read_bitmaps() */
+
ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
ext2fs_group_desc_csum_set(fs, group);
ext2fs_mark_super_dirty(fs);
@@ -95,10 +62,11 @@ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map,
ext2fs_fast_unmark_inode_bitmap2(map, ino);
ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
+ /* Mimics what the kernel does */
+ ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
ext2fs_group_desc_csum_set(fs, group);
ext2fs_mark_ib_dirty(fs);
ext2fs_mark_super_dirty(fs);
- check_block_uninit(fs, fs->block_map, group);
}
/*
@@ -169,8 +137,8 @@ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
ext2fs_block_bitmap map, blk64_t *ret)
{
- blk64_t i;
- int c_ratio;
+ errcode_t retval;
+ blk64_t b = 0;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
@@ -180,29 +148,21 @@ errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
return EXT2_ET_NO_BLOCK_BITMAP;
if (!goal || (goal >= ext2fs_blocks_count(fs->super)))
goal = fs->super->s_first_data_block;
- i = goal;
- c_ratio = 1 << ext2fs_get_bitmap_granularity(map);
- if (c_ratio > 1)
- goal &= ~EXT2FS_CLUSTER_MASK(fs);
- check_block_uninit(fs, map,
- (i - fs->super->s_first_data_block) /
- EXT2_BLOCKS_PER_GROUP(fs->super));
- do {
- if (((i - fs->super->s_first_data_block) %
- EXT2_BLOCKS_PER_GROUP(fs->super)) == 0)
- check_block_uninit(fs, map,
- (i - fs->super->s_first_data_block) /
- EXT2_BLOCKS_PER_GROUP(fs->super));
-
- if (!ext2fs_fast_test_block_bitmap2(map, i)) {
- *ret = i;
- return 0;
- }
- i = (i + c_ratio) & ~(c_ratio - 1);
- if (i >= ext2fs_blocks_count(fs->super))
- i = fs->super->s_first_data_block;
- } while (i != goal);
- return EXT2_ET_BLOCK_ALLOC_FAIL;
+ goal &= ~EXT2FS_CLUSTER_MASK(fs);
+
+ retval = ext2fs_find_first_zero_block_bitmap2(map,
+ goal, ext2fs_blocks_count(fs->super) - 1, &b);
+ if ((retval == ENOENT) && (goal != fs->super->s_first_data_block))
+ retval = ext2fs_find_first_zero_block_bitmap2(map,
+ fs->super->s_first_data_block, goal - 1, &b);
+ if (retval == ENOENT)
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+ if (retval)
+ return retval;
+
+ clear_block_uninit(fs, ext2fs_group_of_blk2(fs, b));
+ *ret = b;
+ return 0;
}
errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
diff --git a/lib/ext2fs/alloc_sb.c b/lib/ext2fs/alloc_sb.c
index 0d1c0000..8788c009 100644
--- a/lib/ext2fs/alloc_sb.c
+++ b/lib/ext2fs/alloc_sb.c
@@ -47,7 +47,7 @@ int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
{
blk64_t super_blk, old_desc_blk, new_desc_blk;
blk_t used_blks;
- int j, old_desc_blocks, num_blocks;
+ int old_desc_blocks, num_blocks;
ext2fs_super_and_bgd_loc2(fs, group, &super_blk,
&old_desc_blk, &new_desc_blk, &used_blks);
@@ -65,12 +65,11 @@ int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
ext2fs_mark_block_bitmap2(bmap, 0);
if (old_desc_blk) {
- if (fs->super->s_reserved_gdt_blocks && fs->block_map == bmap)
- ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
- for (j=0; j < old_desc_blocks; j++)
- if (old_desc_blk + j < ext2fs_blocks_count(fs->super))
- ext2fs_mark_block_bitmap2(bmap,
- old_desc_blk + j);
+ num_blocks = old_desc_blocks;
+ if (old_desc_blk + num_blocks >= ext2fs_blocks_count(fs->super))
+ num_blocks = ext2fs_blocks_count(fs->super) -
+ old_desc_blk;
+ ext2fs_mark_block_bitmap_range2(bmap, old_desc_blk, num_blocks);
}
if (new_desc_blk)
ext2fs_mark_block_bitmap2(bmap, new_desc_blk);
diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c
index adec3636..1f58e001 100644
--- a/lib/ext2fs/alloc_stats.c
+++ b/lib/ext2fs/alloc_stats.c
@@ -106,3 +106,44 @@ void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs,
fs->block_alloc_stats = func;
}
+
+void ext2fs_block_alloc_stats_range(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse)
+{
+#ifndef OMIT_COM_ERR
+ if (blk + num > ext2fs_blocks_count(fs->super)) {
+ com_err("ext2fs_block_alloc_stats_range", 0,
+ "Illegal block range: %llu (%u) ",
+ (unsigned long long) blk, num);
+ return;
+ }
+#endif
+ if (inuse == 0)
+ return;
+ if (inuse > 0) {
+ ext2fs_mark_block_bitmap_range2(fs->block_map, blk, num);
+ inuse = 1;
+ } else {
+ ext2fs_unmark_block_bitmap_range2(fs->block_map, blk, num);
+ inuse = -1;
+ }
+ while (num) {
+ int group = ext2fs_group_of_blk2(fs, blk);
+ blk64_t last_blk = ext2fs_group_last_block2(fs, group);
+ blk_t n = num;
+
+ if (blk + num > last_blk)
+ n = last_blk - blk + 1;
+
+ ext2fs_bg_free_blocks_count_set(fs, group,
+ ext2fs_bg_free_blocks_count(fs, group) -
+ inuse*n/EXT2FS_CLUSTER_RATIO(fs));
+ ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, group);
+ ext2fs_free_blocks_count_add(fs->super, -inuse * n);
+ blk += n;
+ num -= n;
+ }
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+}
diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c
index 9f3d4e04..bc99943c 100644
--- a/lib/ext2fs/alloc_tables.c
+++ b/lib/ext2fs/alloc_tables.c
@@ -54,8 +54,8 @@ static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk,
* Don't do a long search if the previous block
* search is still valid.
*/
- if (start_blk && ext2fs_test_block_bitmap_range2(bmap, start_blk,
- elem_size))
+ if (start_blk && start_blk < ext2fs_blocks_count(fs->super) &&
+ ext2fs_test_block_bitmap_range2(bmap, start_blk, elem_size))
return start_blk;
start_blk = ext2fs_group_first_block2(fs, flexbg_size * flexbg);
@@ -83,9 +83,8 @@ static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk,
errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
ext2fs_block_bitmap bmap)
{
- unsigned int j;
errcode_t retval;
- blk64_t group_blk, start_blk, last_blk, new_blk, blk;
+ blk64_t group_blk, start_blk, last_blk, new_blk;
dgrp_t last_grp = 0;
int rem_grps = 0, flexbg_size = 0;
@@ -205,19 +204,12 @@ errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
bmap, &new_blk);
if (retval)
return retval;
- for (j=0, blk = new_blk;
- j < fs->inode_blocks_per_group;
- j++, blk++) {
- ext2fs_mark_block_bitmap2(bmap, blk);
- if (flexbg_size) {
- dgrp_t gr = ext2fs_group_of_blk2(fs, blk);
- ext2fs_bg_free_blocks_count_set(fs, gr, ext2fs_bg_free_blocks_count(fs, gr) - 1);
- ext2fs_free_blocks_count_add(fs->super, -1);
- ext2fs_bg_flags_clear(fs, gr,
- EXT2_BG_BLOCK_UNINIT);
- ext2fs_group_desc_csum_set(fs, gr);
- }
- }
+ if (flexbg_size)
+ ext2fs_block_alloc_stats_range(fs, new_blk,
+ fs->inode_blocks_per_group, +1);
+ else
+ ext2fs_mark_block_bitmap_range2(fs->block_map,
+ new_blk, fs->inode_blocks_per_group);
ext2fs_inode_table_loc_set(fs, group, new_blk);
}
ext2fs_group_desc_csum_set(fs, group);
diff --git a/lib/ext2fs/bitops.c b/lib/ext2fs/bitops.c
index 9322a353..8e4c05c4 100644
--- a/lib/ext2fs/bitops.c
+++ b/lib/ext2fs/bitops.c
@@ -116,3 +116,43 @@ int ext2fs_test_bit64(__u64 nr, const void * addr)
return (mask & *ADDR);
}
+static unsigned int popcount8(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res + (res >> 4)) & 0x0F;
+}
+
+static unsigned int popcount32(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+unsigned int ext2fs_bitcount(const void *addr, unsigned int nbytes)
+{
+ const unsigned char *cp = addr;
+ const __u32 *p;
+ unsigned int res = 0;
+
+ while (((((unsigned long) cp) & 3) != 0) && (nbytes > 0)) {
+ res += popcount8(*cp++);
+ nbytes--;
+ }
+ p = (const __u32 *) cp;
+
+ while (nbytes > 4) {
+ res += popcount32(*p++);
+ nbytes -= 4;
+ }
+ cp = (const unsigned char *) p;
+
+ while (nbytes > 0) {
+ res += popcount8(*cp++);
+ nbytes--;
+ }
+ return res;
+}
diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h
index 93827907..4fb7dc6e 100644
--- a/lib/ext2fs/bitops.h
+++ b/lib/ext2fs/bitops.h
@@ -10,20 +10,6 @@
* %End-Header%
*/
-extern int ext2fs_set_bit(unsigned int nr,void * addr);
-extern int ext2fs_clear_bit(unsigned int nr, void * addr);
-extern int ext2fs_test_bit(unsigned int nr, const void * addr);
-extern void ext2fs_fast_set_bit(unsigned int nr,void * addr);
-extern void ext2fs_fast_clear_bit(unsigned int nr, void * addr);
-extern int ext2fs_set_bit64(__u64 nr,void * addr);
-extern int ext2fs_clear_bit64(__u64 nr, void * addr);
-extern int ext2fs_test_bit64(__u64 nr, const void * addr);
-extern void ext2fs_fast_set_bit64(__u64 nr,void * addr);
-extern void ext2fs_fast_clear_bit64(__u64 nr, void * addr);
-extern __u16 ext2fs_swab16(__u16 val);
-extern __u32 ext2fs_swab32(__u32 val);
-extern __u64 ext2fs_swab64(__u64 val);
-
#ifdef WORDS_BIGENDIAN
#define ext2fs_cpu_to_le64(x) ext2fs_swab64((x))
#define ext2fs_le64_to_cpu(x) ext2fs_swab64((x))
@@ -67,6 +53,15 @@ extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
int code, unsigned long arg);
+#ifdef NO_INLINE_FUNCS
+extern void ext2fs_fast_set_bit(unsigned int nr,void * addr);
+extern void ext2fs_fast_clear_bit(unsigned int nr, void * addr);
+extern void ext2fs_fast_set_bit64(__u64 nr,void * addr);
+extern void ext2fs_fast_clear_bit64(__u64 nr, void * addr);
+extern __u16 ext2fs_swab16(__u16 val);
+extern __u32 ext2fs_swab32(__u32 val);
+extern __u64 ext2fs_swab64(__u64 val);
+
extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
blk_t block);
@@ -95,6 +90,15 @@ extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap);
extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap);
extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap);
+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);
+#endif
+
+/* These functions routines moved to gen_bitmap.c */
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,
@@ -103,15 +107,6 @@ extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
blk_t block, int num);
extern int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap,
ino_t inode, 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);
-extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
-
-/* These routines moved to gen_bitmap.c (actually, some of the above, too) */
extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
__u32 bitno);
extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
@@ -120,11 +115,13 @@ extern int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
blk_t bitno);
extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
blk_t block, int num);
+extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
extern __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap);
extern __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap);
/* 64-bit versions */
+#ifdef NO_INLINE_FUNCS
extern int ext2fs_mark_block_bitmap2(ext2fs_block_bitmap bitmap,
blk64_t block);
extern int ext2fs_unmark_block_bitmap2(ext2fs_block_bitmap bitmap,
@@ -153,10 +150,18 @@ extern void ext2fs_fast_unmark_inode_bitmap2(ext2fs_inode_bitmap bitmap,
extern int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap,
ext2_ino_t inode);
extern errcode_t ext2fs_find_first_zero_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out);
+extern errcode_t ext2fs_find_first_zero_inode_bitmap2(ext2fs_inode_bitmap bitmap,
ext2_ino_t start,
ext2_ino_t end,
ext2_ino_t *out);
-extern errcode_t ext2fs_find_first_zero_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+extern errcode_t ext2fs_find_first_set_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out);
+extern errcode_t ext2fs_find_first_set_inode_bitmap2(ext2fs_inode_bitmap bitmap,
ext2_ino_t start,
ext2_ino_t end,
ext2_ino_t *out);
@@ -174,6 +179,8 @@ extern void ext2fs_fast_mark_block_bitmap_range2(ext2fs_block_bitmap bitmap,
extern void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap,
blk64_t block,
unsigned int num);
+#endif
+
/* These routines moved to gen_bitmap64.c */
extern void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap);
extern errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
@@ -199,6 +206,9 @@ extern void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap,
extern errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 start, __u64 end,
__u64 *out);
+extern errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end,
+ __u64 *out);
/*
* The inline routines themselves...
@@ -209,7 +219,7 @@ extern errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitma
*/
#ifdef NO_INLINE_FUNCS
#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \
- defined(__i586__) || defined(__mc68000__)))
+ defined(__i586__)))
/* This prevents bitops.c from trying to include the C */
/* function version of these functions */
#define _EXT2_HAVE_ASM_BITOPS_
@@ -218,14 +228,22 @@ extern errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitma
#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
#ifdef INCLUDE_INLINE_FUNCS
-#define _INLINE_ extern
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ extern inline
#else
+#define _INLINE_ inline
+#endif
+#else /* !INCLUDE_INLINE FUNCS */
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else /* not C99 */
#ifdef __GNUC__
#define _INLINE_ extern __inline__
#else /* For Watcom C */
#define _INLINE_ extern inline
-#endif
-#endif
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
+#endif /* INCLUDE_INLINE_FUNCS */
/*
* Fast bit set/clear functions that doesn't need to return the
@@ -237,7 +255,7 @@ _INLINE_ void ext2fs_fast_set_bit(unsigned int nr,void * addr)
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
- *ADDR |= (1 << (nr & 0x07));
+ *ADDR |= (unsigned char) (1 << (nr & 0x07));
}
_INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr)
@@ -245,7 +263,7 @@ _INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr)
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
- *ADDR &= ~(1 << (nr & 0x07));
+ *ADDR &= (unsigned char) ~(1 << (nr & 0x07));
}
@@ -254,7 +272,7 @@ _INLINE_ void ext2fs_fast_set_bit64(__u64 nr, void * addr)
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
- *ADDR |= (1 << (nr & 0x07));
+ *ADDR |= (unsigned char) (1 << (nr & 0x07));
}
_INLINE_ void ext2fs_fast_clear_bit64(__u64 nr, void * addr)
@@ -262,7 +280,7 @@ _INLINE_ void ext2fs_fast_clear_bit64(__u64 nr, void * addr)
unsigned char *ADDR = (unsigned char *) addr;
ADDR += nr >> 3;
- *ADDR &= ~(1 << (nr & 0x07));
+ *ADDR &= (unsigned char) ~(1 << (nr & 0x07));
}
@@ -346,49 +364,12 @@ _INLINE_ __u16 ext2fs_swab16(__u16 val)
#endif /* i386 */
-#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \
- (defined(__mc68000__)))
-
-#define _EXT2_HAVE_ASM_BITOPS_
-
-_INLINE_ int ext2fs_set_bit(unsigned int nr,void * addr)
-{
- char retval;
-
- __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0"
- : "=d" (retval) : "d" (nr^7), "a" (addr));
-
- return retval;
-}
-
-_INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr)
-{
- char retval;
-
- __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0"
- : "=d" (retval) : "d" (nr^7), "a" (addr));
-
- return retval;
-}
-
-_INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr)
-{
- char retval;
-
- __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0"
- : "=d" (retval) : "d" (nr^7), "a" (addr));
-
- return retval;
-}
-
-#endif /* __mc68000__ */
-
#if !defined(_EXT2_HAVE_ASM_SWAB_)
_INLINE_ __u16 ext2fs_swab16(__u16 val)
{
- return (val >> 8) | (val << 8);
+ return (val >> 8) | (__u16) (val << 8);
}
_INLINE_ __u32 ext2fs_swab32(__u32 val)
@@ -401,7 +382,7 @@ _INLINE_ __u32 ext2fs_swab32(__u32 val)
_INLINE_ __u64 ext2fs_swab64(__u64 val)
{
- return (ext2fs_swab32(val >> 32) |
+ return (ext2fs_swab32((__u32) (val >> 32)) |
(((__u64)ext2fs_swab32(val & 0xFFFFFFFFUL)) << 32));
}
@@ -605,9 +586,9 @@ _INLINE_ int ext2fs_fast_test_inode_bitmap2(ext2fs_inode_bitmap bitmap,
}
_INLINE_ errcode_t ext2fs_find_first_zero_block_bitmap2(ext2fs_block_bitmap bitmap,
- ext2_ino_t start,
- ext2_ino_t end,
- ext2_ino_t *out)
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out)
{
__u64 o;
errcode_t rv;
@@ -630,10 +611,40 @@ _INLINE_ errcode_t ext2fs_find_first_zero_inode_bitmap2(ext2fs_inode_bitmap bitm
rv = ext2fs_find_first_zero_generic_bmap((ext2fs_generic_bitmap) bitmap,
start, end, &o);
if (!rv)
+ *out = (ext2_ino_t) o;
+ return rv;
+}
+
+_INLINE_ errcode_t ext2fs_find_first_set_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_set_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
*out = o;
return rv;
}
+_INLINE_ errcode_t ext2fs_find_first_set_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t start,
+ ext2_ino_t end,
+ ext2_ino_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_set_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
+ *out = (ext2_ino_t) o;
+ return rv;
+}
+
_INLINE_ blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap)
{
return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap);
@@ -641,7 +652,7 @@ _INLINE_ blk64_t ext2fs_get_block_bitmap_start2(ext2fs_block_bitmap bitmap)
_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start2(ext2fs_inode_bitmap bitmap)
{
- return ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap);
+ return (ext2_ino_t) ext2fs_get_generic_bmap_start((ext2fs_generic_bitmap) bitmap);
}
_INLINE_ blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap)
@@ -651,7 +662,7 @@ _INLINE_ blk64_t ext2fs_get_block_bitmap_end2(ext2fs_block_bitmap bitmap)
_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end2(ext2fs_inode_bitmap bitmap)
{
- return ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap);
+ return (ext2_ino_t) ext2fs_get_generic_bmap_end((ext2fs_generic_bitmap) bitmap);
}
_INLINE_ int ext2fs_fast_test_block_bitmap_range2(ext2fs_block_bitmap bitmap,
@@ -678,3 +689,13 @@ _INLINE_ void ext2fs_fast_unmark_block_bitmap_range2(ext2fs_block_bitmap bitmap,
#undef _INLINE_
#endif
+#ifndef _EXT2_HAVE_ASM_BITOPS_
+extern int ext2fs_set_bit(unsigned int nr,void * addr);
+extern int ext2fs_clear_bit(unsigned int nr, void * addr);
+extern int ext2fs_test_bit(unsigned int nr, const void * addr);
+#endif
+
+extern int ext2fs_set_bit64(__u64 nr,void * addr);
+extern int ext2fs_clear_bit64(__u64 nr, void * addr);
+extern int ext2fs_test_bit64(__u64 nr, const void * addr);
+extern unsigned int ext2fs_bitcount(const void *addr, unsigned int nbytes);
diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c
index 8eddde9a..894293a1 100644
--- a/lib/ext2fs/blkmap64_ba.c
+++ b/lib/ext2fs/blkmap64_ba.c
@@ -328,12 +328,6 @@ static errcode_t ba_find_first_zero(ext2fs_generic_bitmap bitmap,
const unsigned char *pos;
unsigned long max_loop_count, i;
- if (start < bitmap->start || end > bitmap->end || start > end)
- return EINVAL;
-
- if (bitmap->cluster_bits)
- return EINVAL;
-
/* scan bits until we hit a byte boundary */
while ((bitpos & 0x7) != 0 && count > 0) {
if (!ext2fs_test_bit64(bitpos, bp->bitarray)) {
@@ -397,6 +391,80 @@ static errcode_t ba_find_first_zero(ext2fs_generic_bitmap bitmap,
return ENOENT;
}
+/* Find the first one bit between start and end, inclusive. */
+static errcode_t ba_find_first_set(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ ext2fs_ba_private bp = (ext2fs_ba_private)bitmap->private;
+ unsigned long bitpos = start - bitmap->start;
+ unsigned long count = end - start + 1;
+ int byte_found = 0; /* whether a != 0xff byte has been found */
+ const unsigned char *pos;
+ unsigned long max_loop_count, i;
+
+ /* scan bits until we hit a byte boundary */
+ while ((bitpos & 0x7) != 0 && count > 0) {
+ if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ count--;
+ }
+
+ if (!count)
+ return ENOENT;
+
+ pos = ((unsigned char *)bp->bitarray) + (bitpos >> 3);
+ /* scan bytes until 8-byte (64-bit) aligned */
+ while (count >= 8 && (((unsigned long)pos) & 0x07)) {
+ if (*pos != 0) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ count -= 8;
+ bitpos += 8;
+ }
+
+ if (!byte_found) {
+ max_loop_count = count >> 6; /* 8-byte blocks */
+ i = max_loop_count;
+ while (i) {
+ if (*((const __u64 *)pos) != 0)
+ break;
+ pos += 8;
+ i--;
+ }
+ count -= 64 * (max_loop_count - i);
+ bitpos += 64 * (max_loop_count - i);
+
+ max_loop_count = count >> 3;
+ i = max_loop_count;
+ while (i) {
+ if (*pos != 0) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ i--;
+ }
+ count -= 8 * (max_loop_count - i);
+ bitpos += 8 * (max_loop_count - i);
+ }
+
+ /* Here either count < 8 or byte_found == 1. */
+ while (count-- > 0) {
+ if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ }
+
+ return ENOENT;
+}
+
struct ext2_bitmap_ops ext2fs_blkmap64_bitarray = {
.type = EXT2FS_BMAP64_BITARRAY,
.new_bmap = ba_new_bmap,
@@ -413,5 +481,6 @@ struct ext2_bitmap_ops ext2fs_blkmap64_bitarray = {
.get_bmap_range = ba_get_bmap_range,
.clear_bmap = ba_clear_bmap,
.print_stats = ba_print_stats,
- .find_first_zero = ba_find_first_zero
+ .find_first_zero = ba_find_first_zero,
+ .find_first_set = ba_find_first_set
};
diff --git a/lib/ext2fs/blkmap64_rb.c b/lib/ext2fs/blkmap64_rb.c
index aba7cfd3..4dcb03f9 100644
--- a/lib/ext2fs/blkmap64_rb.c
+++ b/lib/ext2fs/blkmap64_rb.c
@@ -33,19 +33,31 @@
struct bmap_rb_extent {
struct rb_node node;
__u64 start;
- __u32 count;
+ __u64 count;
};
struct ext2fs_rb_private {
struct rb_root root;
- struct bmap_rb_extent **wcursor;
- struct bmap_rb_extent **rcursor;
+ struct bmap_rb_extent *wcursor;
+ struct bmap_rb_extent *rcursor;
+ struct bmap_rb_extent *rcursor_next;
#ifdef BMAP_STATS_OPS
__u64 mark_hit;
__u64 test_hit;
#endif
};
+inline static struct bmap_rb_extent *node_to_extent(struct rb_node *node)
+{
+ /*
+ * This depends on the fact the struct rb_node is at the
+ * beginning of the bmap_rb_extent structure. We use this
+ * instead of the ext2fs_rb_entry macro because it causes gcc
+ * -Wall to generate a huge amount of noise.
+ */
+ return (struct bmap_rb_extent *) node;
+}
+
static int rb_insert_extent(__u64 start, __u64 count,
struct ext2fs_rb_private *);
static void rb_get_new_extent(struct bmap_rb_extent **, __u64, __u64);
@@ -62,30 +74,30 @@ static void print_tree(struct rb_root *root)
node = ext2fs_rb_first(root);
for (node = ext2fs_rb_first(root); node != NULL;
node = ext2fs_rb_next(node)) {
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
printf("\t\t\t--> (%llu -> %llu)\n",
ext->start, ext->start + ext->count);
}
printf("\t\t\t=================================\n");
}
-static int check_tree(struct rb_root *root, const char *msg)
+static void check_tree(struct rb_root *root, const char *msg)
{
struct rb_node *new_node, *node, *next;
struct bmap_rb_extent *ext, *old = NULL;
for (node = ext2fs_rb_first(root); node;
node = ext2fs_rb_next(node)) {
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
- if (ext->count <= 0) {
- printf("Tree Error: count is crazy\n");
- printf("extent: %llu -> %llu (%u)\n", ext->start,
+ ext = node_to_extent(node);
+ if (ext->count == 0) {
+ printf("Tree Error: count is zero\n");
+ printf("extent: %llu -> %llu (%llu)\n", ext->start,
ext->start + ext->count, ext->count);
goto err_out;
}
- if (ext->start < 0) {
- printf("Tree Error: start is crazy\n");
- printf("extent: %llu -> %llu (%u)\n", ext->start,
+ if (ext->start + ext->count < ext->start) {
+ printf("Tree Error: start or count is crazy\n");
+ printf("extent: %llu -> %llu (%llu)\n", ext->start,
ext->start + ext->count, ext->count);
goto err_out;
}
@@ -93,20 +105,20 @@ static int check_tree(struct rb_root *root, const char *msg)
if (old) {
if (old->start > ext->start) {
printf("Tree Error: start is crazy\n");
- printf("extent: %llu -> %llu (%u)\n",
+ printf("extent: %llu -> %llu (%llu)\n",
old->start, old->start + old->count,
old->count);
- printf("extent next: %llu -> %llu (%u)\n",
+ printf("extent next: %llu -> %llu (%llu)\n",
ext->start, ext->start + ext->count,
ext->count);
goto err_out;
}
if ((old->start + old->count) >= ext->start) {
printf("Tree Error: extent is crazy\n");
- printf("extent: %llu -> %llu (%u)\n",
+ printf("extent: %llu -> %llu (%llu)\n",
old->start, old->start + old->count,
old->count);
- printf("extent next: %llu -> %llu (%u)\n",
+ printf("extent next: %llu -> %llu (%llu)\n",
ext->start, ext->start + ext->count,
ext->count);
goto err_out;
@@ -114,7 +126,7 @@ static int check_tree(struct rb_root *root, const char *msg)
}
old = ext;
}
- return 0;
+ return;
err_out:
printf("%s\n", msg);
@@ -122,8 +134,8 @@ err_out:
exit(1);
}
#else
-#define check_tree(root, msg) 0
-#define print_tree(root, msg) 0
+#define check_tree(root, msg) do {} while (0)
+#define print_tree(root) do {} while (0)
#endif
static void rb_get_new_extent(struct bmap_rb_extent **ext, __u64 start,
@@ -148,10 +160,12 @@ inline
static void rb_free_extent(struct ext2fs_rb_private *bp,
struct bmap_rb_extent *ext)
{
- if (*bp->wcursor == ext)
- *bp->wcursor = NULL;
- if (*bp->rcursor == ext)
- *bp->rcursor = NULL;
+ if (bp->wcursor == ext)
+ bp->wcursor = NULL;
+ if (bp->rcursor == ext)
+ bp->rcursor = NULL;
+ if (bp->rcursor_next == ext)
+ bp->rcursor_next = NULL;
ext2fs_free_mem(&ext);
}
@@ -165,14 +179,9 @@ static errcode_t rb_alloc_private_data (ext2fs_generic_bitmap bitmap)
return retval;
bp->root = RB_ROOT;
- retval = ext2fs_get_mem(sizeof(struct bmap_rb_extent *), &bp->rcursor);
- if (retval)
- return retval;
- retval = ext2fs_get_mem(sizeof(struct bmap_rb_extent *), &bp->wcursor);
- if (retval)
- return retval;
- *bp->rcursor = NULL;
- *bp->wcursor = NULL;
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+ bp->wcursor = NULL;
#ifdef BMAP_STATS_OPS
bp->test_hit = 0;
@@ -202,7 +211,7 @@ static void rb_free_tree(struct rb_root *root)
for (node = ext2fs_rb_first(root); node; node = next) {
next = ext2fs_rb_next(node);
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
ext2fs_rb_erase(node, root);
ext2fs_free_mem(&ext);
}
@@ -215,8 +224,6 @@ static void rb_free_bmap(ext2fs_generic_bitmap bitmap)
bp = (struct ext2fs_rb_private *) bitmap->private;
rb_free_tree(&bp->root);
- ext2fs_free_mem(&bp->rcursor);
- ext2fs_free_mem(&bp->wcursor);
ext2fs_free_mem(&bp);
bp = 0;
}
@@ -235,12 +242,12 @@ static errcode_t rb_copy_bmap(ext2fs_generic_bitmap src,
src_bp = (struct ext2fs_rb_private *) src->private;
dest_bp = (struct ext2fs_rb_private *) dest->private;
- *src_bp->rcursor = NULL;
- *dest_bp->rcursor = NULL;
+ src_bp->rcursor = NULL;
+ dest_bp->rcursor = NULL;
src_node = ext2fs_rb_first(&src_bp->root);
while (src_node) {
- src_ext = ext2fs_rb_entry(src_node, struct bmap_rb_extent, node);
+ src_ext = node_to_extent(src_node);
retval = ext2fs_get_mem(sizeof (struct bmap_rb_extent),
&dest_ext);
if (retval)
@@ -273,7 +280,7 @@ static void rb_truncate(__u64 new_max, struct rb_root *root)
node = ext2fs_rb_last(root);
while (node) {
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
if ((ext->start + ext->count - 1) <= new_max)
break;
@@ -299,8 +306,8 @@ static errcode_t rb_resize_bmap(ext2fs_generic_bitmap bmap,
}
bp = (struct ext2fs_rb_private *) bmap->private;
- *bp->rcursor = NULL;
- *bp->wcursor = NULL;
+ bp->rcursor = NULL;
+ bp->wcursor = NULL;
/* truncate tree to new_real_end size */
rb_truncate(new_real_end, &bp->root);
@@ -314,13 +321,12 @@ static errcode_t rb_resize_bmap(ext2fs_generic_bitmap bmap,
inline static int
rb_test_bit(struct ext2fs_rb_private *bp, __u64 bit)
{
- struct bmap_rb_extent *rcursor;
- struct rb_node *parent = NULL;
+ struct bmap_rb_extent *rcursor, *next_ext = NULL;
+ struct rb_node *parent = NULL, *next;
struct rb_node **n = &bp->root.rb_node;
struct bmap_rb_extent *ext;
- int i=0;
- rcursor = *bp->rcursor;
+ rcursor = bp->rcursor;
if (!rcursor)
goto search_tree;
@@ -331,7 +337,26 @@ rb_test_bit(struct ext2fs_rb_private *bp, __u64 bit)
return 1;
}
- rcursor = *bp->wcursor;
+ next_ext = bp->rcursor_next;
+ if (!next_ext) {
+ next = ext2fs_rb_next(&rcursor->node);
+ if (next)
+ next_ext = node_to_extent(next);
+ bp->rcursor_next = next_ext;
+ }
+ if (next_ext) {
+ if ((bit >= rcursor->start + rcursor->count) &&
+ (bit < next_ext->start)) {
+#ifdef BMAP_STATS_OPS
+ bp->test_hit++;
+#endif
+ return 0;
+ }
+ }
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+
+ rcursor = bp->wcursor;
if (!rcursor)
goto search_tree;
@@ -342,13 +367,14 @@ search_tree:
while (*n) {
parent = *n;
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if (bit < ext->start)
n = &(*n)->rb_left;
else if (bit >= (ext->start + ext->count))
n = &(*n)->rb_right;
else {
- *bp->rcursor = ext;
+ bp->rcursor = ext;
+ bp->rcursor_next = NULL;
return 1;
}
}
@@ -365,7 +391,8 @@ static int rb_insert_extent(__u64 start, __u64 count,
struct bmap_rb_extent *ext;
int retval = 0;
- ext = *bp->wcursor;
+ bp->rcursor_next = NULL;
+ ext = bp->wcursor;
if (ext) {
if (start >= ext->start &&
start <= (ext->start + ext->count)) {
@@ -378,7 +405,7 @@ static int rb_insert_extent(__u64 start, __u64 count,
while (*n) {
parent = *n;
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if (start < ext->start) {
n = &(*n)->rb_left;
@@ -408,11 +435,11 @@ got_extent:
new_node = &new_ext->node;
ext2fs_rb_link_node(new_node, parent, n);
ext2fs_rb_insert_color(new_node, root);
- *bp->wcursor = new_ext;
+ bp->wcursor = new_ext;
node = ext2fs_rb_prev(new_node);
if (node) {
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
if ((ext->start + ext->count) == start) {
start = ext->start;
count += ext->count;
@@ -425,7 +452,7 @@ skip_insert:
/* See if we can merge extent to the right */
for (node = ext2fs_rb_next(new_node); node != NULL; node = next) {
next = ext2fs_rb_next(node);
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
if ((ext->start + ext->count) <= start)
continue;
@@ -470,7 +497,7 @@ static int rb_remove_extent(__u64 start, __u64 count,
while (*n) {
parent = *n;
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if (start < ext->start) {
n = &(*n)->rb_left;
continue;
@@ -513,7 +540,7 @@ static int rb_remove_extent(__u64 start, __u64 count,
/* See if we should delete or truncate extent on the right */
for (; parent != NULL; parent = node) {
node = ext2fs_rb_next(parent);
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if ((ext->start + ext->count) <= start)
continue;
@@ -542,13 +569,14 @@ static int rb_remove_extent(__u64 start, __u64 count,
static int rb_mark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg)
{
struct ext2fs_rb_private *bp;
- int i;
-
+ int retval;
bp = (struct ext2fs_rb_private *) bitmap->private;
arg -= bitmap->start;
- return rb_insert_extent(arg, 1, bp);
+ retval = rb_insert_extent(arg, 1, bp);
+ check_tree(&bp->root, __func__);
+ return retval;
}
static int rb_unmark_bmap(ext2fs_generic_bitmap bitmap, __u64 arg)
@@ -580,19 +608,18 @@ static void rb_mark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg,
unsigned int num)
{
struct ext2fs_rb_private *bp;
- struct bmap_rb_extent *new_ext;
bp = (struct ext2fs_rb_private *) bitmap->private;
arg -= bitmap->start;
rb_insert_extent(arg, num, bp);
+ check_tree(&bp->root, __func__);
}
static void rb_unmark_bmap_extent(ext2fs_generic_bitmap bitmap, __u64 arg,
unsigned int num)
{
struct ext2fs_rb_private *bp;
- int ret;
bp = (struct ext2fs_rb_private *) bitmap->private;
arg -= bitmap->start;
@@ -624,7 +651,7 @@ static int rb_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap,
*/
while (*n) {
parent = *n;
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if (start < ext->start) {
n = &(*n)->rb_left;
} else if (start >= (ext->start + ext->count)) {
@@ -641,7 +668,7 @@ static int rb_test_clear_bmap_extent(ext2fs_generic_bitmap bitmap,
node = parent;
while (node) {
next = ext2fs_rb_next(node);
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
node = next;
if ((ext->start + ext->count) <= start)
@@ -661,15 +688,43 @@ static errcode_t rb_set_bmap_range(ext2fs_generic_bitmap bitmap,
__u64 start, size_t num, void *in)
{
struct ext2fs_rb_private *bp;
+ unsigned char *cp = in;
size_t i;
- int ret;
+ int first_set = -1;
bp = (struct ext2fs_rb_private *) bitmap->private;
for (i = 0; i < num; i++) {
- ret = ext2fs_test_bit(i, in);
- if (ret)
- rb_insert_extent(start + i - bitmap->start, 1, bp);
+ if ((i & 7) == 0) {
+ unsigned char c = cp[i/8];
+ if (c == 0xFF) {
+ if (first_set == -1)
+ first_set = i;
+ i += 7;
+ continue;
+ }
+ if ((c == 0x00) && (first_set == -1)) {
+ i += 7;
+ continue;
+ }
+ }
+ if (ext2fs_test_bit(i, in)) {
+ if (first_set == -1)
+ first_set = i;
+ continue;
+ }
+ if (first_set == -1)
+ continue;
+
+ rb_insert_extent(start + first_set - bitmap->start,
+ i - first_set, bp);
+ check_tree(&bp->root, __func__);
+ first_set = -1;
+ }
+ if (first_set != -1) {
+ rb_insert_extent(start + first_set - bitmap->start,
+ num - first_set, bp);
+ check_tree(&bp->root, __func__);
}
return 0;
@@ -682,6 +737,7 @@ static errcode_t rb_get_bmap_range(ext2fs_generic_bitmap bitmap,
struct rb_node *parent = NULL, *next, **n;
struct ext2fs_rb_private *bp;
struct bmap_rb_extent *ext;
+ int count;
__u64 pos;
bp = (struct ext2fs_rb_private *) bitmap->private;
@@ -693,7 +749,7 @@ static errcode_t rb_get_bmap_range(ext2fs_generic_bitmap bitmap,
while (*n) {
parent = *n;
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
if (start < ext->start) {
n = &(*n)->rb_left;
} else if (start >= (ext->start + ext->count)) {
@@ -702,44 +758,139 @@ static errcode_t rb_get_bmap_range(ext2fs_generic_bitmap bitmap,
break;
}
- pos = start;
+ memset(out, 0, (num + 7) >> 3);
+
for (; parent != NULL; parent = next) {
next = ext2fs_rb_next(parent);
- ext = ext2fs_rb_entry(parent, struct bmap_rb_extent, node);
+ ext = node_to_extent(parent);
- while (((pos - start) < num) &&
- (pos < ext->start)) {
- ext2fs_fast_clear_bit64((pos - start), out);
- pos++;
+ pos = ext->start;
+ count = ext->count;
+ if (pos >= start + num)
+ break;
+ if (pos < start) {
+ count -= start - pos;
+ if (count < 0)
+ continue;
+ pos = start;
}
-
- if ((pos - start) >= num)
- return 0;
-
- while (((pos - start) < num) &&
- (pos < (ext->start + ext->count))) {
+ if (pos + count > start + num)
+ count = start + num - pos;
+
+ while (count > 0) {
+ if ((count >= 8) &&
+ ((pos - start) % 8) == 0) {
+ int nbytes = count >> 3;
+ int offset = (pos - start) >> 3;
+
+ memset(((char *) out) + offset, 0xFF, nbytes);
+ pos += nbytes << 3;
+ count -= nbytes << 3;
+ continue;
+ }
ext2fs_fast_set_bit64((pos - start), out);
pos++;
+ count--;
}
}
+ return 0;
+}
+
+static void rb_clear_bmap(ext2fs_generic_bitmap bitmap)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
- while ((pos - start) < num) {
- ext2fs_fast_clear_bit64((pos - start), out);
- pos++;
+ rb_free_tree(&bp->root);
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+ bp->wcursor = NULL;
+ check_tree(&bp->root, __func__);
+}
+
+static errcode_t rb_find_first_zero(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ struct rb_node *parent = NULL, **n;
+ struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+ end -= bitmap->start;
+
+ if (start > end)
+ return EINVAL;
+
+ if (EXT2FS_RB_EMPTY_ROOT(&bp->root))
+ return ENOENT;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else if (ext->start + ext->count <= end) {
+ *out = ext->start + ext->count + bitmap->start;
+ return 0;
+ } else
+ return ENOENT;
}
+ *out = start + bitmap->start;
return 0;
}
-static void rb_clear_bmap(ext2fs_generic_bitmap bitmap)
+static errcode_t rb_find_first_set(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
{
+ struct rb_node *parent = NULL, **n;
+ struct rb_node *node;
struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+ end -= bitmap->start;
- rb_free_tree(&bp->root);
- *bp->rcursor = NULL;
- *bp->wcursor = NULL;
+ if (start > end)
+ return EINVAL;
+
+ if (EXT2FS_RB_EMPTY_ROOT(&bp->root))
+ return ENOENT;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else {
+ /* The start bit is set */
+ *out = start + bitmap->start;
+ return 0;
+ }
+ }
+
+ node = parent;
+ ext = node_to_extent(node);
+ if (ext->start < start) {
+ node = ext2fs_rb_next(node);
+ if (node == NULL)
+ return ENOENT;
+ ext = node_to_extent(node);
+ }
+ if (ext->start <= end) {
+ *out = ext->start + bitmap->start;
+ return 0;
+ }
+ return ENOENT;
}
#ifdef BMAP_STATS
@@ -752,15 +903,17 @@ static void rb_print_stats(ext2fs_generic_bitmap bitmap)
__u64 max_size = 0;
__u64 min_size = ULONG_MAX;
__u64 size = 0, avg_size = 0;
+ double eff;
+#ifdef BMAP_STATS_OPS
__u64 mark_all, test_all;
- double eff, m_hit = 0.0, t_hit = 0.0;
+ double m_hit = 0.0, t_hit = 0.0;
+#endif
bp = (struct ext2fs_rb_private *) bitmap->private;
- node = ext2fs_rb_first(&bp->root);
for (node = ext2fs_rb_first(&bp->root); node != NULL;
node = ext2fs_rb_next(node)) {
- ext = ext2fs_rb_entry(node, struct bmap_rb_extent, node);
+ ext = node_to_extent(node);
count++;
if (ext->count > max_size)
max_size = ext->count;
@@ -821,4 +974,6 @@ struct ext2_bitmap_ops ext2fs_blkmap64_rbtree = {
.get_bmap_range = rb_get_bmap_range,
.clear_bmap = rb_clear_bmap,
.print_stats = rb_print_stats,
+ .find_first_zero = rb_find_first_zero,
+ .find_first_set = rb_find_first_set,
};
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
index 33da7d6f..8ced1eec 100644
--- a/lib/ext2fs/blknum.c
+++ b/lib/ext2fs/blknum.c
@@ -187,11 +187,8 @@ struct ext2_group_desc *ext2fs_group_desc(ext2_filsys fs,
struct opaque_ext2_group_desc *gdp,
dgrp_t group)
{
- if (fs->super->s_desc_size >= EXT2_MIN_DESC_SIZE_64BIT)
- return (struct ext2_group_desc *)
- ((struct ext4_group_desc *) gdp + group);
- else
- return (struct ext2_group_desc *) gdp + group;
+ return (struct ext2_group_desc *)((char *)gdp +
+ group * EXT2_DESC_SIZE(fs->super));
}
/* Do the same but as an ext4 group desc for internal use here */
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
index 85a1803d..b8c68798 100644
--- a/lib/ext2fs/block.c
+++ b/lib/ext2fs/block.c
@@ -389,7 +389,7 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
if (inode.i_flags & EXT4_EXTENTS_FL) {
ext2_extent_handle_t handle;
- struct ext2fs_extent extent;
+ struct ext2fs_extent extent, next;
e2_blkcnt_t blockcnt = 0;
blk64_t blk, new_blk;
int op = EXT2_EXTENT_ROOT;
@@ -401,7 +401,11 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
goto abort_exit;
while (1) {
- ctx.errcode = ext2fs_extent_get(handle, op, &extent);
+ if (op == EXT2_EXTENT_CURRENT)
+ ctx.errcode = 0;
+ else
+ ctx.errcode = ext2fs_extent_get(handle, op,
+ &extent);
if (ctx.errcode) {
if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT)
break;
@@ -456,14 +460,21 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
uninit = 0;
if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
uninit = EXT2_EXTENT_SET_BMAP_UNINIT;
+
+ /*
+ * Get the next extent before we start messing
+ * with the current extent
+ */
+ retval = ext2fs_extent_get(handle, op, &next);
+
#if 0
printf("lblk %llu pblk %llu len %d blockcnt %llu\n",
extent.e_lblk, extent.e_pblk,
extent.e_len, blockcnt);
#endif
- if (extent.e_lblk + extent.e_len <= blockcnt)
+ if (extent.e_lblk + extent.e_len <= (blk64_t) blockcnt)
continue;
- if (extent.e_lblk > blockcnt)
+ if (extent.e_lblk > (blk64_t) blockcnt)
blockcnt = extent.e_lblk;
j = blockcnt - extent.e_lblk;
blk += j;
@@ -487,6 +498,10 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
if (ret & BLOCK_ABORT)
goto extent_done;
}
+ if (retval == 0) {
+ extent = next;
+ op = EXT2_EXTENT_CURRENT;
+ }
}
extent_done:
diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c
index 16d51e0b..db2fd726 100644
--- a/lib/ext2fs/bmap.c
+++ b/lib/ext2fs/bmap.c
@@ -18,7 +18,7 @@
#include <errno.h>
#include "ext2_fs.h"
-#include "ext2fs.h"
+#include "ext2fsP.h"
#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
#define _BMAP_INLINE_ __inline__
@@ -95,7 +95,7 @@ static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags,
int *blocks_alloc,
blk_t nr, blk_t *ret_blk)
{
- blk_t b;
+ blk_t b = 0;
errcode_t retval;
blk_t addr_per_block;
@@ -115,7 +115,7 @@ static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags,
int *blocks_alloc,
blk_t nr, blk_t *ret_blk)
{
- blk_t b;
+ blk_t b = 0;
errcode_t retval;
blk_t addr_per_block;
@@ -140,7 +140,7 @@ static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
ext2_extent_handle_t handle,
- blk64_t block, blk64_t *phys_blk)
+ blk64_t lblk, blk64_t *phys_blk)
{
blk64_t base_block, pblock = 0;
int i;
@@ -149,10 +149,19 @@ static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino,
EXT4_FEATURE_RO_COMPAT_BIGALLOC))
return 0;
- base_block = block & ~EXT2FS_CLUSTER_MASK(fs);
+ base_block = lblk & ~EXT2FS_CLUSTER_MASK(fs);
+ /*
+ * Except for the logical block (lblk) that was passed in, search all
+ * blocks in this logical cluster for a mapping to a physical cluster.
+ * If any such map exists, calculate the physical block that maps to
+ * the logical block and return that.
+ *
+ * The old code wouldn't even look if (block % cluster_ratio) == 0;
+ * this is incorrect if we're allocating blocks in reverse order.
+ */
for (i = 0; i < EXT2FS_CLUSTER_RATIO(fs); i++) {
- if (block == base_block)
- return 0;
+ if (base_block + i == lblk)
+ continue;
extent_bmap(fs, ino, inode, handle, 0, 0,
base_block + i, 0, 0, &pblock);
if (pblock)
@@ -160,10 +169,39 @@ static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino,
}
if (pblock == 0)
return 0;
- *phys_blk = pblock - i + (block - base_block);
+ *phys_blk = pblock - i + (lblk - base_block);
return 0;
}
+/* Try to map a logical block to an already-allocated physical cluster. */
+errcode_t ext2fs_map_cluster_block(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk,
+ blk64_t *pblk)
+{
+ ext2_extent_handle_t handle;
+ errcode_t retval;
+
+ /* Need bigalloc and extents to be enabled */
+ *pblk = 0;
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_BIGALLOC) ||
+ !(inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+
+ retval = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (retval)
+ goto out;
+
+ retval = implied_cluster_alloc(fs, ino, inode, handle, lblk, pblk);
+ if (retval)
+ goto out2;
+
+out2:
+ ext2fs_extent_free(handle);
+out:
+ return retval;
+}
+
static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
ext2_extent_handle_t handle,
@@ -217,8 +255,10 @@ got_block:
set_extent:
retval = ext2fs_extent_set_bmap(handle, block,
blk64, 0);
- if (retval)
+ if (retval) {
+ ext2fs_block_alloc_stats2(fs, blk64, -1);
return retval;
+ }
/* Update inode after setting extent */
retval = ext2fs_read_inode(fs, ino, inode);
if (retval)
@@ -229,6 +269,27 @@ got_block:
return 0;
}
+int ext2fs_file_block_offset_too_big(ext2_filsys fs,
+ struct ext2_inode *inode,
+ blk64_t offset)
+{
+ blk64_t addr_per_block, max_map_block;
+
+ /* Kernel seems to cut us off at 4294967294 blocks */
+ if (offset >= (1ULL << 32) - 1)
+ return 1;
+
+ if (inode->i_flags & EXT4_EXTENTS_FL)
+ return 0;
+
+ addr_per_block = fs->blocksize >> 2;
+ max_map_block = addr_per_block;
+ max_map_block += addr_per_block * addr_per_block;
+ max_map_block += addr_per_block * addr_per_block * addr_per_block;
+ max_map_block += 12;
+
+ return offset >= max_map_block;
+}
errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
char *block_buf, int bmap_flags, blk64_t block,
@@ -257,6 +318,9 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
}
addr_per_block = (blk_t) fs->blocksize >> 2;
+ if (ext2fs_file_block_offset_too_big(fs, inode, block))
+ return EXT2_ET_FILE_TOO_BIG;
+
if (!block_buf) {
retval = ext2fs_get_array(2, fs->blocksize, &buf);
if (retval)
diff --git a/lib/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h
index f44d379c..9deba46c 100644
--- a/lib/ext2fs/bmap64.h
+++ b/lib/ext2fs/bmap64.h
@@ -94,6 +94,10 @@ struct ext2_bitmap_ops {
* May be NULL, in which case a generic function is used. */
errcode_t (*find_first_zero)(ext2fs_generic_bitmap bitmap,
__u64 start, __u64 end, __u64 *out);
+ /* Find the first set bit between start and end, inclusive.
+ * May be NULL, in which case a generic function is used. */
+ errcode_t (*find_first_set)(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out);
};
extern struct ext2_bitmap_ops ext2fs_blkmap64_bitarray;
diff --git a/lib/ext2fs/brel.h b/lib/ext2fs/brel.h
index a0dd5b9c..9fdddd40 100644
--- a/lib/ext2fs/brel.h
+++ b/lib/ext2fs/brel.h
@@ -10,11 +10,11 @@
*/
struct ext2_block_relocate_entry {
- blk_t new;
+ blk64_t new;
__s16 offset;
__u16 flags;
union {
- blk_t block_ref;
+ blk64_t block_ref;
ext2_ino_t inode_ref;
} owner;
};
@@ -28,19 +28,19 @@ typedef struct ext2_block_relocation_table *ext2_brel;
struct ext2_block_relocation_table {
__u32 magic;
char *name;
- blk_t current;
+ blk64_t current;
void *priv_data;
/*
* Add a block relocation entry.
*/
- errcode_t (*put)(ext2_brel brel, blk_t old,
+ errcode_t (*put)(ext2_brel brel, blk64_t old,
struct ext2_block_relocate_entry *ent);
/*
* Get a block relocation entry.
*/
- errcode_t (*get)(ext2_brel brel, blk_t old,
+ errcode_t (*get)(ext2_brel brel, blk64_t old,
struct ext2_block_relocate_entry *ent);
/*
@@ -52,19 +52,19 @@ struct ext2_block_relocation_table {
* 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,
+ errcode_t (*next)(ext2_brel brel, blk64_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);
+ errcode_t (*move)(ext2_brel brel, blk64_t old, blk_t new);
/*
* Remove a block relocation entry.
*/
- errcode_t (*delete)(ext2_brel brel, blk_t old);
+ errcode_t (*delete)(ext2_brel brel, blk64_t old);
/*
@@ -73,7 +73,7 @@ struct ext2_block_relocation_table {
errcode_t (*free)(ext2_brel brel);
};
-errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+errcode_t ext2fs_brel_memarray_create(char *name, blk64_t max_block,
ext2_brel *brel);
#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent))
diff --git a/lib/ext2fs/brel_ma.c b/lib/ext2fs/brel_ma.c
index e8a8280e..a12afaeb 100644
--- a/lib/ext2fs/brel_ma.c
+++ b/lib/ext2fs/brel_ma.c
@@ -27,24 +27,24 @@
#include "ext2fs.h"
#include "brel.h"
-static errcode_t bma_put(ext2_brel brel, blk_t old,
+static errcode_t bma_put(ext2_brel brel, blk64_t old,
struct ext2_block_relocate_entry *ent);
-static errcode_t bma_get(ext2_brel brel, blk_t old,
+static errcode_t bma_get(ext2_brel brel, blk64_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,
+static errcode_t bma_next(ext2_brel brel, blk64_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_move(ext2_brel brel, blk64_t old, blk64_t new);
+static errcode_t bma_delete(ext2_brel brel, blk64_t old);
static errcode_t bma_free(ext2_brel brel);
struct brel_ma {
__u32 magic;
- blk_t max_block;
+ blk64_t max_block;
struct ext2_block_relocate_entry *entries;
};
-errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+errcode_t ext2fs_brel_memarray_create(char *name, blk64_t max_block,
ext2_brel *new_brel)
{
ext2_brel brel = 0;
@@ -102,7 +102,7 @@ errout:
return retval;
}
-static errcode_t bma_put(ext2_brel brel, blk_t old,
+static errcode_t bma_put(ext2_brel brel, blk64_t old,
struct ext2_block_relocate_entry *ent)
{
struct brel_ma *ma;
@@ -114,7 +114,7 @@ static errcode_t bma_put(ext2_brel brel, blk_t old,
return 0;
}
-static errcode_t bma_get(ext2_brel brel, blk_t old,
+static errcode_t bma_get(ext2_brel brel, blk64_t old,
struct ext2_block_relocate_entry *ent)
{
struct brel_ma *ma;
@@ -134,7 +134,7 @@ static errcode_t bma_start_iter(ext2_brel brel)
return 0;
}
-static errcode_t bma_next(ext2_brel brel, blk_t *old,
+static errcode_t bma_next(ext2_brel brel, blk64_t *old,
struct ext2_block_relocate_entry *ent)
{
struct brel_ma *ma;
@@ -151,7 +151,7 @@ static errcode_t bma_next(ext2_brel brel, blk_t *old,
return 0;
}
-static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
+static errcode_t bma_move(ext2_brel brel, blk64_t old, blk64_t new)
{
struct brel_ma *ma;
@@ -165,7 +165,7 @@ static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
return 0;
}
-static errcode_t bma_delete(ext2_brel brel, blk_t old)
+static errcode_t bma_delete(ext2_brel brel, blk64_t old)
{
struct brel_ma *ma;
diff --git a/lib/ext2fs/check_desc.c b/lib/ext2fs/check_desc.c
index a6fcc454..1a768f92 100644
--- a/lib/ext2fs/check_desc.c
+++ b/lib/ext2fs/check_desc.c
@@ -42,6 +42,9 @@ errcode_t ext2fs_check_desc(ext2_filsys fs)
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+ if (EXT2_DESC_SIZE(fs->super) & (EXT2_DESC_SIZE(fs->super) - 1))
+ return EXT2_ET_BAD_DESC_SIZE;
+
retval = ext2fs_allocate_subcluster_bitmap(fs, "check_desc map", &bmap);
if (retval)
return retval;
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 973c2a23..4e91778a 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -20,12 +20,12 @@
#include "ext2_fs.h"
#include "ext2fsP.h"
-static int test_root(int a, int b)
+static int test_root(unsigned int a, unsigned int b)
{
- if (a == 0)
- return 1;
while (1) {
- if (a == 1)
+ if (a < b)
+ return 0;
+ if (a == b)
return 1;
if (a % b)
return 0;
@@ -33,14 +33,23 @@ static int test_root(int a, int b)
}
}
-int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t group)
{
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+ if (group == 0)
return 1;
-
- if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
- test_root(group_block, 7))
+ if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
+ if (group == fs->super->s_backup_bgs[0] ||
+ group == fs->super->s_backup_bgs[1])
+ return 1;
+ return 0;
+ }
+ if ((group <= 1) || !(fs->super->s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+ return 1;
+ if (!(group & 1))
+ return 0;
+ if (test_root(group, 3) || (test_root(group, 5)) ||
+ test_root(group, 7))
return 1;
return 0;
@@ -243,7 +252,7 @@ void ext2fs_update_dynamic_rev(ext2_filsys fs)
}
static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
- blk_t group_block,
+ blk64_t group_block,
struct ext2_super_block *super_shadow)
{
dgrp_t sgrp = group;
@@ -301,7 +310,7 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
fs->desc_blocks);
/* swap the group descriptors */
- for (j=0; j < fs->group_desc_count; j++) {
+ for (j = 0; j < fs->group_desc_count; j++) {
gdp = ext2fs_group_desc(fs, group_shadow, j);
ext2fs_swap_group_desc2(fs, gdp);
}
diff --git a/lib/ext2fs/crc32c.c b/lib/ext2fs/crc32c.c
index 6be43369..2512528a 100644
--- a/lib/ext2fs/crc32c.c
+++ b/lib/ext2fs/crc32c.c
@@ -35,9 +35,9 @@
#define __force
#define min(x, y) ((x) > (y) ? (y) : (x))
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
-#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
+#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
-#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
+#define PTR_ALIGN(p, a) ((__typeof__(p))ALIGN((unsigned long)(p), (a)))
#include "crc32c_defs.h"
#include "ext2fs.h"
@@ -224,7 +224,7 @@ static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
crc = (__force uint32_t) __cpu_to_le32(crc);
p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 8);
+ p32 = (const uint32_t *)PTR_ALIGN(p8, 8);
init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
words = (len - init_bytes) >> 3;
end_bytes = (len - init_bytes) & 7;
@@ -273,7 +273,7 @@ static uint32_t crc32c_le_body(uint32_t crc, uint8_t const *buf, size_t len)
#endif
}
- p8 = (uint8_t *)(++p32);
+ p8 = (const uint8_t *)(++p32);
for (i = 0; i < end_bytes; i++) {
#ifndef WORDS_BIGENDIAN
@@ -304,7 +304,7 @@ static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
crc = (__force uint32_t) __cpu_to_be32(crc);
p8 = buf;
- p32 = (uint32_t *)PTR_ALIGN(p8, 8);
+ p32 = (const uint32_t *)PTR_ALIGN(p8, 8);
init_bytes = min((uintptr_t)p32 - (uintptr_t)p8, len);
words = (len - init_bytes) >> 3;
end_bytes = (len - init_bytes) & 7;
@@ -353,7 +353,7 @@ static uint32_t crc32c_be_body(uint32_t crc, uint8_t const *buf, size_t len)
#endif
}
- p8 = (uint8_t *)(++p32);
+ p8 = (const uint8_t *)(++p32);
for (i = 0; i < end_bytes; i++) {
#ifndef WORDS_BIGENDIAN
@@ -1117,12 +1117,12 @@ static int test_crc32c(void)
be = ext2fs_crc32c_be(t->crc, test_buf + t->start, t->length);
if (le != t->crc_le) {
printf("Test %d LE fails, %x != %x\n",
- (t - test), le, t->crc_le);
+ (int) (t - test), le, t->crc_le);
failures++;
}
if (be != t->crc_be) {
printf("Test %d BE fails, %x != %x\n",
- (t - test), be, t->crc_be);
+ (int) (t - test), be, t->crc_be);
failures++;
}
t++;
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
index 9fa3f24f..669f8068 100644
--- a/lib/ext2fs/csum.c
+++ b/lib/ext2fs/csum.c
@@ -32,27 +32,23 @@
__u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
{
+ struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc,
+ group);
+ size_t size = EXT2_DESC_SIZE(fs->super);
__u16 crc = 0;
- struct ext2_group_desc *desc;
- size_t size;
-
- size = fs->super->s_desc_size;
- if (size < EXT2_MIN_DESC_SIZE)
- size = EXT2_MIN_DESC_SIZE;
- if (size > sizeof(struct ext4_group_desc)) {
- printf("%s: illegal s_desc_size(%zd)\n", __func__, size);
- size = sizeof(struct ext4_group_desc);
- }
-
- desc = ext2fs_group_desc(fs, fs->group_desc, group);
if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
#ifdef WORDS_BIGENDIAN
struct ext4_group_desc swabdesc;
+ size_t save_size = size;
+ const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
+ struct ext2_group_desc *save_desc = desc;
/* Have to swab back to little-endian to do the checksum */
+ if (size > ext4_bg_size)
+ size = ext4_bg_size;
memcpy(&swabdesc, desc, size);
ext2fs_swap_group_desc2(fs,
(struct ext2_group_desc *) &swabdesc);
@@ -70,6 +66,17 @@ __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
crc = ext2fs_crc16(crc, (char *)desc + offset,
size - offset);
}
+#ifdef WORDS_BIGENDIAN
+ /*
+ * If the size of the bg descriptor is greater than 64
+ * bytes, which is the size of the traditional ext4 bg
+ * descriptor, checksum the rest of the descriptor here
+ */
+ if (save_size > ext4_bg_size)
+ crc = ext2fs_crc16(crc,
+ (char *)save_desc + ext4_bg_size,
+ save_size - ext4_bg_size);
+#endif
}
return crc;
@@ -166,21 +173,22 @@ void print_csum(const char *msg, ext2_filsys fs, dgrp_t group)
{
__u16 crc1, crc2, crc3;
dgrp_t swabgroup;
- struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, group);
- size_t size;
+ struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc,
+ group);
+ size_t size = EXT2_DESC_SIZE(fs->super);
struct ext2_super_block *sb = fs->super;
int offset = offsetof(struct ext2_group_desc, bg_checksum);
#ifdef WORDS_BIGENDIAN
struct ext4_group_desc swabdesc;
+ struct ext2_group_desc *save_desc = desc;
+ const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
+ size_t save_size = size;
#endif
- size = fs->super->s_desc_size;
- if (size < EXT2_MIN_DESC_SIZE)
- size = EXT2_MIN_DESC_SIZE;
- if (size > sizeof(struct ext4_group_desc))
- size = sizeof(struct ext4_group_desc);
#ifdef WORDS_BIGENDIAN
/* Have to swab back to little-endian to do the checksum */
+ if (size > ext4_bg_size)
+ size = ext4_bg_size;
memcpy(&swabdesc, desc, size);
ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc);
desc = (struct ext2_group_desc *) &swabdesc;
@@ -197,8 +205,13 @@ void print_csum(const char *msg, ext2_filsys fs, dgrp_t group)
/* for checksum of struct ext4_group_desc do the rest...*/
if (offset < size)
crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset);
+#ifdef WORDS_BIGENDIAN
+ if (save_size > ext4_bg_size)
+ crc3 = ext2fs_crc16(crc3, (char *)save_desc + ext4_bg_size,
+ save_size - ext4_bg_size);
+#endif
- printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n",
+ printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n",
msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3,
ext2fs_group_desc_csum(fs, group));
}
@@ -216,6 +229,11 @@ int main(int argc, char **argv)
memset(&param, 0, sizeof(param));
ext2fs_blocks_count_set(&param, 32768);
+#if 0
+ param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
+ param.s_desc_size = 128;
+ csum_known = 0x5b6e;
+#endif
retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
test_io_manager, &fs);
diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index ca1446b9..3f6ea50d 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -61,7 +61,7 @@ static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size,
struct ext2_db_entry2 *list,
ext2_dblist *ret_dblist)
{
- ext2_dblist dblist;
+ ext2_dblist dblist = NULL;
errcode_t retval;
ext2_ino_t num_dirs;
size_t len;
@@ -393,10 +393,11 @@ int ext2fs_dblist_count(ext2_dblist dblist)
errcode_t ext2fs_dblist_get_last(ext2_dblist dblist,
struct ext2_db_entry **entry)
{
- EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
static struct ext2_db_entry ret_entry;
struct ext2_db_entry2 *last;
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
if (dblist->count == 0)
return EXT2_ET_DBLIST_EMPTY;
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index 5125d199..589af692 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -151,16 +151,16 @@ static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)),
return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
}
-extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
- ext2_ino_t dir,
- int flags,
- char *block_buf,
- int (*func)(struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data)
+errcode_t ext2fs_dir_iterate(ext2_filsys fs,
+ ext2_ino_t dir,
+ int flags,
+ char *block_buf,
+ int (*func)(struct ext2_dir_entry *dirent,
+ int offset,
+ int blocksize,
+ char *buf,
+ void *priv_data),
+ void *priv_data)
{
struct xlate xl;
diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
index 64d31248..02721e1a 100644
--- a/lib/ext2fs/dupfs.c
+++ b/lib/ext2fs/dupfs.c
@@ -40,6 +40,9 @@ errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
fs->block_map = 0;
fs->badblocks = 0;
fs->dblist = 0;
+ fs->mmp_buf = 0;
+ fs->mmp_cmp = 0;
+ fs->mmp_fd = -1;
io_channel_bumpcount(fs->io);
if (fs->icache)
@@ -87,6 +90,28 @@ errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
if (retval)
goto errout;
}
+ if (src->mmp_buf) {
+ retval = ext2fs_get_mem(src->blocksize, &fs->mmp_buf);
+ if (retval)
+ goto errout;
+ memcpy(fs->mmp_buf, src->mmp_buf, src->blocksize);
+ }
+ if (src->mmp_fd >= 0) {
+ fs->mmp_fd = dup(src->mmp_fd);
+ if (fs->mmp_fd < 0) {
+ retval = EXT2_ET_MMP_OPEN_DIRECT;
+ goto errout;
+ }
+ }
+ if (src->mmp_cmp) {
+ int align = ext2fs_get_dio_alignment(src->mmp_fd);
+
+ retval = ext2fs_get_memalign(src->blocksize, align,
+ &fs->mmp_cmp);
+ if (retval)
+ goto errout;
+ memcpy(fs->mmp_cmp, src->mmp_cmp, src->blocksize);
+ }
*dest = fs;
return 0;
errout:
diff --git a/lib/ext2fs/e2image.h b/lib/ext2fs/e2image.h
index c918529e..53b20cc7 100644
--- a/lib/ext2fs/e2image.h
+++ b/lib/ext2fs/e2image.h
@@ -12,15 +12,6 @@
* %End-Header%
*/
-/* Image types */
-#define E2IMAGE_RAW 1
-#define E2IMAGE_QCOW2 2
-
-/* Image flags */
-#define E2IMAGE_INSTALL_FLAG 1
-#define E2IMAGE_SCRAMBLE_FLAG 2
-#define E2IMAGE_IS_QCOW2_FLAG 3
-
struct ext2_image_hdr {
__u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */
char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 41c40882..153b838c 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -54,6 +54,7 @@ static int expand_dir_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
es->newblocks++;
+ ext2fs_block_alloc_stats2(fs, new_blk, +1);
}
if (blockcnt > 0) {
retval = ext2fs_new_dir_block(fs, 0, 0, &block);
@@ -80,7 +81,6 @@ static int expand_dir_proc(ext2_filsys fs,
}
ext2fs_free_mem(&block);
*blocknr = new_blk;
- ext2fs_block_alloc_stats2(fs, new_blk, +1);
if (es->done)
return (BLOCK_CHANGED | BLOCK_ABORT);
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index ccf1894a..87812ab4 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -96,10 +96,10 @@ ec EXT2_ET_INODE_BITMAP_READ,
"Can't read an inode bitmap"
ec EXT2_ET_BLOCK_BITMAP_WRITE,
- "Can't write an block bitmap"
+ "Can't write a block bitmap"
ec EXT2_ET_BLOCK_BITMAP_READ,
- "Can't read an block bitmap"
+ "Can't read a block bitmap"
ec EXT2_ET_INODE_TABLE_WRITE,
"Can't write an inode table"
@@ -416,7 +416,7 @@ ec EXT2_ET_EXTENT_INVALID_LENGTH,
ec EXT2_ET_IO_CHANNEL_NO_SUPPORT_64,
"I/O Channel does not support 64-bit block numbers"
-ec EXT2_NO_MTAB_FILE,
+ec EXT2_ET_NO_MTAB_FILE,
"Can't check if filesystem is mounted due to missing mtab file"
ec EXT2_ET_CANT_USE_LEGACY_BITMAPS,
@@ -443,4 +443,37 @@ ec EXT2_ET_MMP_CHANGE_ABORT,
ec EXT2_ET_MMP_OPEN_DIRECT,
"MMP: open with O_DIRECT failed"
+ec EXT2_ET_BAD_DESC_SIZE,
+ "Block group descriptor size incorrect"
+
+ec EXT2_ET_INODE_CSUM_INVALID,
+ "Inode checksum does not match inode"
+
+ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
+ "Inode bitmap checksum does not match bitmap"
+
+ec EXT2_ET_EXTENT_CSUM_INVALID,
+ "Extent block checksum does not match extent block"
+
+ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
+ "Directory block does not have space for checksum"
+
+ec EXT2_ET_DIR_CSUM_INVALID,
+ "Directory block checksum does not match directory block"
+
+ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
+ "Extended attribute block checksum does not match block"
+
+ec EXT2_ET_SB_CSUM_INVALID,
+ "Superblock checksum does not match superblock"
+
+ec EXT2_ET_UNKNOWN_CSUM,
+ "Unknown checksum algorithm"
+
+ec EXT2_ET_MMP_CSUM_INVALID,
+ "MMP block checksum does not match MMP block"
+
+ec EXT2_ET_FILE_EXISTS,
+ "Ext2 file already exists"
+
end
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 20decff2..d9e14d7c 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -159,7 +159,7 @@ struct ext2_group_desc
__u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
__u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
__u16 bg_itable_unused; /* Unused inodes count */
- __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
+ __u16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/
};
/*
@@ -301,6 +301,7 @@ struct ext2_dx_countlimit {
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
/* EXT4_EOFBLOCKS_FL 0x00400000 was here */
+#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */
#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */
#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */
@@ -644,7 +645,8 @@ struct ext2_super_block {
__u32 s_usr_quota_inum; /* inode number of user quota file */
__u32 s_grp_quota_inum; /* inode number of group quota file */
__u32 s_overhead_blocks; /* overhead blocks/clusters in fs */
- __u32 s_reserved[108]; /* Padding to the end of the block */
+ __u32 s_backup_bgs[2]; /* If sparse_super2 enabled */
+ __u32 s_reserved[106]; /* Padding to the end of the block */
__u32 s_checksum; /* crc32c(superblock) */
};
@@ -695,6 +697,7 @@ struct ext2_super_block {
#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040
/* #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 not used, legacy */
#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP 0x0100
+#define EXT4_FEATURE_COMPAT_SPARSE_SUPER2 0x0200
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
@@ -721,6 +724,9 @@ struct ext2_super_block {
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000
+/* 0x2000 was EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM but this was never used */
+#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
+#define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
index bcc2f876..1894fb8c 100644
--- a/lib/ext2fs/ext2_io.h
+++ b/lib/ext2fs/ext2_io.h
@@ -58,6 +58,7 @@ struct struct_io_channel {
long reserved[14];
void *private_data;
void *app_data;
+ int align;
};
struct struct_io_stats {
@@ -121,6 +122,8 @@ extern errcode_t io_channel_write_blk64(io_channel channel,
extern errcode_t io_channel_discard(io_channel channel,
unsigned long long block,
unsigned long long count);
+extern errcode_t io_channel_alloc_buf(io_channel channel,
+ int count, void *ptr);
/* unix_io.c */
extern io_manager unix_io_manager;
diff --git a/lib/ext2fs/ext2_types.h.in b/lib/ext2fs/ext2_types.h.in
index aa7ca3a9..5b98f715 100644
--- a/lib/ext2fs/ext2_types.h.in
+++ b/lib/ext2fs/ext2_types.h.in
@@ -92,11 +92,11 @@ typedef __U64_TYPEDEF __u64;
#if (@SIZEOF_INT@ == 8)
typedef unsigned int __u64;
#else
-#if (@SIZEOF_LONG@ == 8)
-typedef unsigned long __u64;
-#else
#if (@SIZEOF_LONG_LONG@ == 8)
typedef unsigned long long __u64;
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef unsigned long __u64;
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
@@ -108,15 +108,15 @@ typedef __S64_TYPEDEF __s64;
#if (@SIZEOF_INT@ == 8)
typedef int __s64;
#else
-#if (@SIZEOF_LONG@ == 8)
-typedef long __s64;
-#else
#if (@SIZEOF_LONG_LONG@ == 8)
#if defined(__GNUC__)
-typedef __signed__ long long __s64;
+typedef __signed__ long long __s64;
#else
-typedef signed long long __s64;
+typedef signed long long __s64;
#endif /* __GNUC__ */
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef long __s64;
#endif /* SIZEOF_LONG_LONG == 8 */
#endif /* SIZEOF_LONG == 8 */
#endif /* SIZEOF_INT == 8 */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 4e7711ac..1491c622 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -29,10 +29,6 @@ extern "C" {
#define NO_INLINE_FUNCS
#endif
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600 /* for posix_memalign() */
-#endif
-
/*
* Where the master copy of the superblock is located, and how big
* superblocks are supposed to be. We define SUPERBLOCK_SIZE because
@@ -57,16 +53,6 @@ extern "C" {
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#ifndef __USE_XOPEN2K
-/* If the "#define _XOPEN_SOURCE 600" didn't succeed in declaring
- * posix_memalign(), maybe due to <features.h> or <stdlib.h> included beforej
- * _XOPEN_SOURCE, declare it here to avoid compiler warnings. */
-extern int posix_memalign(void **__memptr, size_t __alignment, size_t __size);
-#endif
#if EXT2_FLAT_INCLUDES
#include "e2_types.h"
@@ -78,14 +64,20 @@ extern int posix_memalign(void **__memptr, size_t __alignment, size_t __size);
#include <ext2fs/ext3_extents.h>
#endif /* EXT2_FLAT_INCLUDES */
-typedef __u32 ext2_ino_t;
-typedef __u32 blk_t;
-typedef __u64 blk64_t;
-typedef __u32 dgrp_t;
-typedef __u32 ext2_off_t;
-typedef __u64 ext2_off64_t;
-typedef __s64 e2_blkcnt_t;
-typedef __u32 ext2_dirhash_t;
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __attribute__((bitwise))
+#else
+#define __bitwise
+#endif
+
+typedef __u32 __bitwise ext2_ino_t;
+typedef __u32 __bitwise blk_t;
+typedef __u64 __bitwise blk64_t;
+typedef __u32 __bitwise dgrp_t;
+typedef __u32 __bitwise ext2_off_t;
+typedef __u64 __bitwise ext2_off64_t;
+typedef __s64 __bitwise e2_blkcnt_t;
+typedef __u32 __bitwise ext2_dirhash_t;
#if EXT2_FLAT_INCLUDES
#include "com_err.h"
@@ -211,6 +203,7 @@ typedef struct ext2_file *ext2_file_t;
*/
#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 /* create V1 superblock (deprecated) */
#define EXT2_MKJOURNAL_LAZYINIT 0x0000002 /* don't zero journal inode before use*/
+#define EXT2_MKJOURNAL_NO_MNT_CHECK 0x0000004 /* don't check mount status */
struct opaque_ext2_group_desc;
@@ -557,7 +550,8 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
EXT2_FEATURE_COMPAT_RESIZE_INODE|\
EXT2_FEATURE_COMPAT_DIR_INDEX|\
- EXT2_FEATURE_COMPAT_EXT_ATTR)
+ EXT2_FEATURE_COMPAT_EXT_ATTR|\
+ EXT4_FEATURE_COMPAT_SPARSE_SUPER2)
/* This #ifdef is temporary until compression is fully supported */
#ifdef ENABLE_COMPRESSION
@@ -639,6 +633,12 @@ typedef struct stat ext2fs_struct_stat;
* function prototypes
*/
+/* The LARGE_FILE feature should be set if we have stored files 2GB+ in size */
+static inline int ext2fs_needs_large_file_feature(unsigned long long file_size)
+{
+ return file_size >= 0x80000000ULL;
+}
+
/* alloc.c */
extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
ext2fs_inode_bitmap map, ext2_ino_t *ret);
@@ -684,6 +684,8 @@ void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
int inuse, int isdir);
void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
void ext2fs_block_alloc_stats2(ext2_filsys fs, blk64_t blk, int inuse);
+void ext2fs_block_alloc_stats_range(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse);
/* alloc_tables.c */
extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
@@ -907,6 +909,9 @@ extern errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
char *block_buf, int bmap_flags, blk64_t block,
int *ret_flags, blk64_t *phys_blk);
+errcode_t ext2fs_map_cluster_block(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk,
+ blk64_t *pblk);
#if 0
/* bmove.c */
@@ -924,7 +929,7 @@ extern errcode_t ext2fs_close(ext2_filsys fs);
extern errcode_t ext2fs_close2(ext2_filsys fs, int flags);
extern errcode_t ext2fs_flush(ext2_filsys fs);
extern errcode_t ext2fs_flush2(ext2_filsys fs, int flags);
-extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
+extern int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t group_block);
extern errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs,
dgrp_t group,
blk64_t *ret_super_blk,
@@ -1083,6 +1088,7 @@ extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
extern void ext2fs_extent_free(ext2_extent_handle_t handle);
extern errcode_t ext2fs_extent_get(ext2_extent_handle_t handle,
int flags, struct ext2fs_extent *extent);
+extern errcode_t ext2fs_extent_node_split(ext2_extent_handle_t handle);
extern errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, int flags,
struct ext2fs_extent *extent);
extern errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
@@ -1095,6 +1101,9 @@ extern errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle,
struct ext2_extent_info *info);
extern errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
blk64_t blk);
+extern errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle,
+ int leaf_level, blk64_t blk);
+extern errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle);
/* fileio.c */
extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
@@ -1168,6 +1177,12 @@ extern errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap,
errcode_t magic,
__u32 start, __u32 num,
void *in);
+extern errcode_t ext2fs_find_first_zero_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out);
+extern errcode_t ext2fs_find_first_set_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out);
/* gen_bitmap64.c */
@@ -1209,6 +1224,7 @@ extern errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
blk64_t *retblocks);
/* getsectsize.c */
+extern int ext2fs_get_dio_alignment(int fd);
errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize);
errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize);
@@ -1257,6 +1273,11 @@ extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
+/* inline.c */
+
+extern errcode_t ext2fs_get_memalign(unsigned long size,
+ unsigned long align, void *ptr);
+
/* inode.c */
extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
@@ -1308,6 +1329,10 @@ extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
char *mtpt, int mtlen);
/* punch.c */
+/*
+ * NOTE: This function removes from an inode the blocks "start", "end", and
+ * every block in between.
+ */
extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
char *block_buf, blk64_t start,
@@ -1346,6 +1371,8 @@ extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
ext2_filsys journal_dev);
extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks,
int flags);
+extern errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+ blk64_t goal, int flags);
extern int ext2fs_default_journal_size(__u64 num_blocks);
/* openfs.c */
@@ -1356,6 +1383,11 @@ extern errcode_t ext2fs_open2(const char *name, const char *io_options,
int flags, int superblock,
unsigned int block_size, io_manager manager,
ext2_filsys *ret_fs);
+/*
+ * The dgrp_t argument to these two functions is not actually a group number
+ * but a block number offset within a group table! Convert with the formula
+ * (group_number / groups_per_block).
+ */
extern blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs,
blk64_t group_block, dgrp_t i);
extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block,
@@ -1374,6 +1406,10 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
ext2_ino_t ino, int flags);
+/* symlink.c */
+errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
+ const char *name, char *target);
+
/* mmp.c */
errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf);
errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf);
@@ -1421,6 +1457,11 @@ extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
struct ext2_inode *f, int hostorder);
extern void ext2fs_swap_mmp(struct mmp_struct *mmp);
+/* unix_io.c */
+extern int ext2fs_open_file(const char *pathname, int flags, mode_t mode);
+extern int ext2fs_stat(const char *path, ext2fs_struct_stat *buf);
+extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);
+
/* valid_blk.c */
extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
extern int ext2fs_inode_has_valid_blocks2(ext2_filsys fs,
@@ -1438,9 +1479,8 @@ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
/* inline functions */
+#ifdef NO_INLINE_FUNCS
extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
-extern errcode_t ext2fs_get_memalign(unsigned long size,
- unsigned long align, void *ptr);
extern errcode_t ext2fs_get_memzero(unsigned long size, void *ptr);
extern errcode_t ext2fs_get_array(unsigned long count,
unsigned long size, void *ptr);
@@ -1459,17 +1499,15 @@ extern void ext2fs_mark_ib_dirty(ext2_filsys fs);
extern void ext2fs_mark_bb_dirty(ext2_filsys fs);
extern int ext2fs_test_ib_dirty(ext2_filsys fs);
extern int ext2fs_test_bb_dirty(ext2_filsys fs);
-extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
-extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
+extern dgrp_t ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
+extern dgrp_t ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
extern blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group);
extern blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group);
extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
struct ext2_inode *inode);
extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b);
extern __u64 ext2fs_div64_ceil(__u64 a, __u64 b);
-extern int ext2fs_open_file(const char *pathname, int flags, mode_t mode);
-extern int ext2fs_stat(const char *path, ext2fs_struct_stat *buf);
-extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);
+#endif
/*
* The actual inlined functions definitions themselves...
@@ -1481,17 +1519,21 @@ extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);
#ifdef INCLUDE_INLINE_FUNCS
#define _INLINE_ extern
#else
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else
#ifdef __GNUC__
#define _INLINE_ extern __inline__
#else /* For Watcom C */
#define _INLINE_ extern inline
-#endif
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
#endif
#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
#include <string.h>
/*
- * Allocate memory
+ * Allocate memory. The 'ptr' arg must point to a pointer.
*/
_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
{
@@ -1538,7 +1580,7 @@ _INLINE_ errcode_t ext2fs_get_arrayzero(unsigned long count,
}
/*
- * Free memory
+ * Free memory. The 'ptr' arg must point to a pointer.
*/
_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
{
@@ -1552,7 +1594,7 @@ _INLINE_ errcode_t ext2fs_free_mem(void *ptr)
}
/*
- * Resize memory
+ * Resize memory. The 'ptr' arg must point to a pointer.
*/
_INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
unsigned long size, void *ptr)
@@ -1653,14 +1695,14 @@ _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs)
/*
* Return the group # of a block
*/
-_INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
+_INLINE_ dgrp_t ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
{
return ext2fs_group_of_blk2(fs, blk);
}
/*
* Return the group # of an inode number
*/
-_INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
+_INLINE_ dgrp_t ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
{
return (ino - 1) / fs->super->s_inodes_per_group;
}
@@ -1670,7 +1712,7 @@ _INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
*/
_INLINE_ blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group)
{
- return ext2fs_group_first_block2(fs, group);
+ return (blk_t) ext2fs_group_first_block2(fs, group);
}
/*
@@ -1678,13 +1720,13 @@ _INLINE_ blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group)
*/
_INLINE_ blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group)
{
- return ext2fs_group_last_block2(fs, group);
+ return (blk_t) ext2fs_group_last_block2(fs, group);
}
_INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
struct ext2_inode *inode)
{
- return ext2fs_inode_data_blocks2(fs, inode);
+ return (blk_t) ext2fs_inode_data_blocks2(fs, inode);
}
/*
@@ -1704,40 +1746,6 @@ _INLINE_ __u64 ext2fs_div64_ceil(__u64 a, __u64 b)
return ((a - 1) / b) + 1;
}
-_INLINE_ int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
-{
- va_list args;
-
- if (mode)
-#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
- return open64(pathname, flags, mode);
- else
- return open64(pathname, flags);
-#else
- return open(pathname, flags, mode);
- else
- return open(pathname, flags);
-#endif
-}
-
-_INLINE_ int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
-{
-#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
- return stat64(path, buf);
-#else
- return stat(path, buf);
-#endif
-}
-
-_INLINE_ int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
-{
-#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
- return fstat64(fd, buf);
-#else
- return fstat(fd, buf);
-#endif
-}
-
#undef _INLINE_
#endif
diff --git a/lib/ext2fs/ext2fs.pc.in b/lib/ext2fs/ext2fs.pc.in
index 8db86635..efac85e3 100644
--- a/lib/ext2fs/ext2fs.pc.in
+++ b/lib/ext2fs/ext2fs.pc.in
@@ -7,5 +7,5 @@ Name: ext2fs
Description: Ext2fs library
Version: @E2FSPROGS_VERSION@
Requires.private: com_err
-Cflags: -I${includedir}/ext2fs
+Cflags: -I${includedir}/ext2fs -I${includedir}
Libs: -L${libdir} -lext2fs
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index 729d5c53..a88db93e 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -66,7 +66,7 @@ struct dir_context {
*/
struct ext2_inode_cache {
void * buffer;
- blk_t buffer_blk;
+ blk64_t buffer_blk;
int cache_last;
int cache_size;
int refcount;
@@ -141,3 +141,7 @@ extern errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bitmap,
extern void ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap,const char *func);
extern int ext2fs_mem_is_zero(const char *mem, size_t len);
+
+extern int ext2fs_file_block_offset_too_big(ext2_filsys fs,
+ struct ext2_inode *inode,
+ blk64_t offset);
diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c
index eb096d6a..0bf82ea4 100644
--- a/lib/ext2fs/extent.c
+++ b/lib/ext2fs/extent.c
@@ -160,7 +160,7 @@ errcode_t ext2fs_extent_header_verify(void *ptr, int size)
/*
* Begin functions to handle an inode's extent information
*/
-extern void ext2fs_extent_free(ext2_extent_handle_t handle)
+void ext2fs_extent_free(ext2_extent_handle_t handle)
{
int i;
@@ -177,13 +177,13 @@ extern void ext2fs_extent_free(ext2_extent_handle_t handle)
ext2fs_free_mem(&handle);
}
-extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino,
+errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino,
ext2_extent_handle_t *ret_handle)
{
return ext2fs_extent_open2(fs, ino, NULL, ret_handle);
}
-extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
+errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
ext2_extent_handle_t *ret_handle)
{
@@ -604,8 +604,8 @@ errcode_t ext2fs_extent_free_path(ext2_extent_path_t path)
* If "blk" has no mapping (hole) then handle is left at last
* extent before blk.
*/
-static errcode_t extent_goto(ext2_extent_handle_t handle,
- int leaf_level, blk64_t blk)
+errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle,
+ int leaf_level, blk64_t blk)
{
struct ext2fs_extent extent;
errcode_t retval;
@@ -694,7 +694,7 @@ static errcode_t extent_goto(ext2_extent_handle_t handle,
errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
blk64_t blk)
{
- return extent_goto(handle, 0, blk);
+ return ext2fs_extent_goto2(handle, 0, blk);
}
/*
@@ -706,12 +706,14 @@ errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle,
* Safe to call for any position in node; if not at the first entry,
* will simply return.
*/
-static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
+errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
{
int retval = 0;
+ int orig_height;
blk64_t start;
struct extent_path *path;
struct ext2fs_extent extent;
+ struct ext2_extent_info info;
EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
@@ -732,6 +734,10 @@ static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
/* modified node's start block */
start = extent.e_lblk;
+ if ((retval = ext2fs_extent_get_info(handle, &info)))
+ return retval;
+ orig_height = info.max_depth - info.curr_level;
+
/* traverse up until index not first, or startblk matches, or top */
while (handle->level > 0 &&
(path->left == path->entries - 1)) {
@@ -750,7 +756,7 @@ static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
}
/* put handle back to where we started */
- retval = ext2fs_extent_goto(handle, start);
+ retval = ext2fs_extent_goto2(handle, orig_height, start);
done:
return retval;
}
@@ -813,7 +819,7 @@ errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle,
*
* handle will be left pointing at original record.
*/
-static errcode_t extent_node_split(ext2_extent_handle_t handle)
+errcode_t ext2fs_extent_node_split(ext2_extent_handle_t handle)
{
errcode_t retval = 0;
blk64_t new_node_pblk;
@@ -868,12 +874,12 @@ static errcode_t extent_node_split(ext2_extent_handle_t handle)
goto done;
goal_blk = extent.e_pblk;
- retval = extent_node_split(handle);
+ retval = ext2fs_extent_node_split(handle);
if (retval)
goto done;
/* get handle back to our original split position */
- retval = extent_goto(handle, orig_height, orig_lblk);
+ retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
if (retval)
goto done;
}
@@ -928,8 +934,7 @@ static errcode_t extent_node_split(ext2_extent_handle_t handle)
if (log_flex)
group = group & ~((1 << (log_flex)) - 1);
- goal_blk = (group * handle->fs->super->s_blocks_per_group) +
- handle->fs->super->s_first_data_block;
+ goal_blk = ext2fs_group_first_block2(handle->fs, group);
}
retval = ext2fs_alloc_block2(handle->fs, goal_blk, block_buf,
&new_node_pblk);
@@ -1022,12 +1027,13 @@ static errcode_t extent_node_split(ext2_extent_handle_t handle)
}
/* get handle back to our original position */
- retval = extent_goto(handle, orig_height, orig_lblk);
+ retval = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
if (retval)
goto done;
/* new node hooked in, so update inode block count (do this here?) */
- handle->inode->i_blocks += handle->fs->blocksize / 512;
+ handle->inode->i_blocks += (handle->fs->blocksize *
+ EXT2FS_CLUSTER_RATIO(handle->fs)) / 512;
retval = ext2fs_write_inode(handle->fs, handle->ino,
handle->inode);
if (retval)
@@ -1072,7 +1078,7 @@ errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
printf("node full (level %d) - splitting\n",
handle->level);
#endif
- retval = extent_node_split(handle);
+ retval = ext2fs_extent_node_split(handle);
if (retval)
return retval;
path = handle->path + handle->level;
@@ -1086,8 +1092,10 @@ errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags,
ix++;
path->left--;
}
- } else
+ } else {
ix = EXT_FIRST_INDEX(eh);
+ path->left = -1;
+ }
path->curr = ix;
@@ -1351,6 +1359,9 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
&next_extent);
if (retval)
goto done;
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval)
+ goto done;
} else
retval = ext2fs_extent_insert(handle,
EXT2_EXTENT_INSERT_AFTER, &newextent);
@@ -1403,15 +1414,22 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
retval = ext2fs_extent_replace(handle, 0, &extent);
if (retval)
goto done;
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval)
+ goto done;
} else {
__u32 orig_length;
+ blk64_t orig_lblk;
+ struct ext2fs_extent orig_extent;
+ errcode_t r2;
#ifdef DEBUG
printf("(re/un)mapping in middle of extent\n");
#endif
/* need to split this extent; later */
-
+ orig_lblk = extent.e_lblk;
orig_length = extent.e_len;
+ orig_extent = extent;
/* shorten pre-split extent */
extent.e_len = (logical - extent.e_lblk);
@@ -1423,8 +1441,13 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
/* insert new extent after current */
retval = ext2fs_extent_insert(handle,
EXT2_EXTENT_INSERT_AFTER, &newextent);
- if (retval)
+ if (retval) {
+ r2 = ext2fs_extent_goto(handle, orig_lblk);
+ if (r2 == 0)
+ ext2fs_extent_replace(handle, 0,
+ &orig_extent);
goto done;
+ }
}
/* add post-split extent */
extent.e_pblk += extent.e_len + 1;
@@ -1432,15 +1455,25 @@ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle,
extent.e_len = orig_length - extent.e_len - 1;
retval = ext2fs_extent_insert(handle,
EXT2_EXTENT_INSERT_AFTER, &extent);
- if (retval)
+ if (retval) {
+ if (physical) {
+ r2 = ext2fs_extent_goto(handle,
+ newextent.e_lblk);
+ if (r2 == 0)
+ ext2fs_extent_delete(handle, 0);
+ }
+ r2 = ext2fs_extent_goto(handle, orig_lblk);
+ if (r2 == 0)
+ ext2fs_extent_replace(handle, 0, &orig_extent);
goto done;
+ }
}
done:
/* get handle back to its position */
if (orig_height > handle->max_depth)
orig_height = handle->max_depth; /* In case we shortened the tree */
- extent_goto(handle, orig_height, orig_lblk);
+ ext2fs_extent_goto2(handle, orig_height, orig_lblk);
return retval;
}
@@ -1501,7 +1534,9 @@ errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags)
return retval;
retval = ext2fs_extent_delete(handle, flags);
- handle->inode->i_blocks -= handle->fs->blocksize / 512;
+ handle->inode->i_blocks -=
+ (handle->fs->blocksize *
+ EXT2FS_CLUSTER_RATIO(handle->fs)) / 512;
retval = ext2fs_write_inode(handle->fs, handle->ino,
handle->inode);
ext2fs_block_alloc_stats2(handle->fs,
@@ -1550,460 +1585,10 @@ errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle,
}
#ifdef DEBUG
-
-#include "ss/ss.h"
-
-#include "debugfs.h"
-
/*
- * Hook in new commands into debugfs
+ * Override debugfs's prompt
*/
const char *debug_prog_name = "tst_extents";
-extern ss_request_table extent_cmds;
-ss_request_table *extra_cmds = &extent_cmds;
-
-ext2_ino_t current_ino = 0;
-ext2_extent_handle_t current_handle;
-
-int common_extent_args_process(int argc, char *argv[], int min_argc,
- int max_argc, const char *cmd,
- const char *usage, int flags)
-{
- if (common_args_process(argc, argv, min_argc, max_argc, cmd,
- usage, flags))
- return 1;
-
- if (!current_handle) {
- com_err(cmd, 0, "Extent handle not open");
- return 1;
- }
- return 0;
-}
-
-void do_inode(int argc, char *argv[])
-{
- ext2_ino_t inode;
- int i;
- struct ext3_extent_header *eh;
- errcode_t retval;
-
- if (check_fs_open(argv[0]))
- return;
-
- if (argc == 1) {
- if (current_ino)
- printf("Current inode is %d\n", current_ino);
- else
- printf("No current inode\n");
- return;
- }
-
- if (common_inode_args_process(argc, argv, &inode, 0)) {
- return;
- }
-
- current_ino = 0;
-
- retval = ext2fs_extent_open(current_fs, inode, &current_handle);
- if (retval) {
- com_err(argv[1], retval, "while opening extent handle");
- return;
- }
-
- current_ino = inode;
-
- printf("Loaded inode %d\n", current_ino);
-
- return;
-}
-
-void generic_goto_node(char *cmd_name, int op)
-{
- struct ext2fs_extent extent;
- errcode_t retval;
-
- if (check_fs_open(cmd_name))
- return;
-
- if (!current_handle) {
- com_err(cmd_name, 0, "Extent handle not open");
- return;
- }
-
- retval = ext2fs_extent_get(current_handle, op, &extent);
- if (retval) {
- com_err(cmd_name, retval, 0);
- return;
- }
- dbg_print_extent(0, &extent);
-}
-
-void do_current_node(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_CURRENT);
-}
-
-void do_root_node(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_ROOT);
-}
-
-void do_last_leaf(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_LAST_LEAF);
-}
-
-void do_first_sib(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_FIRST_SIB);
-}
-
-void do_last_sib(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_LAST_SIB);
-}
-
-void do_next_sib(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_NEXT_SIB);
-}
-
-void do_prev_sib(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_PREV_SIB);
-}
-
-void do_next_leaf(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_NEXT_LEAF);
-}
-
-void do_prev_leaf(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_PREV_LEAF);
-}
-
-void do_next(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_NEXT);
-}
-
-void do_prev(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_PREV);
-}
-
-void do_up(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_UP);
-}
-
-void do_down(int argc, char *argv[])
-{
- generic_goto_node(argv[0], EXT2_EXTENT_DOWN);
-}
-
-void do_delete_node(int argc, char *argv[])
-{
- errcode_t retval;
- int err;
-
- if (common_extent_args_process(argc, argv, 1, 1, "delete_node",
- "", CHECK_FS_RW | CHECK_FS_BITMAPS))
- return;
-
- retval = ext2fs_extent_delete(current_handle, 0);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
- if (current_handle->path && current_handle->path[0].curr)
- do_current_node(argc, argv);
-}
-
-void do_replace_node(int argc, char *argv[])
-{
- const char *usage = "[--uninit] <lblk> <len> <pblk>";
- errcode_t retval;
- struct ext2fs_extent extent;
- int err;
-
- if (common_extent_args_process(argc, argv, 3, 5, "replace_node",
- usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
- return;
-
- extent.e_flags = 0;
-
- if (!strcmp(argv[1], "--uninit")) {
- argc--;
- argv++;
- extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
- }
-
- if (argc != 4) {
- fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
- return;
- }
-
- extent.e_lblk = parse_ulong(argv[1], argv[0], "logical block", &err);
- if (err)
- return;
-
- extent.e_len = parse_ulong(argv[2], argv[0], "logical block", &err);
- if (err)
- return;
-
- extent.e_pblk = parse_ulong(argv[3], argv[0], "logical block", &err);
- if (err)
- return;
-
- retval = ext2fs_extent_replace(current_handle, 0, &extent);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
- do_current_node(argc, argv);
-}
-
-void do_split_node(int argc, char *argv[])
-{
- errcode_t retval;
- struct ext2fs_extent extent;
- int err;
-
- if (common_extent_args_process(argc, argv, 1, 1, "split_node",
- "", CHECK_FS_RW | CHECK_FS_BITMAPS))
- return;
- retval = extent_node_split(current_handle);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
- do_current_node(argc, argv);
-}
-
-void do_insert_node(int argc, char *argv[])
-{
- const char *usage = "[--after] [--uninit] <lblk> <len> <pblk>";
- errcode_t retval;
- struct ext2fs_extent extent;
- char *cmd;
- int err;
- int flags = 0;
-
- if (common_extent_args_process(argc, argv, 3, 6, "insert_node",
- usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
- return;
-
- cmd = argv[0];
-
- extent.e_flags = 0;
-
- while (argc > 2) {
- if (!strcmp(argv[1], "--after")) {
- argc--;
- argv++;
- flags |= EXT2_EXTENT_INSERT_AFTER;
- continue;
- }
- if (!strcmp(argv[1], "--uninit")) {
- argc--;
- argv++;
- extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
- continue;
- }
- break;
- }
-
- if (argc != 4) {
- fprintf(stderr, "usage: %s %s\n", cmd, usage);
- return;
- }
-
- extent.e_lblk = parse_ulong(argv[1], cmd,
- "logical block", &err);
- if (err)
- return;
-
- extent.e_len = parse_ulong(argv[2], cmd,
- "length", &err);
- if (err)
- return;
-
- extent.e_pblk = parse_ulong(argv[3], cmd,
- "pysical block", &err);
- if (err)
- return;
-
- retval = ext2fs_extent_insert(current_handle, flags, &extent);
- if (retval) {
- com_err(cmd, retval, 0);
- return;
- }
- do_current_node(argc, argv);
-}
-
-void do_set_bmap(int argc, char **argv)
-{
- const char *usage = "[--uninit] <lblk> <pblk>";
- errcode_t retval;
- blk_t logical;
- blk_t physical;
- char *cmd = argv[0];
- int flags = 0;
- int err;
-
- if (common_extent_args_process(argc, argv, 3, 5, "set_bmap",
- usage, CHECK_FS_RW | CHECK_FS_BITMAPS))
- return;
-
- if (argc > 2 && !strcmp(argv[1], "--uninit")) {
- argc--;
- argv++;
- flags |= EXT2_EXTENT_SET_BMAP_UNINIT;
- }
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s %s\n", cmd, usage);
- return;
- }
-
- logical = parse_ulong(argv[1], cmd,
- "logical block", &err);
- if (err)
- return;
-
- physical = parse_ulong(argv[2], cmd,
- "physical block", &err);
- if (err)
- return;
-
- retval = ext2fs_extent_set_bmap(current_handle, logical,
- (blk64_t) physical, flags);
- if (retval) {
- com_err(cmd, retval, 0);
- return;
- }
- if (current_handle->path && current_handle->path[0].curr)
- do_current_node(argc, argv);
-}
-
-void do_print_all(int argc, char **argv)
-{
- const char *usage = "[--leaf-only|--reverse|--reverse-leaf]";
- struct ext2fs_extent extent;
- errcode_t retval;
- errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT;
- int op = EXT2_EXTENT_NEXT;
- int first_op = EXT2_EXTENT_ROOT;
-
-
- if (common_extent_args_process(argc, argv, 1, 2, "print_all",
- usage, 0))
- return;
-
- if (argc == 2) {
- if (!strcmp(argv[1], "--leaf-only"))
- op = EXT2_EXTENT_NEXT_LEAF;
- else if (!strcmp(argv[1], "--reverse")) {
- op = EXT2_EXTENT_PREV;
- first_op = EXT2_EXTENT_LAST_LEAF;
- end_err = EXT2_ET_EXTENT_NO_PREV;
- } else if (!strcmp(argv[1], "--reverse-leaf")) {
- op = EXT2_EXTENT_PREV_LEAF;
- first_op = EXT2_EXTENT_LAST_LEAF;
- end_err = EXT2_ET_EXTENT_NO_PREV;
- } else {
- fprintf(stderr, "Usage: %s %s\n", argv[0], usage);
- return;
- }
- }
-
- retval = ext2fs_extent_get(current_handle, first_op, &extent);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
- dbg_print_extent(0, &extent);
-
- while (1) {
- retval = ext2fs_extent_get(current_handle, op, &extent);
- if (retval == end_err)
- break;
-
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
- dbg_print_extent(0, &extent);
- }
-}
-
-void do_info(int argc, char **argv)
-{
- struct ext2fs_extent extent;
- struct ext2_extent_info info;
- errcode_t retval;
-
- if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0))
- return;
-
- retval = ext2fs_extent_get_info(current_handle, &info);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
-
- retval = ext2fs_extent_get(current_handle,
- EXT2_EXTENT_CURRENT, &extent);
- if (retval) {
- com_err(argv[0], retval, 0);
- return;
- }
-
- dbg_print_extent(0, &extent);
-
- printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n",
- info.curr_entry, info.num_entries, info.max_entries,
- info.bytes_avail, info.curr_level, info.max_depth);
- printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk,
- info.max_pblk);
- printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len,
- info.max_uninit_len);
-}
-
-void do_goto_block(int argc, char **argv)
-{
- struct ext2fs_extent extent;
- errcode_t retval;
- int op = EXT2_EXTENT_NEXT_LEAF;
- blk64_t blk;
- int level = 0, err;
-
- if (common_extent_args_process(argc, argv, 2, 3, "goto_block",
- "block [level]", 0))
- return;
-
- if (strtoblk(argv[0], argv[1], &blk))
- return;
-
- if (argc == 3) {
- level = parse_ulong(argv[2], argv[0], "level", &err);
- if (err)
- return;
- }
-
- retval = extent_goto(current_handle, level, (blk64_t) blk);
-
- if (retval) {
- com_err(argv[0], retval,
- "while trying to go to block %llu, level %d",
- (unsigned long long) blk, level);
- return;
- }
-
- generic_goto_node(argv[0], EXT2_EXTENT_CURRENT);
-}
#endif
diff --git a/lib/ext2fs/extent_dbg.ct b/lib/ext2fs/extent_dbg.ct
deleted file mode 100644
index d0571f47..00000000
--- a/lib/ext2fs/extent_dbg.ct
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
-# under the terms of the GNU Public License.
-#
-command_table extent_cmds;
-
-request do_inode, "Open an inode",
- inode;
-
-request do_current_node, "Current extent node",
- current_node, current;
-
-request do_root_node, "Goto root extent",
- root_node, root;
-
-request do_last_leaf, "Goto last leaf",
- last_leaf;
-
-request do_first_sib, "Goto first sibling",
- first_sibling, first_sib;
-
-request do_last_sib, "Goto last sibling",
- last_sibling, last_sib;
-
-request do_next_sib, "Goto next sibling",
- next_sibling, next_sib, ns;
-
-request do_prev_sib, "Goto previous sibling",
- prev_sibling, prev_sib, ps;
-
-request do_next_leaf, "Goto next leaf",
- next_leaf, nl;
-
-request do_prev_leaf, "Goto previous leaf",
- prev_leaf, pl;
-
-request do_next, "Goto next node",
- next, n;
-
-request do_prev, "Goto previous node",
- previous, prev, p;
-
-request do_up, "Up node",
- up_node, up, u;
-
-request do_down, "Down node",
- down_node, down, d;
-
-request do_delete_node, "Delete node",
- delete_node, delete;
-
-request do_insert_node, "Insert node",
- insert_node, insert;
-
-request do_split_node, "Split node",
- split_node, split;
-
-request do_set_bmap, "Set block mapping",
- set_bmap;
-
-request do_replace_node, "Insert node",
- replace_node, replace;
-
-request do_print_all, "Iterate over all nodes and print them",
- print_all, all;
-
-request do_goto_block, "Goto extent containing specified block",
- goto_block, goto;
-
-request do_info, "Print extent info",
- info;
-
-end;
-
diff --git a/lib/ext2fs/fiemap.h b/lib/ext2fs/fiemap.h
index 30bf5555..895cd0ba 100644
--- a/lib/ext2fs/fiemap.h
+++ b/lib/ext2fs/fiemap.h
@@ -64,5 +64,7 @@ struct fiemap {
#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
* support extents. Result
* merged for efficiency. */
+#define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other
+ * files. */
#endif /* _LINUX_FIEMAP_H */
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index 1f7002cd..5a39c321 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -18,6 +18,7 @@
#include "ext2_fs.h"
#include "ext2fs.h"
+#include "ext2fsP.h"
struct ext2_file {
errcode_t magic;
@@ -142,8 +143,7 @@ errcode_t ext2fs_file_flush(ext2_file_t file)
return retval;
}
- retval = io_channel_write_blk(fs->io, file->physblock,
- 1, file->buf);
+ retval = io_channel_write_blk64(fs->io, file->physblock, 1, file->buf);
if (retval)
return retval;
@@ -158,7 +158,7 @@ errcode_t ext2fs_file_flush(ext2_file_t file)
*/
static errcode_t sync_buffer_position(ext2_file_t file)
{
- blk_t b;
+ blk64_t b;
errcode_t retval;
b = file->pos / file->fs->blocksize;
@@ -194,9 +194,9 @@ static errcode_t load_buffer(ext2_file_t file, int dontfill)
return retval;
if (!dontfill) {
if (file->physblock) {
- retval = io_channel_read_blk(fs->io,
- file->physblock,
- 1, file->buf);
+ retval = io_channel_read_blk64(fs->io,
+ file->physblock,
+ 1, file->buf);
if (retval)
return retval;
} else
@@ -298,6 +298,20 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
if (retval)
goto fail;
+ /*
+ * OK, the physical block hasn't been allocated yet.
+ * Allocate it.
+ */
+ if (!file->physblock) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode,
+ BMAP_BUFFER,
+ file->ino ? BMAP_ALLOC : 0,
+ file->blockno, 0,
+ &file->physblock);
+ if (retval)
+ goto fail;
+ }
+
file->flags |= EXT2_FILE_BUF_DIRTY;
memcpy(file->buf+start, ptr, c);
file->pos += c;
@@ -307,6 +321,15 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
}
fail:
+ /* Update inode size */
+ if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+ errcode_t rc;
+
+ rc = ext2fs_file_set_size2(file, file->pos);
+ if (retval == 0)
+ retval = rc;
+ }
+
if (written)
*written = count;
return retval;
@@ -371,6 +394,53 @@ ext2_off_t ext2fs_file_get_size(ext2_file_t file)
return size;
}
+/* Zero the parts of the last block that are past EOF. */
+static errcode_t ext2fs_file_zero_past_offset(ext2_file_t file,
+ ext2_off64_t offset)
+{
+ ext2_filsys fs = file->fs;
+ char *b = NULL;
+ ext2_off64_t off = offset % fs->blocksize;
+ blk64_t blk;
+ int ret_flags;
+ errcode_t retval;
+
+ if (off == 0)
+ return 0;
+
+ retval = sync_buffer_position(file);
+ if (retval)
+ return retval;
+
+ /* Is there an initialized block at the end? */
+ retval = ext2fs_bmap2(fs, file->ino, NULL, NULL, 0,
+ offset / fs->blocksize, &ret_flags, &blk);
+ if (retval)
+ return retval;
+ if ((blk == 0) || (ret_flags & BMAP_RET_UNINIT))
+ return 0;
+
+ /* Zero to the end of the block */
+ retval = ext2fs_get_mem(fs->blocksize, &b);
+ if (retval)
+ return retval;
+
+ /* Read/zero/write block */
+ retval = io_channel_read_blk64(fs->io, blk, 1, b);
+ if (retval)
+ goto out;
+
+ memset(b + off, 0, fs->blocksize - off);
+
+ retval = io_channel_write_blk64(fs->io, blk, 1, b);
+ if (retval)
+ goto out;
+
+out:
+ ext2fs_free_mem(&b);
+ return retval;
+}
+
/*
* This function sets the size of the file, truncating it if necessary
*
@@ -383,11 +453,26 @@ errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size)
EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ if (size && ext2fs_file_block_offset_too_big(file->fs, &file->inode,
+ (size - 1) / file->fs->blocksize))
+ return EXT2_ET_FILE_TOO_BIG;
truncate_block = ((size + file->fs->blocksize - 1) >>
- EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1;
+ EXT2_BLOCK_SIZE_BITS(file->fs->super));
old_size = EXT2_I_SIZE(&file->inode);
old_truncate = ((old_size + file->fs->blocksize - 1) >>
- EXT2_BLOCK_SIZE_BITS(file->fs->super)) + 1;
+ EXT2_BLOCK_SIZE_BITS(file->fs->super));
+
+ /* If we're writing a large file, set the large_file flag */
+ if (LINUX_S_ISREG(file->inode.i_mode) &&
+ ext2fs_needs_large_file_feature(EXT2_I_SIZE(&file->inode)) &&
+ (!EXT2_HAS_RO_COMPAT_FEATURE(file->fs->super,
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE) ||
+ file->fs->super->s_rev_level == EXT2_GOOD_OLD_REV)) {
+ file->fs->super->s_feature_ro_compat |=
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+ ext2fs_update_dynamic_rev(file->fs);
+ ext2fs_mark_super_dirty(file->fs);
+ }
file->inode.i_size = size & 0xffffffff;
file->inode.i_size_high = (size >> 32);
@@ -397,6 +482,10 @@ errcode_t ext2fs_file_set_size2(ext2_file_t file, ext2_off64_t size)
return retval;
}
+ retval = ext2fs_file_zero_past_offset(file, size);
+ if (retval)
+ return retval;
+
if (truncate_block >= old_truncate)
return 0;
diff --git a/lib/ext2fs/flushb.c b/lib/ext2fs/flushb.c
index ac8923cb..98821fc7 100644
--- a/lib/ext2fs/flushb.c
+++ b/lib/ext2fs/flushb.c
@@ -70,7 +70,7 @@ errcode_t ext2fs_sync_device(int fd, int flushb)
#warning BLKFLSBUF not defined
#endif
#ifdef FDFLUSH
- ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */
+ return ioctl(fd, FDFLUSH, 0); /* In case this is a floppy */
#elif defined(__linux__)
#warning FDFLUSH not defined
#endif
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
index 28c4132f..1ad2d916 100644
--- a/lib/ext2fs/freefs.c
+++ b/lib/ext2fs/freefs.c
@@ -43,6 +43,8 @@ void ext2fs_free(ext2_filsys fs)
ext2fs_free_block_bitmap(fs->block_map);
if (fs->inode_map)
ext2fs_free_inode_bitmap(fs->inode_map);
+ if (fs->image_header)
+ ext2fs_free_mem(&fs->image_header);
if (fs->badblocks)
ext2fs_badblocks_list_free(fs->badblocks);
diff --git a/lib/ext2fs/gen_bitmap.c b/lib/ext2fs/gen_bitmap.c
index 6679bffa..6cd6fe63 100644
--- a/lib/ext2fs/gen_bitmap.c
+++ b/lib/ext2fs/gen_bitmap.c
@@ -504,6 +504,52 @@ static int ext2fs_test_clear_generic_bitmap_range(ext2fs_generic_bitmap bitmap,
return ext2fs_mem_is_zero(ADDR + start_byte, len_byte);
}
+errcode_t ext2fs_find_first_zero_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out)
+{
+ blk_t b;
+
+ if (start < bitmap->start || end > bitmap->end || start > end) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ while (start <= end) {
+ b = ext2fs_test_bit(start - bitmap->start, bitmap->bitmap);
+ if (!b) {
+ *out = start;
+ return 0;
+ }
+ start++;
+ }
+
+ return ENOENT;
+}
+
+errcode_t ext2fs_find_first_set_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out)
+{
+ blk_t b;
+
+ if (start < bitmap->start || end > bitmap->end || start > end) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ while (start <= end) {
+ b = ext2fs_test_bit(start - bitmap->start, bitmap->bitmap);
+ if (b) {
+ *out = start;
+ return 0;
+ }
+ start++;
+ }
+
+ return ENOENT;
+}
+
int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
blk_t block, int num)
{
@@ -558,3 +604,4 @@ void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
ext2fs_fast_clear_bit(block + i - bitmap->start,
bitmap->bitmap);
}
+
diff --git a/lib/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
index b57df546..af550972 100644
--- a/lib/ext2fs/gen_bitmap64.c
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -128,6 +128,7 @@ errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
if (gettimeofday(&bitmap->stats.created,
(struct timezone *) NULL) == -1) {
perror("gettimeofday");
+ ext2fs_free_mem(&bitmap);
return 1;
}
bitmap->stats.type = type;
@@ -174,11 +175,13 @@ errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
}
#ifdef BMAP_STATS
-void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap)
+static void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap bitmap)
{
struct ext2_bmap_statistics *stats = &bitmap->stats;
+#ifdef BMAP_STATS_OPS
float mark_seq_perc = 0.0, test_seq_perc = 0.0;
float mark_back_perc = 0.0, test_back_perc = 0.0;
+#endif
double inuse;
struct timeval now;
@@ -298,6 +301,7 @@ errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src,
if (gettimeofday(&new_bmap->stats.created,
(struct timezone *) NULL) == -1) {
perror("gettimeofday");
+ ext2fs_free_mem(&new_bmap);
return 1;
}
new_bmap->stats.type = src->stats.type;
@@ -622,6 +626,8 @@ void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap)
int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
+ __u64 end = block + num;
+
if (!bmap)
return EINVAL;
@@ -644,12 +650,26 @@ int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap,
INC_STAT(bmap, test_ext_count);
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1 << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
+ if ((block < bmap->start) || (block+num-1 > bmap->end)) {
+ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, block,
+ bmap->description);
+ return EINVAL;
+ }
+
return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num);
}
void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
+ __u64 end = block + num;
+
if (!bmap)
return;
@@ -668,6 +688,12 @@ void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap,
INC_STAT(bmap, mark_ext_count);
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1 << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
if ((block < bmap->start) || (block+num-1 > bmap->end)) {
ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
bmap->description);
@@ -680,6 +706,8 @@ void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap,
void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap,
blk64_t block, unsigned int num)
{
+ __u64 end = block + num;
+
if (!bmap)
return;
@@ -698,6 +726,12 @@ void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap,
INC_STAT(bmap, unmark_ext_count);
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1 << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
if ((block < bmap->start) || (block+num-1 > bmap->end)) {
ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
bmap->description);
@@ -766,27 +800,107 @@ errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs,
errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 start, __u64 end, __u64 *out)
{
- int b;
+ __u64 cstart, cend, cout;
+ errcode_t retval;
- if (bitmap->bitmap_ops->find_first_zero)
- return bitmap->bitmap_ops->find_first_zero(bitmap, start, end, out);
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ blk_t blk = 0;
- if (!bitmap || !EXT2FS_IS_64_BITMAP(bitmap) || bitmap->cluster_bits)
+ if (((start) & ~0xffffffffULL) ||
+ ((end) & ~0xffffffffULL)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ retval = ext2fs_find_first_zero_generic_bitmap(bitmap, start,
+ end, &blk);
+ if (retval == 0)
+ *out = blk;
+ return retval;
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
return EINVAL;
- if (start < bitmap->start || end > bitmap->end || start > end) {
+ cstart = start >> bitmap->cluster_bits;
+ cend = end >> bitmap->cluster_bits;
+
+ if (cstart < bitmap->start || cend > bitmap->end || start > end) {
warn_bitmap(bitmap, EXT2FS_TEST_ERROR, start);
return EINVAL;
}
- while (start <= end) {
- b = bitmap->bitmap_ops->test_bmap(bitmap, start);
- if (!b) {
- *out = start;
- return 0;
+ if (bitmap->bitmap_ops->find_first_zero) {
+ retval = bitmap->bitmap_ops->find_first_zero(bitmap, cstart,
+ cend, &cout);
+ if (retval)
+ return retval;
+ found:
+ cout <<= bitmap->cluster_bits;
+ *out = (cout >= start) ? cout : start;
+ return 0;
+ }
+
+ for (cout = cstart; cout <= cend; cout++)
+ if (!bitmap->bitmap_ops->test_bmap(bitmap, cout))
+ goto found;
+
+ return ENOENT;
+}
+
+errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ __u64 cstart, cend, cout;
+ errcode_t retval;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ blk_t blk = 0;
+
+ if (((start) & ~0xffffffffULL) ||
+ ((end) & ~0xffffffffULL)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
}
- start++;
+
+ retval = ext2fs_find_first_set_generic_bitmap(bitmap, start,
+ end, &blk);
+ if (retval == 0)
+ *out = blk;
+ return retval;
}
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return EINVAL;
+
+ cstart = start >> bitmap->cluster_bits;
+ cend = end >> bitmap->cluster_bits;
+
+ if (cstart < bitmap->start || cend > bitmap->end || start > end) {
+ warn_bitmap(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ if (bitmap->bitmap_ops->find_first_set) {
+ retval = bitmap->bitmap_ops->find_first_set(bitmap, cstart,
+ cend, &cout);
+ if (retval)
+ return retval;
+ found:
+ cout <<= bitmap->cluster_bits;
+ *out = (cout >= start) ? cout : start;
+ return 0;
+ }
+
+ for (cout = cstart; cout <= cend; cout++)
+ if (bitmap->bitmap_ops->test_bmap(bitmap, cout))
+ goto found;
+
return ENOENT;
}
diff --git a/lib/ext2fs/get_pathname.c b/lib/ext2fs/get_pathname.c
index 33209949..52aea624 100644
--- a/lib/ext2fs/get_pathname.c
+++ b/lib/ext2fs/get_pathname.c
@@ -100,16 +100,16 @@ static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir,
retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp);
if (retval == EXT2_ET_NO_DIRECTORY) {
- char buf[32];
+ char tmp[32];
if (ino)
- snprintf(buf, sizeof(buf), "<%u>/<%u>", dir, ino);
+ snprintf(tmp, sizeof(tmp), "<%u>/<%u>", dir, ino);
else
- snprintf(buf, sizeof(buf), "<%u>", dir);
- retval = ext2fs_get_mem(strlen(buf)+1, name);
+ snprintf(tmp, sizeof(tmp), "<%u>", dir);
+ retval = ext2fs_get_mem(strlen(tmp)+1, name);
if (retval)
goto cleanup;
- strcpy(*name, buf);
+ strcpy(*name, tmp);
return 0;
} else if (retval)
goto cleanup;
diff --git a/lib/ext2fs/getsectsize.c b/lib/ext2fs/getsectsize.c
index 30faecc7..9c3f4a2e 100644
--- a/lib/ext2fs/getsectsize.c
+++ b/lib/ext2fs/getsectsize.c
@@ -62,6 +62,32 @@ errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize)
}
/*
+ * Return desired alignment for direct I/O
+ */
+int ext2fs_get_dio_alignment(int fd)
+{
+ int align = 0;
+
+#ifdef BLKSSZGET
+ if (ioctl(fd, BLKSSZGET, &align) < 0)
+ align = 0;
+#endif
+
+#ifdef _SC_PAGESIZE
+ if (align <= 0)
+ align = sysconf(_SC_PAGESIZE);
+#endif
+#ifdef HAVE_GETPAGESIZE
+ if (align <= 0)
+ align = getpagesize();
+#endif
+ if (align <= 0)
+ align = 4096;
+
+ return align;
+}
+
+/*
* Returns the physical sector size of a device
*/
errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize)
diff --git a/lib/ext2fs/getsize.c b/lib/ext2fs/getsize.c
index 0a7053e3..a9a4812e 100644
--- a/lib/ext2fs/getsize.c
+++ b/lib/ext2fs/getsize.c
@@ -140,25 +140,11 @@ static int valid_offset (int fd, ext2_loff_t offset)
* Returns the number of blocks in a partition
*/
errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
- blk64_t *retblocks)
+ blk64_t *retblocks)
{
int fd, rc = 0;
- int valid_blkgetsize64 = 1;
-#ifdef __linux__
- struct utsname ut;
-#endif
unsigned long long size64;
- unsigned long size;
ext2_loff_t high, low;
-#ifdef FDGETPRM
- struct floppy_struct this_floppy;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
- int part;
- struct disklabel lab;
- struct partition *pp;
- char ch;
-#endif /* HAVE_SYS_DISKLABEL_H */
fd = ext2fs_open_file(file, O_RDONLY, 0);
if (fd < 0)
@@ -172,63 +158,83 @@ errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
#endif
#ifdef BLKGETSIZE64
+ {
+ int valid_blkgetsize64 = 1;
#ifdef __linux__
- if ((uname(&ut) == 0) &&
- ((ut.release[0] == '2') && (ut.release[1] == '.') &&
- (ut.release[2] < '6') && (ut.release[3] == '.')))
- valid_blkgetsize64 = 0;
+ struct utsname ut;
+
+ if ((uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] < '6') && (ut.release[3] == '.')))
+ valid_blkgetsize64 = 0;
#endif
- if (valid_blkgetsize64 &&
- ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
- *retblocks = size64 / blocksize;
- goto out;
+ if (valid_blkgetsize64 &&
+ ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+ *retblocks = size64 / blocksize;
+ goto out;
+ }
}
#endif /* BLKGETSIZE64 */
#ifdef BLKGETSIZE
- if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
- *retblocks = size / (blocksize / 512);
- goto out;
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ *retblocks = size / (blocksize / 512);
+ goto out;
+ }
}
#endif
#ifdef FDGETPRM
- if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
- *retblocks = this_floppy.size / (blocksize / 512);
- goto out;
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+ *retblocks = this_floppy.size / (blocksize / 512);
+ goto out;
+ }
}
#endif
#ifdef HAVE_SYS_DISKLABEL_H
-#if defined(DIOCGMEDIASIZE)
{
- off_t ms;
- u_int bs;
- if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
- *retblocks = ms / blocksize;
- goto out;
- }
- }
+ int part;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+
+#if defined(DIOCGMEDIASIZE)
+ {
+ off_t ms;
+ u_int bs;
+ if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
+ *retblocks = ms / blocksize;
+ goto out;
+ }
+ }
#elif defined(DIOCGDINFO)
- /* old disklabel interface */
- part = strlen(file) - 1;
- if (part >= 0) {
- ch = file[part];
- if (isdigit(ch))
- part = 0;
- else if (ch >= 'a' && ch <= 'h')
- part = ch - 'a';
- else
- part = -1;
- }
- if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
- pp = &lab.d_partitions[part];
- if (pp->p_size) {
- *retblocks = pp->p_size / (blocksize / 512);
- goto out;
+ /* old disklabel interface */
+ part = strlen(file) - 1;
+ if (part >= 0) {
+ ch = file[part];
+ if (isdigit(ch))
+ part = 0;
+ else if (ch >= 'a' && ch <= 'h')
+ part = ch - 'a';
+ else
+ part = -1;
+ }
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size) {
+ *retblocks = pp->p_size / (blocksize / 512);
+ goto out;
+ }
}
- }
#endif /* defined(DIOCG*) */
+ }
#endif /* HAVE_SYS_DISKLABEL_H */
{
@@ -247,10 +253,9 @@ errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
* find the size of the partition.
*/
low = 0;
- for (high = 1024; valid_offset (fd, high); high *= 2)
+ for (high = 1024; valid_offset(fd, high); high *= 2)
low = high;
- while (low < high - 1)
- {
+ while (low < high - 1) {
const ext2_loff_t mid = (low + high) / 2;
if (valid_offset (fd, mid))
@@ -258,7 +263,7 @@ errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
else
high = mid;
}
- valid_offset (fd, 0);
+ valid_offset(fd, 0);
size64 = low + 1;
*retblocks = size64 / blocksize;
out:
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
index 8b46eda4..a3b20f06 100644
--- a/lib/ext2fs/icount.c
+++ b/lib/ext2fs/icount.c
@@ -181,6 +181,7 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
errcode_t retval;
char *fn, uuid[40];
ext2_ino_t num_inodes;
+ mode_t save_umask;
int fd;
retval = alloc_icount(fs, flags, &icount);
@@ -192,8 +193,14 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
goto errout;
uuid_unparse(fs->super->s_uuid, uuid);
sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid);
+ icount->tdb_fn = fn;
+ save_umask = umask(077);
fd = mkstemp(fn);
-
+ if (fd < 0) {
+ retval = errno;
+ goto errout;
+ }
+ umask(save_umask);
/*
* This is an overestimate of the size that we will need; the
* ideal value is the number of used inodes with a count
@@ -204,18 +211,15 @@ errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
*/
num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
- icount->tdb_fn = fn;
icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (icount->tdb) {
- close(fd);
- *ret = icount;
- return 0;
- }
-
- retval = errno;
close(fd);
-
+ if (icount->tdb == NULL) {
+ retval = errno;
+ goto errout;
+ }
+ *ret = icount;
+ return 0;
errout:
ext2fs_free_icount(icount);
return(retval);
@@ -351,9 +355,7 @@ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
ext2_ino_t ino, int create)
{
- float range;
int low, high, mid;
- ext2_ino_t lowval, highval;
if (!icount || !icount->list)
return 0;
diff --git a/lib/ext2fs/imager.c b/lib/ext2fs/imager.c
index a0fb81e4..378a3c88 100644
--- a/lib/ext2fs/imager.c
+++ b/lib/ext2fs/imager.c
@@ -66,6 +66,7 @@ errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
blk64_t blk;
ssize_t actual;
errcode_t retval;
+ off_t r;
buf = malloc(fs->blocksize * BUF_BLOCKS);
if (!buf)
@@ -97,7 +98,11 @@ errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
blk++;
left--;
cp += fs->blocksize;
- lseek(fd, fs->blocksize, SEEK_CUR);
+ r = lseek(fd, fs->blocksize, SEEK_CUR);
+ if (r < 0) {
+ retval = errno;
+ goto errout;
+ }
continue;
}
/* Find non-zero blocks */
@@ -279,7 +284,7 @@ errout:
errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
{
ext2fs_generic_bitmap bmap;
- errcode_t err, retval;
+ errcode_t retval;
ssize_t actual;
__u32 itr, cnt, size;
int c, total_size;
@@ -292,7 +297,6 @@ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
return retval;
}
bmap = fs->inode_map;
- err = EXT2_ET_MAGIC_INODE_BITMAP;
itr = 1;
cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
@@ -303,7 +307,6 @@ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
return retval;
}
bmap = fs->block_map;
- err = EXT2_ET_MAGIC_BLOCK_BITMAP;
itr = fs->super->s_first_data_block;
cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
@@ -356,7 +359,7 @@ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
{
ext2fs_generic_bitmap bmap;
- errcode_t err, retval;
+ errcode_t retval;
__u32 itr, cnt;
char buf[1024];
unsigned int size;
@@ -369,7 +372,6 @@ errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
return retval;
}
bmap = fs->inode_map;
- err = EXT2_ET_MAGIC_INODE_BITMAP;
itr = 1;
cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
@@ -380,7 +382,6 @@ errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
return retval;
}
bmap = fs->block_map;
- err = EXT2_ET_MAGIC_BLOCK_BITMAP;
itr = fs->super->s_first_data_block;
cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index b06371cf..36c94a9a 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -98,8 +98,10 @@ errcode_t ext2fs_initialize(const char *name, int flags,
int csum_flag;
int bigalloc_flag;
int io_flags;
+ unsigned reserved_inos;
char *buf = 0;
char c;
+ double reserved_ratio;
if (!param || !ext2fs_blocks_count(param))
return EXT2_ET_INVALID_ARGUMENT;
@@ -171,6 +173,8 @@ errcode_t ext2fs_initialize(const char *name, int flags,
set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */
set_field(s_log_groups_per_flex, 0);
set_field(s_flags, 0);
+ assign_field(s_backup_bgs[0]);
+ assign_field(s_backup_bgs[1]);
if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
retval = EXT2_ET_UNSUPP_FEATURE;
goto cleanup;
@@ -205,6 +209,8 @@ errcode_t ext2fs_initialize(const char *name, int flags,
super->s_log_block_size;
if (bigalloc_flag) {
+ unsigned long long bpg;
+
if (param->s_blocks_per_group &&
param->s_clusters_per_group &&
((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) !=
@@ -218,12 +224,19 @@ errcode_t ext2fs_initialize(const char *name, int flags,
super->s_clusters_per_group =
param->s_blocks_per_group /
EXT2FS_CLUSTER_RATIO(fs);
- else
+ else if (super->s_log_cluster_size + 15 < 32)
super->s_clusters_per_group = fs->blocksize * 8;
+ else
+ super->s_clusters_per_group = (fs->blocksize - 1) * 8;
if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super))
super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super);
- super->s_blocks_per_group = EXT2FS_C2B(fs,
- super->s_clusters_per_group);
+ bpg = EXT2FS_C2B(fs,
+ (unsigned long long) super->s_clusters_per_group);
+ if (bpg >= (((unsigned long long) 1) << 32)) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+ super->s_blocks_per_group = bpg;
} else {
set_field(s_blocks_per_group, fs->blocksize * 8);
if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
@@ -239,6 +252,8 @@ errcode_t ext2fs_initialize(const char *name, int flags,
goto cleanup;
}
+ set_field(s_mmp_update_interval, 0);
+
/*
* If we're creating an external journal device, we don't need
* to bother with the rest.
@@ -259,8 +274,9 @@ retry:
goto cleanup;
}
- if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT)
- super->s_desc_size = EXT2_MIN_DESC_SIZE_64BIT;
+ set_field(s_desc_size,
+ super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ?
+ EXT2_MIN_DESC_SIZE_64BIT : 0);
fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
EXT2_DESC_PER_BLOCK(super));
@@ -390,6 +406,14 @@ ipg_retry:
if (rem && (rem < overhead+50)) {
ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) -
rem);
+ /*
+ * If blocks count is changed, we need to recalculate
+ * reserved blocks count not to exceed 50%.
+ */
+ reserved_ratio = 100.0 * ext2fs_r_blocks_count(param) /
+ ext2fs_blocks_count(param);
+ ext2fs_r_blocks_count_set(super, reserved_ratio *
+ ext2fs_blocks_count(super) / 100.0);
goto retry;
}
@@ -400,6 +424,21 @@ ipg_retry:
* count.
*/
+ /* Set up the locations of the backup superblocks */
+ if (super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
+ if (super->s_backup_bgs[0] >= fs->group_desc_count)
+ super->s_backup_bgs[0] = fs->group_desc_count - 1;
+ if (super->s_backup_bgs[1] >= fs->group_desc_count)
+ super->s_backup_bgs[1] = fs->group_desc_count - 1;
+ if (super->s_backup_bgs[0] == super->s_backup_bgs[1])
+ super->s_backup_bgs[1] = 0;
+ if (super->s_backup_bgs[0] > super->s_backup_bgs[1]) {
+ __u32 t = super->s_backup_bgs[0];
+ super->s_backup_bgs[0] = super->s_backup_bgs[1];
+ super->s_backup_bgs[1] = t;
+ }
+ }
+
retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
if (retval)
goto cleanup;
@@ -439,6 +478,7 @@ ipg_retry:
free_blocks = 0;
csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
+ reserved_inos = super->s_first_ino;
for (i = 0; i < fs->group_desc_count; i++) {
/*
* Don't set the BLOCK_UNINIT group for the last group
@@ -450,8 +490,15 @@ ipg_retry:
EXT2_BG_BLOCK_UNINIT);
ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT);
numblocks = super->s_inodes_per_group;
- if (i == 0)
- numblocks -= super->s_first_ino;
+ if (reserved_inos) {
+ if (numblocks > reserved_inos) {
+ numblocks -= reserved_inos;
+ reserved_inos = 0;
+ } else {
+ reserved_inos -= numblocks;
+ numblocks = 0;
+ }
+ }
ext2fs_bg_itable_unused_set(fs, i, numblocks);
}
numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
diff --git a/lib/ext2fs/inline.c b/lib/ext2fs/inline.c
index ad0c3683..05da1f79 100644
--- a/lib/ext2fs/inline.c
+++ b/lib/ext2fs/inline.c
@@ -11,6 +11,9 @@
* %End-Header%
*/
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 /* for posix_memalign() */
+#endif
#include "config.h"
#include <stdio.h>
@@ -45,7 +48,7 @@ errcode_t ext2fs_get_memalign(unsigned long size,
errcode_t retval;
void **p = ptr;
- if (align == 0)
+ if (align < 8)
align = 8;
#ifdef HAVE_POSIX_MEMALIGN
retval = posix_memalign(p, align, size);
@@ -64,9 +67,55 @@ errcode_t ext2fs_get_memalign(unsigned long size,
return EXT2_ET_NO_MEMORY;
}
#else
-#error memalign or posix_memalign must be defined!
+#ifdef HAVE_VALLOC
+ if (align > sizeof(long long))
+ *p = valloc(size);
+ else
+#endif
+ *p = malloc(size);
+ if ((unsigned long) *p & (align - 1)) {
+ free(*p);
+ *p = 0;
+ }
+ if (*p == 0)
+ return EXT2_ET_NO_MEMORY;
#endif
#endif
return 0;
}
+#ifdef DEBUG
+static int isaligned(void *ptr, unsigned long align)
+{
+ return (((unsigned long) ptr & (align - 1)) == 0);
+}
+
+static errcode_t test_memalign(unsigned long align)
+{
+ void *ptr = 0;
+ errcode_t retval;
+
+ retval = ext2fs_get_memalign(32, align, &ptr);
+ if (!retval && !isaligned(ptr, align))
+ retval = EINVAL;
+ free(ptr);
+ printf("tst_memalign(%lu) is %s\n", align,
+ retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+int main(int argc, char **argv)
+{
+ int err = 0;
+
+ if (test_memalign(4))
+ err++;
+ if (test_memalign(32))
+ err++;
+ if (test_memalign(1024))
+ err++;
+ if (test_memalign(4096))
+ err++;
+ return err;
+}
+#endif
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 6c524ff2..573a8fa5 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -157,8 +157,8 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
(fs->blocksize / scan->inode_size - 1)) *
scan->inode_size / fs->blocksize;
}
- retval = ext2fs_get_memalign(scan->inode_buffer_blocks * fs->blocksize,
- fs->blocksize, &scan->inode_buffer);
+ retval = io_channel_alloc_buf(fs->io, scan->inode_buffer_blocks,
+ &scan->inode_buffer);
scan->done_group = 0;
scan->done_group_data = 0;
scan->bad_block_ptr = 0;
@@ -270,9 +270,9 @@ errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
* increasing order.
*/
static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
- blk_t *num_blocks)
+ blk64_t *num_blocks)
{
- blk_t blk = scan->current_block;
+ blk64_t blk = scan->current_block;
badblocks_list bb = scan->fs->badblocks;
/*
@@ -329,7 +329,7 @@ static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
*/
static errcode_t get_next_blocks(ext2_inode_scan scan)
{
- blk_t num_blocks;
+ blk64_t num_blocks;
errcode_t retval;
/*
@@ -533,7 +533,9 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
/* Check to see if user has an override function */
- if (fs->read_inode) {
+ if (fs->read_inode &&
+ ((bufsize == sizeof(struct ext2_inode)) ||
+ (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) {
retval = (fs->read_inode)(fs, ino, inode);
if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
return retval;
diff --git a/lib/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c
index 25df59ec..34e48592 100644
--- a/lib/ext2fs/io_manager.c
+++ b/lib/ext2fs/io_manager.c
@@ -111,3 +111,20 @@ errcode_t io_channel_discard(io_channel channel, unsigned long long block,
return EXT2_ET_UNIMPLEMENTED;
}
+
+errcode_t io_channel_alloc_buf(io_channel io, int count, void *ptr)
+{
+ size_t size;
+
+ if (count == 0)
+ size = io->block_size;
+ else if (count > 0)
+ size = io->block_size * count;
+ else
+ size = -count;
+
+ if (io->align)
+ return ext2fs_get_memalign(size, io->align, ptr);
+ else
+ return ext2fs_get_mem(size, ptr);
+}
diff --git a/lib/ext2fs/irel.h b/lib/ext2fs/irel.h
index 9a4958be..8aaa2d2b 100644
--- a/lib/ext2fs/irel.h
+++ b/lib/ext2fs/irel.h
@@ -10,7 +10,7 @@
*/
struct ext2_inode_reference {
- blk_t block;
+ blk64_t block;
__u16 offset;
};
diff --git a/lib/ext2fs/ismounted.c b/lib/ext2fs/ismounted.c
index 823eb3fe..6c6ecff0 100644
--- a/lib/ext2fs/ismounted.c
+++ b/lib/ext2fs/ismounted.c
@@ -21,6 +21,13 @@
#ifdef HAVE_LINUX_FD_H
#include <linux/fd.h>
#endif
+#ifdef HAVE_LINUX_LOOP_H
+#include <linux/loop.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_LINUX_MAJOR_H
+#include <linux/major.h>
+#endif /* HAVE_LINUX_MAJOR_H */
+#endif /* HAVE_LINUX_LOOP_H */
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
@@ -35,7 +42,37 @@
#include "ext2_fs.h"
#include "ext2fs.h"
-#ifdef HAVE_MNTENT_H
+/*
+ * Check to see if a regular file is mounted.
+ * If /etc/mtab/ is a symlink of /proc/mounts, you will need the following check
+ * because the name in /proc/mounts is a loopback device not a regular file.
+ */
+static int check_loop_mounted(const char *mnt_fsname, dev_t mnt_rdev,
+ dev_t file_dev, ino_t file_ino)
+{
+#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
+ struct loop_info64 loopinfo;
+ int loop_fd, ret;
+
+ if (major(mnt_rdev) == LOOP_MAJOR) {
+ loop_fd = open(mnt_fsname, O_RDONLY);
+ if (loop_fd < 0)
+ return -1;
+
+ ret = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
+ close(loop_fd);
+ if (ret < 0)
+ return -1;
+
+ if (file_dev == loopinfo.lo_device &&
+ file_ino == loopinfo.lo_inode)
+ return 1;
+ }
+#endif /* defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) */
+ return 0;
+}
+
+#ifdef HAVE_SETMNTENT
/*
* Helper function which checks a file in /etc/mtab format to see if a
* filesystem is mounted. Returns an error if the file doesn't exist
@@ -53,8 +90,15 @@ static errcode_t check_mntent_file(const char *mtab_file, const char *file,
int fd;
*mount_flags = 0;
- if ((f = setmntent (mtab_file, "r")) == NULL)
- return (errno == ENOENT ? EXT2_NO_MTAB_FILE : errno);
+ if ((f = setmntent (mtab_file, "r")) == NULL) {
+ if (errno == ENOENT) {
+ if (getenv("EXT2FS_NO_MTAB_OK"))
+ return 0;
+ else
+ return EXT2_ET_NO_MTAB_FILE;
+ }
+ return errno;
+ }
if (stat(file, &st_buf) == 0) {
if (S_ISBLK(st_buf.st_mode)) {
#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
@@ -75,6 +119,10 @@ static errcode_t check_mntent_file(const char *mtab_file, const char *file,
#ifndef __GNU__
if (file_rdev && (file_rdev == st_buf.st_rdev))
break;
+ if (check_loop_mounted(mnt->mnt_fsname,
+ st_buf.st_rdev, file_dev,
+ file_ino) == 1)
+ break;
#endif /* __GNU__ */
} else {
if (file_dev && ((file_dev == st_buf.st_dev) &&
@@ -231,7 +279,7 @@ static errcode_t check_getmntinfo(const char *file, int *mount_flags,
return 0;
}
#endif /* HAVE_GETMNTINFO */
-#endif /* HAVE_MNTENT_H */
+#endif /* HAVE_SETMNTENT */
/*
* Check to see if we're dealing with the swap device.
@@ -302,15 +350,13 @@ leave:
errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
char *mtpt, int mtlen)
{
- struct stat st_buf;
errcode_t retval = 0;
- int fd;
if (is_swap_device(device)) {
*mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
strncpy(mtpt, "<swap>", mtlen);
} else {
-#ifdef HAVE_MNTENT_H
+#ifdef HAVE_SETMNTENT
retval = check_mntent(device, mount_flags, mtpt, mtlen);
#else
#ifdef HAVE_GETMNTINFO
@@ -321,21 +367,24 @@ errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
#endif
*mount_flags = 0;
#endif /* HAVE_GETMNTINFO */
-#endif /* HAVE_MNTENT_H */
+#endif /* HAVE_SETMNTENT */
}
if (retval)
return retval;
#ifdef __linux__ /* This only works on Linux 2.6+ systems */
- if ((stat(device, &st_buf) != 0) ||
- !S_ISBLK(st_buf.st_mode))
- return 0;
- fd = open(device, O_RDONLY | O_EXCL);
- if (fd < 0) {
- if (errno == EBUSY)
- *mount_flags |= EXT2_MF_BUSY;
- } else
- close(fd);
+ {
+ struct stat st_buf;
+
+ if (stat(device, &st_buf) == 0 && S_ISBLK(st_buf.st_mode)) {
+ int fd = open(device, O_RDONLY | O_EXCL);
+
+ if (fd >= 0)
+ close(fd);
+ else if (errno == EBUSY)
+ *mount_flags |= EXT2_MF_BUSY;
+ }
+ }
#endif
return 0;
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index 066c031e..059bf8fa 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -214,13 +214,13 @@ typedef struct journal_superblock_s
#define JFS_HAS_COMPAT_FEATURE(j,mask) \
((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+ ((j)->j_superblock->s_feature_compat & ext2fs_cpu_to_be32((mask))))
#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \
((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+ ((j)->j_superblock->s_feature_ro_compat & ext2fs_cpu_to_be32((mask))))
#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \
((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+ ((j)->j_superblock->s_feature_incompat & ext2fs_cpu_to_be32((mask))))
#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001
diff --git a/lib/ext2fs/kernel-list.h b/lib/ext2fs/kernel-list.h
index e07d06b6..01f4f6b9 100644
--- a/lib/ext2fs/kernel-list.h
+++ b/lib/ext2fs/kernel-list.h
@@ -17,9 +17,6 @@ struct list_head {
#define LIST_HEAD_INIT(name) { &(name), &(name) }
-#define LIST_HEAD(name) \
- struct list_head name = { &name, &name }
-
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 2d03b573..bf3c859a 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -42,6 +42,9 @@ static int link_proc(struct ext2_dir_entry *dirent,
unsigned int rec_len, min_rec_len, curr_rec_len;
int ret = 0;
+ if (ls->done)
+ return DIRENT_ABORT;
+
rec_len = EXT2_DIR_REC_LEN(ls->namelen);
ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
diff --git a/lib/ext2fs/llseek.c b/lib/ext2fs/llseek.c
index b0576e40..c3a98a2c 100644
--- a/lib/ext2fs/llseek.c
+++ b/lib/ext2fs/llseek.c
@@ -90,17 +90,14 @@ static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin)
ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
{
+#if SIZEOF_OFF_T >= SIZEOF_LONG_LONG
+ return lseek (fd, offset, origin);
+#else
ext2_loff_t result;
static int do_compat = 0;
- if ((sizeof(off_t) >= sizeof(ext2_loff_t)) ||
- (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1))))
- return lseek(fd, (off_t) offset, origin);
-
- if (do_compat) {
- errno = EINVAL;
- return -1;
- }
+ if (do_compat)
+ goto fallback;
result = my_llseek (fd, offset, origin);
if (result == -1 && errno == ENOSYS) {
@@ -109,9 +106,14 @@ ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
* which does not support the llseek system call
*/
do_compat++;
+ fallback:
+ if (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))
+ return lseek(fd, (off_t) offset, origin);
errno = EINVAL;
+ return -1;
}
return result;
+#endif
}
#else /* !linux */
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index 838d751c..884d9c07 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -250,6 +250,7 @@ static int mkjournal_proc(ext2_filsys fs,
es->err = retval;
return BLOCK_ABORT;
}
+ ext2fs_block_alloc_stats2(fs, new_blk, +1);
es->newblocks++;
}
if (blockcnt >= 0)
@@ -285,7 +286,6 @@ static int mkjournal_proc(ext2_filsys fs,
return BLOCK_ABORT;
}
*blocknr = es->goal = new_blk;
- ext2fs_block_alloc_stats2(fs, new_blk, +1);
if (es->num_blocks == 0)
return (BLOCK_CHANGED | BLOCK_ABORT);
@@ -295,13 +295,43 @@ static int mkjournal_proc(ext2_filsys fs,
}
/*
+ * Calculate the initial goal block to be roughly at the middle of the
+ * filesystem. Pick a group that has the largest number of free
+ * blocks.
+ */
+static blk64_t get_midpoint_journal_block(ext2_filsys fs)
+{
+ dgrp_t group, start, end, i, log_flex;
+
+ group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) -
+ fs->super->s_first_data_block) / 2);
+ log_flex = 1 << fs->super->s_log_groups_per_flex;
+ if (fs->super->s_log_groups_per_flex && (group > log_flex)) {
+ group = group & ~(log_flex - 1);
+ while ((group < fs->group_desc_count) &&
+ ext2fs_bg_free_blocks_count(fs, group) == 0)
+ group++;
+ if (group == fs->group_desc_count)
+ group = 0;
+ start = group;
+ } else
+ start = (group > 0) ? group-1 : group;
+ end = ((group+1) < fs->group_desc_count) ? group+1 : group;
+ group = start;
+ for (i = start + 1; i <= end; i++)
+ if (ext2fs_bg_free_blocks_count(fs, i) >
+ ext2fs_bg_free_blocks_count(fs, group))
+ group = i;
+ return ext2fs_group_first_block2(fs, group);
+}
+
+/*
* This function creates a journal using direct I/O routines.
*/
static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
- blk_t num_blocks, int flags)
+ blk_t num_blocks, blk64_t goal, int flags)
{
char *buf;
- dgrp_t group, start, end, i, log_flex;
errcode_t retval;
struct ext2_inode inode;
unsigned long long inode_size;
@@ -312,13 +342,15 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
return retval;
if ((retval = ext2fs_read_bitmaps(fs)))
- return retval;
+ goto out2;
if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
- return retval;
+ goto out2;
- if (inode.i_blocks > 0)
- return EEXIST;
+ if (inode.i_blocks > 0) {
+ retval = EEXIST;
+ goto out2;
+ }
es.num_blocks = num_blocks;
es.newblocks = 0;
@@ -326,41 +358,14 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
es.err = 0;
es.flags = flags;
es.zero_count = 0;
+ es.goal = (goal != ~0ULL) ? goal : get_midpoint_journal_block(fs);
if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
inode.i_flags |= EXT4_EXTENTS_FL;
if ((retval = ext2fs_write_inode(fs, journal_ino, &inode)))
- return retval;
+ goto out2;
}
- /*
- * Set the initial goal block to be roughly at the middle of
- * the filesystem. Pick a group that has the largest number
- * of free blocks.
- */
- group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) -
- fs->super->s_first_data_block) / 2);
- log_flex = 1 << fs->super->s_log_groups_per_flex;
- if (fs->super->s_log_groups_per_flex && (group > log_flex)) {
- group = group & ~(log_flex - 1);
- while ((group < fs->group_desc_count) &&
- ext2fs_bg_free_blocks_count(fs, group) == 0)
- group++;
- if (group == fs->group_desc_count)
- group = 0;
- start = group;
- } else
- start = (group > 0) ? group-1 : group;
- end = ((group+1) < fs->group_desc_count) ? group+1 : group;
- group = start;
- for (i=start+1; i <= end; i++)
- if (ext2fs_bg_free_blocks_count(fs, i) >
- ext2fs_bg_free_blocks_count(fs, group))
- group = i;
-
- es.goal = (fs->super->s_blocks_per_group * group) +
- fs->super->s_first_data_block;
-
retval = ext2fs_block_iterate3(fs, journal_ino, BLOCK_FLAG_APPEND,
0, mkjournal_proc, &es);
if (es.err) {
@@ -380,7 +385,7 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
inode_size = (unsigned long long)fs->blocksize * num_blocks;
inode.i_size = inode_size & 0xFFFFFFFF;
inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
- if (inode.i_size_high)
+ if (ext2fs_needs_large_file_feature(inode_size))
fs->super->s_feature_ro_compat |=
EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
ext2fs_iblk_add_blocks(fs, &inode, es.newblocks);
@@ -400,6 +405,7 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
errout:
ext2fs_zero_blocks2(0, 0, 0, 0, 0);
+out2:
ext2fs_free_mem(&buf);
return retval;
}
@@ -490,20 +496,27 @@ errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
* POSIX routines if the filesystem is mounted, or using direct I/O
* functions if it is not.
*/
-errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
+errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+ blk64_t goal, int flags)
{
errcode_t retval;
ext2_ino_t journal_ino;
struct stat st;
char jfile[1024];
- int mount_flags, f;
+ int mount_flags;
int fd = -1;
- if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
- jfile, sizeof(jfile)-10)))
+ if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK)
+ mount_flags = 0;
+ else if ((retval = ext2fs_check_mount_point(fs->device_name,
+ &mount_flags,
+ jfile, sizeof(jfile)-10)))
return retval;
if (mount_flags & EXT2_MF_MOUNTED) {
+#if HAVE_EXT2_IOCTLS
+ int f = 0;
+#endif
strcat(jfile, "/.journal");
/*
@@ -516,9 +529,10 @@ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
#if HAVE_EXT2_IOCTLS
fd = open(jfile, O_RDONLY);
if (fd >= 0) {
- f = 0;
- ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+ retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
close(fd);
+ if (retval)
+ return retval;
}
#endif
#endif
@@ -574,7 +588,7 @@ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
}
journal_ino = EXT2_JOURNAL_INO;
if ((retval = write_journal_inode(fs, journal_ino,
- num_blocks, flags)))
+ num_blocks, goal, flags)))
return retval;
}
@@ -587,11 +601,17 @@ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
ext2fs_mark_super_dirty(fs);
return 0;
errout:
- if (fd > 0)
+ if (fd >= 0)
close(fd);
return retval;
}
+errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
+{
+ return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags);
+}
+
+
#ifdef DEBUG
main(int argc, char **argv)
{
@@ -612,7 +632,7 @@ main(int argc, char **argv)
exit(1);
}
- retval = ext2fs_add_journal_inode(fs, 1024);
+ retval = ext2fs_add_journal_inode(fs, 1024, 0);
if (retval) {
com_err(argv[0], retval, "while adding journal to %s",
device_name);
diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
index 49a11da9..e4c7dccd 100644
--- a/lib/ext2fs/mmp.c
+++ b/lib/ext2fs/mmp.c
@@ -4,8 +4,8 @@
* Copyright (C) 2011 Whamcloud, Inc.
*
* %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
* %End-Header%
*/
@@ -27,20 +27,6 @@
#include "ext2fs/ext2_fs.h"
#include "ext2fs/ext2fs.h"
-static int mmp_pagesize(void)
-{
-#ifdef _SC_PAGESIZE
- int sysval = sysconf(_SC_PAGESIZE);
- if (sysval > 0)
- return sysval;
-#endif /* _SC_PAGESIZE */
-#ifdef HAVE_GETPAGESIZE
- return getpagesize();
-#else
- return 4096;
-#endif
-}
-
#ifndef O_DIRECT
#define O_DIRECT 0
#endif
@@ -51,23 +37,9 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
errcode_t retval = 0;
if ((mmp_blk <= fs->super->s_first_data_block) ||
- (mmp_blk >= fs->super->s_blocks_count))
+ (mmp_blk >= ext2fs_blocks_count(fs->super)))
return EXT2_ET_MMP_BAD_BLOCK;
- if (fs->mmp_cmp == NULL) {
- /* O_DIRECT in linux 2.4: page aligned
- * O_DIRECT in linux 2.6: sector aligned
- * A filesystem cannot be created with blocksize < sector size,
- * or with blocksize > page_size. */
- int bufsize = fs->blocksize;
-
- if (bufsize < mmp_pagesize())
- bufsize = mmp_pagesize();
- retval = ext2fs_get_memalign(bufsize, bufsize, &fs->mmp_cmp);
- if (retval)
- return retval;
- }
-
/* ext2fs_open() reserves fd0,1,2 to avoid stdio collision, so checking
* mmp_fd <= 0 is OK to validate that the fd is valid. This opens its
* own fd to read the MMP block to ensure that it is using O_DIRECT,
@@ -81,7 +53,17 @@ errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
}
}
- if (ext2fs_llseek(fs->mmp_fd, mmp_blk * fs->blocksize, SEEK_SET) !=
+ if (fs->mmp_cmp == NULL) {
+ int align = ext2fs_get_dio_alignment(fs->mmp_fd);
+
+ retval = ext2fs_get_memalign(fs->blocksize, align,
+ &fs->mmp_cmp);
+ if (retval)
+ return retval;
+ }
+
+ if ((blk64_t) ext2fs_llseek(fs->mmp_fd, mmp_blk * fs->blocksize,
+ SEEK_SET) !=
mmp_blk * fs->blocksize) {
retval = EXT2_ET_LLSEEK_FAILED;
goto out;
@@ -145,7 +127,7 @@ errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
#define rand() random()
#endif
-unsigned ext2fs_mmp_new_seq()
+unsigned ext2fs_mmp_new_seq(void)
{
unsigned new_seq;
struct timeval tv;
diff --git a/lib/ext2fs/namei.c b/lib/ext2fs/namei.c
index efcc02b7..307aecc8 100644
--- a/lib/ext2fs/namei.c
+++ b/lib/ext2fs/namei.c
@@ -34,6 +34,7 @@ static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
char *buffer = 0;
errcode_t retval;
struct ext2_inode ei;
+ blk64_t blk;
#ifdef NAMEI_DEBUG
printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n",
@@ -49,12 +50,16 @@ static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
if (link_count++ >= EXT2FS_MAX_NESTED_LINKS)
return EXT2_ET_SYMLINK_LOOP;
- /* FIXME-64: Actually, this is FIXME EXTENTS */
if (ext2fs_inode_data_blocks(fs,&ei)) {
+ retval = ext2fs_bmap2(fs, inode, &ei, NULL, 0, 0, NULL, &blk);
+ if (retval)
+ return retval;
+
retval = ext2fs_get_mem(fs->blocksize, &buffer);
if (retval)
return retval;
- retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer);
+
+ retval = io_channel_read_blk64(fs->io, blk, 1, buffer);
if (retval) {
ext2fs_free_mem(&buffer);
return retval;
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index b0a1e476..3e2c0dbe 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -44,8 +44,10 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
dir = (struct ext2_dir_entry *) buf;
retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
- if (retval)
+ if (retval) {
+ ext2fs_free_mem(&buf);
return retval;
+ }
if (dir_ino) {
if (fs->super->s_feature_incompat &
@@ -65,8 +67,10 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
*/
dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
retval = ext2fs_set_rec_len(fs, rec_len, dir);
- if (retval)
+ if (retval) {
+ ext2fs_free_mem(&buf);
return retval;
+ }
dir->inode = parent_ino;
dir->name_len = 2 | filetype;
dir->name[0] = '.';
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 220d954b..a1a35176 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -37,17 +37,24 @@ blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block,
dgrp_t i)
{
int bg;
- int has_super = 0;
+ int has_super = 0, group_zero_adjust = 0;
blk64_t ret_blk;
+ /*
+ * On a bigalloc FS with 1K blocks, block 0 is reserved for non-ext4
+ * stuff, so adjust for that if we're being asked for group 0.
+ */
+ if (i == 0 && fs->blocksize == 1024 && EXT2FS_CLUSTER_RATIO(fs) > 1)
+ group_zero_adjust = 1;
+
if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
(i < fs->super->s_first_meta_bg))
- return (group_block + i + 1);
+ return group_block + i + 1 + group_zero_adjust;
bg = EXT2_DESC_PER_BLOCK(fs->super) * i;
if (ext2fs_bg_has_super(fs, bg))
has_super = 1;
- ret_blk = ext2fs_group_first_block2(fs, bg) + has_super;
+ ret_blk = ext2fs_group_first_block2(fs, bg);
/*
* If group_block is not the normal value, we're trying to use
* the backup group descriptors and superblock --- so use the
@@ -57,10 +64,21 @@ blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block,
* have the infrastructure in place to do that.
*/
if (group_block != fs->super->s_first_data_block &&
- ((ret_blk + fs->super->s_blocks_per_group) <
- ext2fs_blocks_count(fs->super)))
+ ((ret_blk + has_super + fs->super->s_blocks_per_group) <
+ ext2fs_blocks_count(fs->super))) {
ret_blk += fs->super->s_blocks_per_group;
- return ret_blk;
+
+ /*
+ * If we're going to jump forward a block group, make sure
+ * that we adjust has_super to account for the next group's
+ * backup superblock (or lack thereof).
+ */
+ if (ext2fs_bg_has_super(fs, bg + 1))
+ has_super = 1;
+ else
+ has_super = 0;
+ }
+ return ret_blk + has_super + group_zero_adjust;
}
blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
@@ -99,10 +117,12 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
errcode_t retval;
unsigned long i, first_meta_bg;
__u32 features;
- unsigned int groups_per_block, blocks_per_group, io_flags;
+ unsigned int blocks_per_group, io_flags;
blk64_t group_block, blk;
char *dest, *cp;
+ int group_zero_adjust = 0;
#ifdef WORDS_BIGENDIAN
+ unsigned int groups_per_block;
struct ext2_group_desc *gdp;
int j;
#endif
@@ -144,7 +164,7 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
goto cleanup;
fs->image_io = fs->io;
fs->io->app_data = fs;
- retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super);
+ retval = io_channel_alloc_buf(fs->io, -SUPERBLOCK_SIZE, &fs->super);
if (retval)
goto cleanup;
if (flags & EXT2_FLAG_IMAGE_FILE) {
@@ -252,6 +272,18 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
retval = EXT2_ET_CORRUPT_SUPERBLOCK;
goto cleanup;
}
+
+ /*
+ * bigalloc requires cluster-aware bitfield operations, which at the
+ * moment means we need EXT2_FLAG_64BITS.
+ */
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
+ !(flags & EXT2_FLAG_64BITS)) {
+ retval = EXT2_ET_CANT_USE_LEGACY_BITMAPS;
+ goto cleanup;
+ }
+
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
(fs->super->s_log_block_size != fs->super->s_log_cluster_size)) {
@@ -329,16 +361,30 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
goto cleanup;
if (!group_block)
group_block = fs->super->s_first_data_block;
+ /*
+ * On a FS with a 1K blocksize, block 0 is reserved for bootloaders
+ * so we must increment block numbers to any group 0 items.
+ *
+ * However, we cannot touch group_block directly because in the meta_bg
+ * case, the ext2fs_descriptor_block_loc2() function will interpret
+ * group_block != s_first_data_block to mean that we want to access the
+ * backup group descriptors. This is not what we want if the caller
+ * set superblock == 0 (i.e. auto-detect the superblock), which is
+ * what's going on here.
+ */
if (group_block == 0 && fs->blocksize == 1024)
- group_block = 1; /* Deal with 1024 blocksize && bigalloc */
+ group_zero_adjust = 1;
dest = (char *) fs->group_desc;
+#ifdef WORDS_BIGENDIAN
groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
+#endif
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
first_meta_bg = fs->super->s_first_meta_bg;
else
first_meta_bg = fs->desc_blocks;
if (first_meta_bg) {
- retval = io_channel_read_blk(fs->io, group_block+1,
+ retval = io_channel_read_blk(fs->io, group_block +
+ group_zero_adjust + 1,
first_meta_bg, dest);
if (retval)
goto cleanup;
@@ -388,9 +434,6 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
ext2fs_mark_super_dirty(fs);
}
- fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
- *ret_fs = fs;
-
if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) &&
!(flags & EXT2_FLAG_SKIP_MMP) &&
(flags & (EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE))) {
@@ -402,12 +445,16 @@ errcode_t ext2fs_open2(const char *name, const char *io_options,
}
}
+ fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
+ *ret_fs = fs;
+
return 0;
cleanup:
- if (flags & EXT2_FLAG_NOFREE_ON_ERROR)
- *ret_fs = fs;
- else
+ if (!(flags & EXT2_FLAG_NOFREE_ON_ERROR)) {
ext2fs_free(fs);
+ fs = NULL;
+ }
+ *ret_fs = fs;
return retval;
}
@@ -436,8 +483,20 @@ errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
{
+ errcode_t err;
+
if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
return EXT2_ET_NOT_IMAGE_FILE;
+ err = io_channel_set_blksize(new_io, fs->blocksize);
+ if (err)
+ return err;
+ if ((new_io == fs->image_io) || (new_io == fs->io))
+ return 0;
+ if ((fs->image_io != fs->io) &&
+ fs->image_io)
+ io_channel_close(fs->image_io);
+ if (fs->io)
+ io_channel_close(fs->io);
fs->io = fs->image_io = new_io;
fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
diff --git a/lib/ext2fs/progress.c b/lib/ext2fs/progress.c
index 37d15096..8c9a6f1d 100644
--- a/lib/ext2fs/progress.c
+++ b/lib/ext2fs/progress.c
@@ -14,7 +14,10 @@
#include "ext2fs.h"
#include "ext2fsP.h"
+#include <time.h>
+
static char spaces[80], backspaces[80];
+static time_t last_update;
static int int_log10(unsigned int arg)
{
@@ -42,11 +45,11 @@ void ext2fs_numeric_progress_init(ext2_filsys fs,
spaces[sizeof(spaces)-1] = 0;
memset(backspaces, '\b', sizeof(backspaces)-1);
backspaces[sizeof(backspaces)-1] = 0;
- progress->skip_progress = 0;
+
+ memset(progress, 0, sizeof(*progress));
if (getenv("E2FSPROGS_SKIP_PROGRESS"))
progress->skip_progress++;
- memset(progress, 0, sizeof(*progress));
/*
* Figure out how many digits we need
@@ -58,16 +61,23 @@ void ext2fs_numeric_progress_init(ext2_filsys fs,
fputs(label, stdout);
fflush(stdout);
}
+ last_update = 0;
}
void ext2fs_numeric_progress_update(ext2_filsys fs,
struct ext2fs_numeric_progress_struct * progress,
__u64 val)
{
+ time_t now;
+
if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS))
return;
if (progress->skip_progress)
return;
+ now = time(0);
+ if (now == last_update)
+ return;
+ last_update = now;
printf("%*llu/%*llu", progress->log_max, val,
progress->log_max, progress->max);
diff --git a/lib/ext2fs/punch.c b/lib/ext2fs/punch.c
index b53653a0..a3d020ec 100644
--- a/lib/ext2fs/punch.c
+++ b/lib/ext2fs/punch.c
@@ -50,17 +50,18 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
blk_t start, blk_t count, int max)
{
errcode_t retval;
- blk_t b, offset;
- int i, incr;
+ blk_t b;
+ int i;
+ blk64_t offset, incr;
int freed = 0;
#ifdef PUNCH_DEBUG
printf("Entering ind_punch, level %d, start %u, count %u, "
"max %d\n", level, start, count, max);
#endif
- incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
+ incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
for (i=0, offset=0; i < max; i++, p++, offset += incr) {
- if (offset > count)
+ if (offset >= start + count)
break;
if (*p == 0 || (offset+incr) <= start)
continue;
@@ -87,7 +88,7 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
continue;
}
#ifdef PUNCH_DEBUG
- printf("Freeing block %u (offset %d)\n", b, offset);
+ printf("Freeing block %u (offset %llu)\n", b, offset);
#endif
ext2fs_block_alloc_stats(fs, b, -1);
*p = 0;
@@ -108,7 +109,7 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
int num = EXT2_NDIR_BLOCKS;
blk_t *bp = inode->i_block;
blk_t addr_per_block;
- blk_t max = EXT2_NDIR_BLOCKS;
+ blk64_t max = EXT2_NDIR_BLOCKS;
if (!block_buf) {
retval = ext2fs_get_array(3, fs->blocksize, &buf);
@@ -119,10 +120,10 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
addr_per_block = (blk_t) fs->blocksize >> 2;
- for (level=0; level < 4; level++, max *= addr_per_block) {
+ for (level = 0; level < 4; level++, max *= (blk64_t)addr_per_block) {
#ifdef PUNCH_DEBUG
printf("Main loop level %d, start %u count %u "
- "max %d num %d\n", level, start, count, max, num);
+ "max %llu num %d\n", level, start, count, max, num);
#endif
if (start < max) {
retval = ind_punch(fs, inode, block_buf, bp, level,
@@ -176,6 +177,75 @@ static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
#define dbg_printf(f, a...) do { } while (0)
#endif
+/* Free a range of blocks, respecting cluster boundaries */
+static errcode_t punch_extent_blocks(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t lfree_start, blk64_t free_start,
+ __u32 free_count, int *freed)
+{
+ blk64_t pblk;
+ int freed_now = 0;
+ __u32 cluster_freed;
+ errcode_t retval = 0;
+
+ /* No bigalloc? Just free each block. */
+ if (EXT2FS_CLUSTER_RATIO(fs) == 1) {
+ *freed += free_count;
+ while (free_count-- > 0)
+ ext2fs_block_alloc_stats2(fs, free_start++, -1);
+ return retval;
+ }
+
+ /*
+ * Try to free up to the next cluster boundary. We assume that all
+ * blocks in a logical cluster map to blocks from the same physical
+ * cluster, and that the offsets within the [pl]clusters match.
+ */
+ if (free_start & EXT2FS_CLUSTER_MASK(fs)) {
+ retval = ext2fs_map_cluster_block(fs, ino, inode,
+ lfree_start, &pblk);
+ if (retval)
+ goto errout;
+ if (!pblk) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ }
+ cluster_freed = EXT2FS_CLUSTER_RATIO(fs) -
+ (free_start & EXT2FS_CLUSTER_MASK(fs));
+ if (cluster_freed > free_count)
+ cluster_freed = free_count;
+ free_count -= cluster_freed;
+ free_start += cluster_freed;
+ lfree_start += cluster_freed;
+ }
+
+ /* Free whole clusters from the middle of the range. */
+ while (free_count > 0 && free_count >= EXT2FS_CLUSTER_RATIO(fs)) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ cluster_freed = EXT2FS_CLUSTER_RATIO(fs);
+ free_count -= cluster_freed;
+ free_start += cluster_freed;
+ lfree_start += cluster_freed;
+ }
+
+ /* Try to free the last cluster. */
+ if (free_count > 0) {
+ retval = ext2fs_map_cluster_block(fs, ino, inode,
+ lfree_start, &pblk);
+ if (retval)
+ goto errout;
+ if (!pblk) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ }
+ }
+
+errout:
+ *freed += freed_now;
+ return retval;
+}
+
static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
blk64_t start, blk64_t end)
@@ -183,18 +253,34 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
ext2_extent_handle_t handle = 0;
struct ext2fs_extent extent;
errcode_t retval;
- blk64_t free_start, next;
+ blk64_t free_start, next, lfree_start;
__u32 free_count, newlen;
int freed = 0;
+ int op;
retval = ext2fs_extent_open2(fs, ino, inode, &handle);
if (retval)
return retval;
+ /*
+ * Find the extent closest to the start of the punch range. We don't
+ * check the return value because _goto() sets the current node to the
+ * next-lowest extent if 'start' is in a hole, and doesn't set a
+ * current node if there was a real error reading the extent tree.
+ * In that case, _get() will error out.
+ *
+ * Note: If _get() returns 'no current node', that simply means that
+ * there aren't any blocks mapped past this point in the file, so we're
+ * done.
+ */
ext2fs_extent_goto(handle, start);
retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
- if (retval)
+ if (retval == EXT2_ET_NO_CURRENT_NODE) {
+ retval = 0;
+ goto errout;
+ } else if (retval)
goto errout;
while (1) {
+ op = EXT2_EXTENT_NEXT_LEAF;
dbg_print_extent("main loop", &extent);
next = extent.e_lblk + extent.e_len;
dbg_printf("start %llu, end %llu, next %llu\n",
@@ -202,12 +288,17 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
(unsigned long long) end,
(unsigned long long) next);
if (start <= extent.e_lblk) {
+ /*
+ * Have we iterated past the end of the punch region?
+ * If so, we can stop.
+ */
if (end < extent.e_lblk)
- goto next_extent;
+ break;
dbg_printf("Case #%d\n", 1);
/* Start of deleted region before extent;
adjust beginning of extent */
free_start = extent.e_pblk;
+ lfree_start = extent.e_lblk;
if (next > end)
free_count = end - extent.e_lblk + 1;
else
@@ -216,13 +307,19 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
extent.e_lblk += free_count;
extent.e_pblk += free_count;
} else if (end >= next-1) {
+ /*
+ * Is the punch region beyond this extent? This can
+ * happen if start is already inside a hole. Try to
+ * advance to the next extent if this is the case.
+ */
if (start >= next)
- break;
+ goto next_extent;
/* End of deleted region after extent;
adjust end of extent */
dbg_printf("Case #%d\n", 2);
newlen = start - extent.e_lblk;
free_start = extent.e_pblk + newlen;
+ lfree_start = extent.e_lblk + newlen;
free_count = extent.e_len - newlen;
extent.e_len = newlen;
} else {
@@ -238,6 +335,7 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
extent.e_len = start - extent.e_lblk;
free_start = extent.e_pblk + extent.e_len;
+ lfree_start = extent.e_lblk + extent.e_len;
free_count = end - start + 1;
dbg_print_extent("inserting", &newex);
@@ -255,22 +353,65 @@ static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
if (extent.e_len) {
dbg_print_extent("replacing", &extent);
retval = ext2fs_extent_replace(handle, 0, &extent);
+ if (retval)
+ goto errout;
+ retval = ext2fs_extent_fix_parents(handle);
} else {
+ struct ext2fs_extent newex;
+ blk64_t old_lblk, next_lblk;
dbg_printf("deleting current extent%s\n", "");
+
+ /*
+ * Save the location of the next leaf, then slip
+ * back to the current extent.
+ */
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &newex);
+ if (retval)
+ goto errout;
+ old_lblk = newex.e_lblk;
+
+ retval = ext2fs_extent_get(handle,
+ EXT2_EXTENT_NEXT_LEAF,
+ &newex);
+ if (retval == EXT2_ET_EXTENT_NO_NEXT)
+ next_lblk = old_lblk;
+ else if (retval)
+ goto errout;
+ else
+ next_lblk = newex.e_lblk;
+
+ retval = ext2fs_extent_goto(handle, old_lblk);
+ if (retval)
+ goto errout;
+
+ /* Now delete the extent. */
retval = ext2fs_extent_delete(handle, 0);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval && retval != EXT2_ET_NO_CURRENT_NODE)
+ goto errout;
+ retval = 0;
+
+ /* Jump forward to the next extent. */
+ ext2fs_extent_goto(handle, next_lblk);
+ op = EXT2_EXTENT_CURRENT;
}
if (retval)
goto errout;
dbg_printf("Free start %llu, free count = %u\n",
free_start, free_count);
- while (free_count-- > 0) {
- ext2fs_block_alloc_stats(fs, free_start++, -1);
- freed++;
- }
+ retval = punch_extent_blocks(fs, ino, inode, lfree_start,
+ free_start, free_count, &freed);
+ if (retval)
+ goto errout;
next_extent:
- retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF,
+ retval = ext2fs_extent_get(handle, op,
&extent);
- if (retval == EXT2_ET_EXTENT_NO_NEXT)
+ if (retval == EXT2_ET_EXTENT_NO_NEXT ||
+ retval == EXT2_ET_NO_CURRENT_NODE)
break;
if (retval)
goto errout;
@@ -286,10 +427,10 @@ errout:
* Deallocate all logical blocks starting at start to end, inclusive.
* If end is ~0, then this is effectively truncate.
*/
-extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- char *block_buf, blk64_t start,
- blk64_t end)
+errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ char *block_buf, blk64_t start,
+ blk64_t end)
{
errcode_t retval;
struct ext2_inode inode_buf;
@@ -297,9 +438,6 @@ extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
if (start > end)
return EINVAL;
- if (start == end)
- return 0;
-
/* Read inode structure if necessary */
if (!inode) {
retval = ext2fs_read_inode(fs, ino, &inode_buf);
@@ -314,7 +452,9 @@ extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
if (start > ~0U)
return 0;
- count = ((end - start) < ~0U) ? (end - start) : ~0U;
+ if (end > ~0U)
+ end = ~0U;
+ count = ((end - start + 1) < ~0U) ? (end - start + 1) : ~0U;
retval = ext2fs_punch_ind(fs, inode, block_buf,
(blk_t) start, count);
}
diff --git a/lib/ext2fs/qcow2.c b/lib/ext2fs/qcow2.c
index b0a02783..c7cdbee8 100644
--- a/lib/ext2fs/qcow2.c
+++ b/lib/ext2fs/qcow2.c
@@ -60,8 +60,10 @@ struct ext2_qcow2_hdr *qcow2_read_header(int fd)
return NULL;
memset(buffer, 0, sizeof(struct ext2_qcow2_hdr));
- if (ext2fs_llseek(fd, 0, SEEK_SET < 0))
+ if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) {
+ ext2fs_free_mem(&buffer);
return NULL;
+ }
size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr));
if (size != sizeof(struct ext2_qcow2_hdr)) {
@@ -91,8 +93,10 @@ static int qcow2_read_l1_table(struct ext2_qcow2_image *img)
if (ret)
return ret;
- if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0)
+ if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) {
+ ext2fs_free_mem(&table);
return errno;
+ }
size = read(fd, table, l1_size);
if (size != l1_size) {
@@ -155,7 +159,7 @@ int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
errcode_t ret = 0;
unsigned int l1_index, l2_index;
ext2_off64_t offset;
- blk64_t *l1_table, *l2_table;
+ blk64_t *l1_table, *l2_table = NULL;
void *copy_buf = NULL;
size_t size;
@@ -231,13 +235,17 @@ int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
}
/* Resize the output image to the filesystem size */
- if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0)
- return errno;
+ if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) {
+ ret = errno;
+ goto out;
+ }
((char *)copy_buf)[0] = 0;
size = write(raw_fd, copy_buf, 1);
- if (size != 1)
- return errno;
+ if (size != 1) {
+ ret = errno;
+ goto out;
+ }
out:
if (copy_buf)
diff --git a/lib/ext2fs/rbtree.c b/lib/ext2fs/rbtree.c
index 7467e10e..94393030 100644
--- a/lib/ext2fs/rbtree.c
+++ b/lib/ext2fs/rbtree.c
@@ -375,7 +375,7 @@ struct rb_node *ext2fs_rb_last(const struct rb_root *root)
return n;
}
-struct rb_node *ext2fs_rb_next(const struct rb_node *node)
+struct rb_node *ext2fs_rb_next(struct rb_node *node)
{
struct rb_node *parent;
@@ -403,7 +403,7 @@ struct rb_node *ext2fs_rb_next(const struct rb_node *node)
return parent;
}
-struct rb_node *ext2fs_rb_prev(const struct rb_node *node)
+struct rb_node *ext2fs_rb_prev(struct rb_node *node)
{
struct rb_node *parent;
diff --git a/lib/ext2fs/rbtree.h b/lib/ext2fs/rbtree.h
index 972297bf..3b0b0784 100644
--- a/lib/ext2fs/rbtree.h
+++ b/lib/ext2fs/rbtree.h
@@ -104,7 +104,7 @@ static inline struct page * rb_insert_page_cache(struct inode * inode,
#endif
#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct rb_node
@@ -158,8 +158,8 @@ extern void ext2fs_rb_augment_erase_end(struct rb_node *node,
rb_augment_f func, void *data);
/* Find logical next and previous nodes in a tree */
-extern struct rb_node *ext2fs_rb_next(const struct rb_node *);
-extern struct rb_node *ext2fs_rb_prev(const struct rb_node *);
+extern struct rb_node *ext2fs_rb_next(struct rb_node *);
+extern struct rb_node *ext2fs_rb_prev(struct rb_node *);
extern struct rb_node *ext2fs_rb_first(const struct rb_root *);
extern struct rb_node *ext2fs_rb_last(const struct rb_root *);
diff --git a/lib/ext2fs/read_bb_file.c b/lib/ext2fs/read_bb_file.c
index 7d7bb7aa..8d1ad1a5 100644
--- a/lib/ext2fs/read_bb_file.c
+++ b/lib/ext2fs/read_bb_file.c
@@ -39,7 +39,7 @@ errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
void *priv_data))
{
errcode_t retval;
- blk_t blockno;
+ blk64_t blockno;
int count;
char buf[128];
@@ -55,9 +55,12 @@ errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
while (!feof (f)) {
if (fgets(buf, sizeof(buf), f) == NULL)
break;
- count = sscanf(buf, "%u", &blockno);
+ count = sscanf(buf, "%llu", &blockno);
if (count <= 0)
continue;
+ /* Badblocks isn't going to be updated for 64bit */
+ if (blockno >> 32)
+ return EOVERFLOW;
if (fs &&
((blockno < fs->super->s_first_data_block) ||
(blockno >= ext2fs_blocks_count(fs->super)))) {
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index 6449228c..e61c3303 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -31,6 +31,19 @@ static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
int mult = 3;
unsigned int ret;
+ if (fs->super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) {
+ if (*min == 1) {
+ *min += 1;
+ if (fs->super->s_backup_bgs[0])
+ return fs->super->s_backup_bgs[0];
+ }
+ if (*min == 2) {
+ *min += 1;
+ if (fs->super->s_backup_bgs[1])
+ return fs->super->s_backup_bgs[1];
+ }
+ return fs->group_desc_count;
+ }
if (!(fs->super->s_feature_ro_compat &
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
ret = *min;
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 1d5f7b2b..d24ba9a3 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -53,8 +53,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
inode_nbytes = block_nbytes = 0;
if (do_block) {
block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
- retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize,
- &block_buf);
+ retval = io_channel_alloc_buf(fs->io, 0, &block_buf);
if (retval)
goto errout;
memset(block_buf, 0xff, fs->blocksize);
@@ -62,8 +61,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
if (do_inode) {
inode_nbytes = (size_t)
((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
- retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize,
- &inode_buf);
+ retval = io_channel_alloc_buf(fs->io, 0, &inode_buf);
if (retval)
goto errout;
memset(inode_buf, 0xff, fs->blocksize);
@@ -147,6 +145,43 @@ errout:
return retval;
}
+static errcode_t mark_uninit_bg_group_blocks(ext2_filsys fs)
+{
+ dgrp_t i;
+ blk64_t blk;
+ ext2fs_block_bitmap bmap = fs->block_map;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT))
+ continue;
+
+ ext2fs_reserve_super_and_bgd(fs, i, bmap);
+
+ /*
+ * Mark the blocks used for the inode table
+ */
+ blk = ext2fs_inode_table_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap_range2(bmap, blk,
+ fs->inode_blocks_per_group);
+
+ /*
+ * Mark block used for the block bitmap
+ */
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);
+
+ /*
+ * Mark block used for the inode bitmap
+ */
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap2(bmap, blk);
+ }
+ return 0;
+}
+
static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
{
dgrp_t i;
@@ -156,7 +191,6 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
int csum_flag = 0;
- int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE;
unsigned int cnt;
blk64_t blk;
blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
@@ -166,6 +200,10 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+ if ((block_nbytes > (int) fs->blocksize) ||
+ (inode_nbytes > (int) fs->blocksize))
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
fs->write_bitmaps = ext2fs_write_bitmaps;
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
@@ -183,13 +221,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
if (retval)
goto cleanup;
- if (do_image)
- retval = ext2fs_get_mem(fs->blocksize, &block_bitmap);
- else
- retval = ext2fs_get_memalign((unsigned) block_nbytes,
- fs->blocksize,
- &block_bitmap);
-
+ retval = io_channel_alloc_buf(fs->io, 0, &block_bitmap);
if (retval)
goto cleanup;
} else
@@ -202,8 +234,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
if (retval)
goto cleanup;
- retval = ext2fs_get_mem(do_image ? fs->blocksize :
- (unsigned) inode_nbytes, &inode_bitmap);
+ retval = io_channel_alloc_buf(fs->io, 0, &inode_bitmap);
if (retval)
goto cleanup;
} else
@@ -261,7 +292,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
blk = 0;
if (blk) {
retval = io_channel_read_blk64(fs->io, blk,
- -block_nbytes, block_bitmap);
+ 1, block_bitmap);
if (retval) {
retval = EXT2_ET_BLOCK_BITMAP_READ;
goto cleanup;
@@ -283,7 +314,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
blk = 0;
if (blk) {
retval = io_channel_read_blk64(fs->io, blk,
- -inode_nbytes, inode_bitmap);
+ 1, inode_bitmap);
if (retval) {
retval = EXT2_ET_INODE_BITMAP_READ;
goto cleanup;
@@ -298,6 +329,14 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
ino_itr += inode_nbytes << 3;
}
}
+
+ /* Mark group blocks for any BLOCK_UNINIT groups */
+ if (do_block) {
+ retval = mark_uninit_bg_group_blocks(fs);
+ if (retval)
+ goto cleanup;
+ }
+
success_cleanup:
if (inode_bitmap)
ext2fs_free_mem(&inode_bitmap);
diff --git a/lib/ext2fs/sparse.c b/lib/ext2fs/sparse.c
deleted file mode 100644
index 3a0877b8..00000000
--- a/lib/ext2fs/sparse.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * sparse.c --- find the groups in an ext2 filesystem with metadata backups
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- * Copyright (C) 2002 Andreas Dilger.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Library
- * General Public License, version 2.
- * %End-Header%
- */
-
-#include "config.h"
-#include <stdio.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static int test_root(int a, int b)
-{
- if (a == 0)
- return 1;
- while (1) {
- if (a == 1)
- return 1;
- if (a % b)
- return 0;
- a = a / b;
- }
-}
-
-int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
-{
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
- return 1;
-
- if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
- test_root(group_block, 7))
- return 1;
-
- return 0;
-}
-
-/*
- * Iterate through the groups which hold BACKUP superblock/GDT copies in an
- * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
- * calling this for the first time. In a sparse filesystem it will be the
- * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
- * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
- */
-unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three,
- unsigned int *five, unsigned int *seven)
-{
- unsigned int *min = three;
- int mult = 3;
- unsigned int ret;
-
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
- ret = *min;
- *min += 1;
- return ret;
- }
-
- if (*five < *min) {
- min = five;
- mult = 5;
- }
- if (*seven < *min) {
- min = seven;
- mult = 7;
- }
-
- ret = *min;
- *min *= mult;
-
- return ret;
-}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 7c99373c..2a7b768c 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -99,10 +99,14 @@ void ext2fs_swap_super(struct ext2_super_block * sb)
}
for (; i < 17; i++)
sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
+ sb->s_backup_bgs[0] = ext2fs_swab32(sb->s_backup_bgs[0]);
+ sb->s_backup_bgs[1] = ext2fs_swab32(sb->s_backup_bgs[1]);
}
void ext2fs_swap_group_desc2(ext2_filsys fs, struct ext2_group_desc *gdp)
{
+ struct ext4_group_desc *gdp4 = (struct ext4_group_desc *)gdp;
+
/* Do the 32-bit parts first */
gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap);
gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap);
@@ -119,12 +123,10 @@ void ext2fs_swap_group_desc2(ext2_filsys fs, struct ext2_group_desc *gdp)
gdp->bg_itable_unused = ext2fs_swab16(gdp->bg_itable_unused);
gdp->bg_checksum = ext2fs_swab16(gdp->bg_checksum);
/* If we're 32-bit, we're done */
- if (fs && (!fs->super->s_desc_size ||
- (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT)))
+ if (fs == NULL || EXT2_DESC_SIZE(fs->super) < EXT2_MIN_DESC_SIZE_64BIT)
return;
/* Swap the 64-bit parts */
- struct ext4_group_desc *gdp4 = (struct ext4_group_desc *) gdp;
gdp4->bg_block_bitmap_hi = ext2fs_swab32(gdp4->bg_block_bitmap_hi);
gdp4->bg_inode_bitmap_hi = ext2fs_swab32(gdp4->bg_inode_bitmap_hi);
gdp4->bg_inode_table_hi = ext2fs_swab32(gdp4->bg_inode_table_hi);
diff --git a/lib/ext2fs/symlink.c b/lib/ext2fs/symlink.c
new file mode 100644
index 00000000..b2ef66c2
--- /dev/null
+++ b/lib/ext2fs/symlink.c
@@ -0,0 +1,150 @@
+/*
+ * symlink.c --- make a symlink in the filesystem, based on mkdir.c
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
+ const char *name, char *target)
+{
+ errcode_t retval;
+ struct ext2_inode inode;
+ ext2_ino_t scratch_ino;
+ blk64_t blk;
+ int fastlink;
+ unsigned int target_len;
+ char *block_buf = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /* The Linux kernel doesn't allow for links longer than a block */
+ target_len = strlen(target);
+ if (target_len > fs->blocksize) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+
+ /*
+ * Allocate a data block for slow links
+ */
+ fastlink = (target_len < sizeof(inode.i_block));
+ if (!fastlink) {
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Allocate an inode, if necessary
+ */
+ if (!ino) {
+ retval = ext2fs_new_inode(fs, parent, LINUX_S_IFLNK | 0755,
+ 0, &ino);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Create the inode structure....
+ */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ inode.i_mode = LINUX_S_IFLNK | 0777;
+ inode.i_uid = inode.i_gid = 0;
+ ext2fs_iblk_set(fs, &inode, fastlink ? 0 : 1);
+ inode.i_links_count = 1;
+ inode.i_size = target_len;
+ /* The time fields are set by ext2fs_write_new_inode() */
+
+ if (fastlink) {
+ /* Fast symlinks, target stored in inode */
+ strcpy((char *)&inode.i_block, target);
+ } else {
+ /* Slow symlinks, target stored in the first block */
+ memset(block_buf, 0, fs->blocksize);
+ strcpy(block_buf, target);
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ /*
+ * The extent bmap is setup after the inode and block
+ * have been written out below.
+ */
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ }
+ }
+
+ /*
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the
+ * operations using ino must come after it.
+ */
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+
+ if (!fastlink) {
+ retval = ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL,
+ &blk);
+ if (retval)
+ goto cleanup;
+
+ retval = io_channel_write_blk64(fs->io, blk, 1, block_buf);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Link the symlink into the filesystem hierarchy
+ */
+ if (name) {
+ retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+ &scratch_ino);
+ if (!retval) {
+ retval = EXT2_ET_FILE_EXISTS;
+ goto cleanup;
+ }
+ if (retval != EXT2_ET_FILE_NOT_FOUND)
+ goto cleanup;
+ retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_SYMLINK);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Update accounting....
+ */
+ if (!fastlink)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
+
+cleanup:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ return retval;
+}
diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c
index f67f6aeb..2f1c4f66 100644
--- a/lib/ext2fs/test_io.c
+++ b/lib/ext2fs/test_io.c
@@ -10,6 +10,12 @@
*/
#include "config.h"
+#if HAVE_SECURE_GETENV
+#define _GNU_SOURCE
+#endif
+#if HAVE_SECURE_GETENV
+#define _GNU_SOURCE
+#endif
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
@@ -57,45 +63,6 @@ struct test_private_data {
void (*write_blk64)(unsigned long long block, int count, errcode_t err);
};
-static errcode_t test_open(const char *name, int flags, io_channel *channel);
-static errcode_t test_close(io_channel channel);
-static errcode_t test_set_blksize(io_channel channel, int blksize);
-static errcode_t test_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t test_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t test_read_blk64(io_channel channel, unsigned long long block,
- int count, void *data);
-static errcode_t test_write_blk64(io_channel channel, unsigned long long block,
- int count, const void *data);
-static errcode_t test_flush(io_channel channel);
-static errcode_t test_write_byte(io_channel channel, unsigned long offset,
- int count, const void *buf);
-static errcode_t test_set_option(io_channel channel, const char *option,
- const char *arg);
-static errcode_t test_get_stats(io_channel channel, io_stats *stats);
-static errcode_t test_discard(io_channel channel, unsigned long long block,
- unsigned long long count);
-
-static struct struct_io_manager struct_test_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Test I/O Manager",
- test_open,
- test_close,
- test_set_blksize,
- test_read_blk,
- test_write_blk,
- test_flush,
- test_write_byte,
- test_set_option,
- test_get_stats,
- test_read_blk64,
- test_write_blk64,
- test_discard,
-};
-
-io_manager test_io_manager = &struct_test_manager;
-
/*
* These global variable can be set by the test program as
* necessary *before* calling test_open
@@ -145,6 +112,28 @@ static void test_dump_block(io_channel channel,
}
}
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *)channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_flush(data->real);
+
+ if (data->flags & TEST_FLAG_FLUSH)
+ fprintf(data->outfile, "Test_io: flush() returned %s\n",
+ retval ? error_message(retval) : "OK");
+
+ return retval;
+}
+
static void test_abort(io_channel channel, unsigned long block)
{
struct test_private_data *data;
@@ -173,7 +162,9 @@ static char *safe_getenv(const char *arg)
#endif
#endif
-#ifdef HAVE___SECURE_GETENV
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(arg);
#else
return getenv(arg);
@@ -216,14 +207,15 @@ static errcode_t test_open(const char *name, int flags, io_channel *channel)
&data->real);
if (retval)
goto cleanup;
- } else
+ } else {
data->real = 0;
- data->read_blk = test_io_cb_read_blk;
- data->write_blk = test_io_cb_write_blk;
- data->set_blksize = test_io_cb_set_blksize;
- data->write_byte = test_io_cb_write_byte;
- data->read_blk64 = test_io_cb_read_blk64;
- data->write_blk64 = test_io_cb_write_blk64;
+ }
+ data->read_blk = test_io_cb_read_blk;
+ data->write_blk = test_io_cb_write_blk;
+ data->set_blksize = test_io_cb_set_blksize;
+ data->write_byte = test_io_cb_write_byte;
+ data->read_blk64 = test_io_cb_read_blk64;
+ data->write_blk64 = test_io_cb_write_blk64;
data->outfile = NULL;
if ((value = safe_getenv("TEST_IO_LOGFILE")) != NULL)
@@ -247,6 +239,9 @@ static errcode_t test_open(const char *name, int flags, io_channel *channel)
if ((value = safe_getenv("TEST_IO_WRITE_ABORT")) != NULL)
data->write_abort_count = strtoul(value, NULL, 0);
+ if (data->real)
+ io->align = data->real->align;
+
*channel = io;
return 0;
@@ -292,8 +287,10 @@ static errcode_t test_set_blksize(io_channel channel, int blksize)
data = (struct test_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
- if (data->real)
+ if (data->real) {
retval = io_channel_set_blksize(data->real, blksize);
+ channel->align = data->real->align;
+ }
if (data->set_blksize)
data->set_blksize(blksize, retval);
if (data->flags & TEST_FLAG_SET_BLKSIZE)
@@ -434,28 +431,6 @@ static errcode_t test_write_byte(io_channel channel, unsigned long offset,
return retval;
}
-/*
- * Flush data buffers to disk.
- */
-static errcode_t test_flush(io_channel channel)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real)
- retval = io_channel_flush(data->real);
-
- if (data->flags & TEST_FLAG_FLUSH)
- fprintf(data->outfile, "Test_io: flush() returned %s\n",
- retval ? error_message(retval) : "OK");
-
- return retval;
-}
-
static errcode_t test_set_option(io_channel channel, const char *option,
const char *arg)
{
@@ -516,3 +491,22 @@ static errcode_t test_discard(io_channel channel, unsigned long long block,
block, count, retval ? error_message(retval) : "OK");
return retval;
}
+
+static struct struct_io_manager struct_test_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Test I/O Manager",
+ test_open,
+ test_close,
+ test_set_blksize,
+ test_read_blk,
+ test_write_blk,
+ test_flush,
+ test_write_byte,
+ test_set_option,
+ test_get_stats,
+ test_read_blk64,
+ test_write_blk64,
+ test_discard,
+};
+
+io_manager test_io_manager = &struct_test_manager;
diff --git a/lib/ext2fs/tst_bitmaps.c b/lib/ext2fs/tst_bitmaps.c
index 27722e53..4e02aded 100644
--- a/lib/ext2fs/tst_bitmaps.c
+++ b/lib/ext2fs/tst_bitmaps.c
@@ -151,7 +151,7 @@ int check_fs_open(char *name)
static void setup_filesystem(const char *name,
unsigned int blocks, unsigned int inodes,
- unsigned int type)
+ unsigned int type, int flags)
{
struct ext2_super_block param;
errcode_t retval;
@@ -160,7 +160,7 @@ static void setup_filesystem(const char *name,
ext2fs_blocks_count_set(&param, blocks);
param.s_inodes_count = inodes;
- retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ retval = ext2fs_initialize("test fs", flags, &param,
test_io_manager, &test_fs);
if (retval) {
@@ -193,11 +193,11 @@ errout:
void setup_cmd(int argc, char **argv)
{
- errcode_t retval;
- int i, c, err;
+ int c, err;
unsigned int blocks = 128;
unsigned int inodes = 0;
unsigned int type = EXT2FS_BMAP64_BITARRAY;
+ int flags = EXT2_FLAG_64BITS;
if (test_fs) {
ext2fs_close(test_fs);
@@ -205,7 +205,7 @@ void setup_cmd(int argc, char **argv)
}
reset_getopt();
- while ((c = getopt(argc, argv, "b:i:t:")) != EOF) {
+ while ((c = getopt(argc, argv, "b:i:lt:")) != EOF) {
switch (c) {
case 'b':
blocks = parse_ulong(optarg, argv[0],
@@ -219,6 +219,9 @@ void setup_cmd(int argc, char **argv)
if (err)
return;
break;
+ case 'l': /* Legacy bitmaps */
+ flags = 0;
+ break;
case 't':
type = parse_ulong(optarg, argv[0],
"bitmap backend type", &err);
@@ -231,7 +234,7 @@ void setup_cmd(int argc, char **argv)
return;
}
}
- setup_filesystem(argv[0], blocks, inodes, type);
+ setup_filesystem(argv[0], blocks, inodes, type, flags);
}
void close_cmd(int argc, char **argv)
@@ -266,6 +269,7 @@ void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
for (i=0; i < len; i++)
printf("%02x", buf[i]);
printf("\n");
+ printf("bits set: %u\n", ext2fs_bitcount(buf, len));
free(buf);
}
@@ -368,7 +372,7 @@ void do_testb(int argc, char *argv[])
{
unsigned int block, num;
int err;
- int test_result, op_result;
+ int test_result;
if (check_fs_open(argv[0]))
return;
@@ -399,6 +403,73 @@ void do_testb(int argc, char *argv[])
printf("Block %u is %s\n", block, test_result ? "set" : "clear");
}
+void do_ffzb(int argc, char *argv[])
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ blk64_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffzb <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_zero_block_bitmap2(test_fs->block_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_zero_block_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First unmarked block is %llu\n", out);
+}
+
+void do_ffsb(int argc, char *argv[])
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ blk64_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffsb <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_set_block_bitmap2(test_fs->block_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_set_block_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First marked block is %llu\n", out);
+}
+
+
void do_zerob(int argc, char *argv[])
{
if (check_fs_open(argv[0]))
@@ -470,7 +541,7 @@ void do_testi(int argc, char *argv[])
{
unsigned int inode;
int err;
- int test_result, op_result;
+ int test_result;
if (check_fs_open(argv[0]))
return;
@@ -488,6 +559,72 @@ void do_testi(int argc, char *argv[])
printf("Inode %u is %s\n", inode, test_result ? "set" : "clear");
}
+void do_ffzi(int argc, char *argv[])
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ ext2_ino_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffzi <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_zero_inode_bitmap2(test_fs->inode_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_zero_inode_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First unmarked inode is %u\n", out);
+}
+
+void do_ffsi(int argc, char *argv[])
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ ext2_ino_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffsi <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_set_inode_bitmap2(test_fs->inode_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_set_inode_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First marked inode is %u\n", out);
+}
+
void do_zeroi(int argc, char *argv[])
{
if (check_fs_open(argv[0]))
@@ -506,28 +643,32 @@ int main(int argc, char **argv)
char *request = (char *)NULL;
char *cmd_file = 0;
int sci_idx;
+ int flags = EXT2_FLAG_64BITS;
add_error_table(&et_ss_error_table);
add_error_table(&et_ext2_error_table);
- while ((c = getopt (argc, argv, "b:i:t:R:f:")) != EOF) {
+ while ((c = getopt (argc, argv, "b:i:lt:R:f:")) != EOF) {
switch (c) {
case 'b':
blocks = parse_ulong(optarg, argv[0],
"number of blocks", &err);
if (err)
- return;
+ exit(1);
break;
case 'i':
inodes = parse_ulong(optarg, argv[0],
"number of blocks", &err);
if (err)
- return;
+ exit(1);
+ break;
+ case 'l': /* Legacy bitmaps */
+ flags = 0;
break;
case 't':
type = parse_ulong(optarg, argv[0],
"bitmap backend type", &err);
if (err)
- return;
+ exit(1);
break;
case 'R':
request = optarg;
@@ -558,7 +699,7 @@ int main(int argc, char **argv)
printf("%s %s. Type '?' for a list of commands.\n\n",
subsystem_name, version);
- setup_filesystem(argv[0], blocks, inodes, type);
+ setup_filesystem(argv[0], blocks, inodes, type, flags);
if (request) {
code = ss_execute_line(sci_idx, request);
diff --git a/lib/ext2fs/tst_bitmaps_cmd.ct b/lib/ext2fs/tst_bitmaps_cmd.ct
index 5a51d23c..13b7fa7c 100644
--- a/lib/ext2fs/tst_bitmaps_cmd.ct
+++ b/lib/ext2fs/tst_bitmaps_cmd.ct
@@ -21,6 +21,12 @@ request do_clearb, "Clear block",
request do_testb, "Test block",
test_block, testb;
+request do_ffzb, "Find first zero block",
+ find_first_zero_block, ffzb;
+
+request do_ffsb, "Find first set block",
+ find_first_set_block, ffsb;
+
request do_zerob, "Clear block bitmap",
clear_block_bitmap, zerob;
@@ -33,6 +39,12 @@ request do_cleari, "Clear inode",
request do_testi, "Test inode",
test_inode, testi;
+request do_ffzi, "Find first zero inode",
+ find_first_zero_inode, ffzi;
+
+request do_ffsi, "Find first set inode",
+ find_first_set_inode, ffsi;
+
request do_zeroi, "Clear inode bitmap",
clear_inode_bitmap, zeroi;
diff --git a/lib/ext2fs/tst_bitmaps_cmds b/lib/ext2fs/tst_bitmaps_cmds
index 13f4ea83..7492592f 100644
--- a/lib/ext2fs/tst_bitmaps_cmds
+++ b/lib/ext2fs/tst_bitmaps_cmds
@@ -16,6 +16,21 @@ testb 11
testb 15
testb 16
dump_bb
+ffzb 11 16
+ffzb 12 16
+ffzb 12 20
+ffsb 0 127
+ffsb 1 128
+ffsb 1 127
+ffsb 1 10
+ffsb 1 11
+ffsb 12 12
+ffsb 13 12
+ffsb 12 15
+clearb 13
+ffzb 12 20
+ffsb 13 18
+setb 13
clearb 12 7
testb 12 7
setb 15
@@ -33,6 +48,18 @@ seti 5
testi 6
testi 1
dump_ib
+ffzi 1 6
+ffzi 2 5
+ffzi 2 6
+ffsi 0 31
+ffsi 1 33
+ffsi 1 32
+ffsi 2 32
+ffsi 6 32
+cleari 4
+ffzi 2 6
+ffsi 4 32
+ffsi 5 32
zeroi
testi 5
seti 5
@@ -42,5 +69,80 @@ cleari 5
testi 17
testi 6
testi 4
+clearb 7 12
+dump_bb
+setb 1
+dump_bb
+setb 2
+dump_bb
+setb 3
+dump_bb
+setb 4
+dump_bb
+setb 5
+dump_bb
+setb 6
+dump_bb
+setb 7
+dump_bb
+setb 8
+dump_bb
+setb 10
+setb 12
+setb 14
+setb 17
+setb 19
+setb 24
+setb 26
+setb 27
+setb 30
+setb 31
+setb 32
+setb 35
+setb 39
+setb 40
+setb 44
+setb 46
+setb 47
+setb 49
+setb 51
+setb 52
+clearb 2
+clearb 3
+clearb 7
+dump_bb
+ffsb 14 127
+ffsb 15 127
+ffsb 36 127
+ffsb 32 127
+ffsb 52 127
+ffsb 53 127
+ffsb 46 127
+ffsb 45 127
+ffsb 41 127
+ffsb 20 127
+ffsb 1 127
+ffsb 2 127
+ffsb 3 127
+ffsb 4 127
+ffsb 5 127
+ffsb 6 127
+ffsb 7 127
+ffsb 8 127
+ffzb 1 127
+ffzb 2 127
+ffzb 3 127
+ffzb 4 127
+ffzb 5 127
+ffzb 6 127
+ffzb 7 127
+ffzb 8 127
+ffzb 45 127
+ffzb 46 127
+ffzb 47 127
+ffzb 48 127
+ffzb 49 127
+ffzb 50 127
+ffzb 51 127
quit
diff --git a/lib/ext2fs/tst_bitmaps_exp b/lib/ext2fs/tst_bitmaps_exp
index aa64ae72..6b226662 100644
--- a/lib/ext2fs/tst_bitmaps_exp
+++ b/lib/ext2fs/tst_bitmaps_exp
@@ -36,6 +36,37 @@ tst_bitmaps: testb 16
Block 16 is set
tst_bitmaps: dump_bb
block bitmap: 00f80000000000000000000000000000
+bits set: 5
+tst_bitmaps: ffzb 11 16
+First unmarked block is 11
+tst_bitmaps: ffzb 12 16
+ext2fs_find_first_zero_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffzb 12 20
+First unmarked block is 17
+tst_bitmaps: ffsb 0 127
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 1 128
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 1 127
+First marked block is 12
+tst_bitmaps: ffsb 1 10
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 1 11
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 12 12
+First marked block is 12
+tst_bitmaps: ffsb 13 12
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 12 15
+First marked block is 12
+tst_bitmaps: clearb 13
+Clearing block 13, was set before
+tst_bitmaps: ffzb 12 20
+First unmarked block is 13
+tst_bitmaps: ffsb 13 18
+First marked block is 14
+tst_bitmaps: setb 13
+Setting block 13, was clear before
tst_bitmaps: clearb 12 7
Clearing blocks 12 to 18
tst_bitmaps: testb 12 7
@@ -52,6 +83,7 @@ tst_bitmaps: setb 12 7
Marking blocks 12 to 18
tst_bitmaps: dump_bb
block bitmap: 00f80300000000000000000000000000
+bits set: 7
tst_bitmaps: seti 2
Setting inode 2, was clear before
tst_bitmaps: seti 5
@@ -70,6 +102,31 @@ tst_bitmaps: testi 1
Inode 1 is clear
tst_bitmaps: dump_ib
inode bitmap: 1e000000
+bits set: 4
+tst_bitmaps: ffzi 1 6
+First unmarked inode is 1
+tst_bitmaps: ffzi 2 5
+ext2fs_find_first_zero_inode_bitmap2() returned No such file or directory
+tst_bitmaps: ffzi 2 6
+First unmarked inode is 6
+tst_bitmaps: ffsi 0 31
+ext2fs_find_first_set_inode_bitmap2() returned Invalid argument
+tst_bitmaps: ffsi 1 33
+ext2fs_find_first_set_inode_bitmap2() returned Invalid argument
+tst_bitmaps: ffsi 1 32
+First marked inode is 2
+tst_bitmaps: ffsi 2 32
+First marked inode is 2
+tst_bitmaps: ffsi 6 32
+ext2fs_find_first_set_inode_bitmap2() returned No such file or directory
+tst_bitmaps: cleari 4
+Clearing inode 4, was set before
+tst_bitmaps: ffzi 2 6
+First unmarked inode is 4
+tst_bitmaps: ffsi 4 32
+First marked inode is 5
+tst_bitmaps: ffsi 5 32
+First marked inode is 5
tst_bitmaps: zeroi
Clearing inode bitmap.
tst_bitmaps: testi 5
@@ -88,5 +145,165 @@ tst_bitmaps: testi 6
Inode 6 is clear
tst_bitmaps: testi 4
Inode 4 is clear
+tst_bitmaps: clearb 7 12
+Clearing blocks 7 to 18
+tst_bitmaps: dump_bb
+block bitmap: 00000000000000000000000000000000
+bits set: 0
+tst_bitmaps: setb 1
+Setting block 1, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 01000000000000000000000000000000
+bits set: 1
+tst_bitmaps: setb 2
+Setting block 2, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 03000000000000000000000000000000
+bits set: 2
+tst_bitmaps: setb 3
+Setting block 3, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 07000000000000000000000000000000
+bits set: 3
+tst_bitmaps: setb 4
+Setting block 4, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 0f000000000000000000000000000000
+bits set: 4
+tst_bitmaps: setb 5
+Setting block 5, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 1f000000000000000000000000000000
+bits set: 5
+tst_bitmaps: setb 6
+Setting block 6, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 3f000000000000000000000000000000
+bits set: 6
+tst_bitmaps: setb 7
+Setting block 7, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 7f000000000000000000000000000000
+bits set: 7
+tst_bitmaps: setb 8
+Setting block 8, was clear before
+tst_bitmaps: dump_bb
+block bitmap: ff000000000000000000000000000000
+bits set: 8
+tst_bitmaps: setb 10
+Setting block 10, was clear before
+tst_bitmaps: setb 12
+Setting block 12, was clear before
+tst_bitmaps: setb 14
+Setting block 14, was clear before
+tst_bitmaps: setb 17
+Setting block 17, was clear before
+tst_bitmaps: setb 19
+Setting block 19, was clear before
+tst_bitmaps: setb 24
+Setting block 24, was clear before
+tst_bitmaps: setb 26
+Setting block 26, was clear before
+tst_bitmaps: setb 27
+Setting block 27, was clear before
+tst_bitmaps: setb 30
+Setting block 30, was clear before
+tst_bitmaps: setb 31
+Setting block 31, was clear before
+tst_bitmaps: setb 32
+Setting block 32, was clear before
+tst_bitmaps: setb 35
+Setting block 35, was clear before
+tst_bitmaps: setb 39
+Setting block 39, was clear before
+tst_bitmaps: setb 40
+Setting block 40, was clear before
+tst_bitmaps: setb 44
+Setting block 44, was clear before
+tst_bitmaps: setb 46
+Setting block 46, was clear before
+tst_bitmaps: setb 47
+Setting block 47, was clear before
+tst_bitmaps: setb 49
+Setting block 49, was clear before
+tst_bitmaps: setb 51
+Setting block 51, was clear before
+tst_bitmaps: setb 52
+Setting block 52, was clear before
+tst_bitmaps: clearb 2
+Clearing block 2, was set before
+tst_bitmaps: clearb 3
+Clearing block 3, was set before
+tst_bitmaps: clearb 7
+Clearing block 7, was set before
+tst_bitmaps: dump_bb
+block bitmap: b92a85e6c4680d000000000000000000
+bits set: 25
+tst_bitmaps: ffsb 14 127
+First marked block is 14
+tst_bitmaps: ffsb 15 127
+First marked block is 17
+tst_bitmaps: ffsb 36 127
+First marked block is 39
+tst_bitmaps: ffsb 32 127
+First marked block is 32
+tst_bitmaps: ffsb 52 127
+First marked block is 52
+tst_bitmaps: ffsb 53 127
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 46 127
+First marked block is 46
+tst_bitmaps: ffsb 45 127
+First marked block is 46
+tst_bitmaps: ffsb 41 127
+First marked block is 44
+tst_bitmaps: ffsb 20 127
+First marked block is 24
+tst_bitmaps: ffsb 1 127
+First marked block is 1
+tst_bitmaps: ffsb 2 127
+First marked block is 4
+tst_bitmaps: ffsb 3 127
+First marked block is 4
+tst_bitmaps: ffsb 4 127
+First marked block is 4
+tst_bitmaps: ffsb 5 127
+First marked block is 5
+tst_bitmaps: ffsb 6 127
+First marked block is 6
+tst_bitmaps: ffsb 7 127
+First marked block is 8
+tst_bitmaps: ffsb 8 127
+First marked block is 8
+tst_bitmaps: ffzb 1 127
+First unmarked block is 2
+tst_bitmaps: ffzb 2 127
+First unmarked block is 2
+tst_bitmaps: ffzb 3 127
+First unmarked block is 3
+tst_bitmaps: ffzb 4 127
+First unmarked block is 7
+tst_bitmaps: ffzb 5 127
+First unmarked block is 7
+tst_bitmaps: ffzb 6 127
+First unmarked block is 7
+tst_bitmaps: ffzb 7 127
+First unmarked block is 7
+tst_bitmaps: ffzb 8 127
+First unmarked block is 9
+tst_bitmaps: ffzb 45 127
+First unmarked block is 45
+tst_bitmaps: ffzb 46 127
+First unmarked block is 48
+tst_bitmaps: ffzb 47 127
+First unmarked block is 48
+tst_bitmaps: ffzb 48 127
+First unmarked block is 48
+tst_bitmaps: ffzb 49 127
+First unmarked block is 50
+tst_bitmaps: ffzb 50 127
+First unmarked block is 50
+tst_bitmaps: ffzb 51 127
+First unmarked block is 53
tst_bitmaps: quit
tst_bitmaps:
diff --git a/lib/ext2fs/tst_inode_size.c b/lib/ext2fs/tst_inode_size.c
index 3e43d9f4..e20ec981 100644
--- a/lib/ext2fs/tst_inode_size.c
+++ b/lib/ext2fs/tst_inode_size.c
@@ -18,7 +18,10 @@
struct ext2_inode_large inode;
+#ifndef offsetof
#define offsetof(type, member) __builtin_offsetof(type, member)
+#endif
+
#define check_field(x, s) cur_offset = do_field(#x, s, sizeof(inode.x), \
offsetof(struct ext2_inode_large, x), \
cur_offset)
diff --git a/lib/ext2fs/tst_iscan.c b/lib/ext2fs/tst_iscan.c
index 6f783c38..70bfbecc 100644
--- a/lib/ext2fs/tst_iscan.c
+++ b/lib/ext2fs/tst_iscan.c
@@ -26,7 +26,7 @@
#include "ext2_fs.h"
#include "ext2fs.h"
-blk_t test_vec[] = { 8, 12, 24, 34, 43, 44, 100, 0 };
+blk64_t test_vec[] = { 8, 12, 24, 34, 43, 44, 100, 0 };
ext2_filsys test_fs;
ext2fs_block_bitmap bad_block_map, touched_map;
@@ -182,7 +182,7 @@ static void check_map(void)
for (i=0; test_vec[i]; i++) {
if (ext2fs_test_block_bitmap2(touched_map, test_vec[i])) {
- printf("Bad block was touched --- %u\n", test_vec[i]);
+ printf("Bad block was touched --- %llu\n", test_vec[i]);
failed++;
first_no_comma = 1;
}
diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c
index 76e6e6fe..f9cec8af 100644
--- a/lib/ext2fs/tst_super_size.c
+++ b/lib/ext2fs/tst_super_size.c
@@ -21,7 +21,10 @@
struct sb_struct sb;
+#ifndef offsetof
#define offsetof(type, member) __builtin_offsetof (type, member)
+#endif
+
#define check_field(x, s) cur_offset = do_field(#x, s, sizeof(sb.x), \
offsetof(struct sb_struct, x), \
cur_offset)
@@ -132,7 +135,8 @@ int main(int argc, char **argv)
check_field(s_usr_quota_inum, 4);
check_field(s_grp_quota_inum, 4);
check_field(s_overhead_blocks, 4);
- check_field(s_reserved, 108 * 4);
+ check_field(s_backup_bgs, 8);
+ check_field(s_reserved, 106 * 4);
check_field(s_checksum, 4);
do_field("Superblock end", 0, 0, cur_offset, 1024);
#endif
diff --git a/lib/ext2fs/undo_io.c b/lib/ext2fs/undo_io.c
index 56b0eeb5..0e05c933 100644
--- a/lib/ext2fs/undo_io.c
+++ b/lib/ext2fs/undo_io.c
@@ -71,42 +71,7 @@ struct undo_private_data {
ext2_loff_t offset;
};
-static errcode_t undo_open(const char *name, int flags, io_channel *channel);
-static errcode_t undo_close(io_channel channel);
-static errcode_t undo_set_blksize(io_channel channel, int blksize);
-static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
- int count, void *data);
-static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
- int count, const void *data);
-static errcode_t undo_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t undo_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t undo_flush(io_channel channel);
-static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
- int size, const void *data);
-static errcode_t undo_set_option(io_channel channel, const char *option,
- const char *arg);
-static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
-
-static struct struct_io_manager struct_undo_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Undo I/O Manager",
- undo_open,
- undo_close,
- undo_set_blksize,
- undo_read_blk,
- undo_write_blk,
- undo_flush,
- undo_write_byte,
- undo_set_option,
- undo_get_stats,
- undo_read_blk64,
- undo_write_blk64,
-};
-
-io_manager undo_io_manager = &struct_undo_manager;
-static io_manager undo_io_backing_manager ;
+static io_manager undo_io_backing_manager;
static char *tdb_file;
static int actual_size;
@@ -621,3 +586,21 @@ static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
return retval;
}
+
+static struct struct_io_manager struct_undo_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Undo I/O Manager",
+ undo_open,
+ undo_close,
+ undo_set_blksize,
+ undo_read_blk,
+ undo_write_blk,
+ undo_flush,
+ undo_write_byte,
+ undo_set_option,
+ undo_get_stats,
+ undo_read_blk64,
+ undo_write_blk64,
+};
+
+io_manager undo_io_manager = &struct_undo_manager;
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index da3f8fde..c3185b6a 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -1,13 +1,13 @@
/*
* unix_io.c --- This is the Unix (well, really POSIX) implementation
- * of the I/O manager.
+ * of the I/O manager.
*
* Implements a one-block write-through cache.
*
* Includes support for Windows NT support under Cygwin.
*
* Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- * 2002 by Theodore Ts'o.
+ * 2002 by Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Library
@@ -58,10 +58,6 @@
#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */
#endif
-#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
-#define BLKSSZGET _IO(0x12,104)/* get block device sector size */
-#endif
-
#undef ALIGN_DEBUG
#include "ext2_fs.h"
@@ -75,11 +71,11 @@
if ((struct)->magic != (code)) return (code)
struct unix_cache {
- char *buf;
- unsigned long block;
- int access_time;
- unsigned dirty:1;
- unsigned in_use:1;
+ char *buf;
+ unsigned long long block;
+ int access_time;
+ unsigned dirty:1;
+ unsigned in_use:1;
};
#define CACHE_SIZE 8
@@ -101,51 +97,9 @@ struct unix_private_data {
#define IS_ALIGNED(n, align) ((((unsigned long) n) & \
((unsigned long) ((align)-1))) == 0)
-static errcode_t unix_open(const char *name, int flags, io_channel *channel);
-static errcode_t unix_close(io_channel channel);
-static errcode_t unix_set_blksize(io_channel channel, int blksize);
-static errcode_t unix_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t unix_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t unix_flush(io_channel channel);
-static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
- int size, const void *data);
-static errcode_t unix_set_option(io_channel channel, const char *option,
- const char *arg);
-static errcode_t unix_get_stats(io_channel channel, io_stats *stats)
-;
-static void reuse_cache(io_channel channel, struct unix_private_data *data,
- struct unix_cache *cache, unsigned long long block);
-static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
- int count, void *data);
-static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
- int count, const void *data);
-static errcode_t unix_discard(io_channel channel, unsigned long long block,
- unsigned long long count);
-
-static struct struct_io_manager struct_unix_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Unix I/O Manager",
- unix_open,
- unix_close,
- unix_set_blksize,
- unix_read_blk,
- unix_write_blk,
- unix_flush,
- unix_write_byte,
- unix_set_option,
- unix_get_stats,
- unix_read_blk64,
- unix_write_blk64,
- unix_discard,
-};
-
-io_manager unix_io_manager = &struct_unix_manager;
-
static errcode_t unix_get_stats(io_channel channel, io_stats *stats)
{
- errcode_t retval = 0;
+ errcode_t retval = 0;
struct unix_private_data *data;
@@ -180,8 +134,9 @@ static errcode_t raw_read_blk(io_channel channel,
retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
goto error_out;
}
- if ((data->align == 0) ||
- ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) {
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
actual = read(data->dev, buf, size);
if (actual != size) {
short_read:
@@ -250,8 +205,9 @@ static errcode_t raw_write_blk(io_channel channel,
goto error_out;
}
- if ((data->align == 0) ||
- ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) {
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
actual = write(data->dev, buf, size);
if (actual != size) {
short_write:
@@ -318,16 +274,14 @@ static errcode_t alloc_cache(io_channel channel,
cache->in_use = 0;
if (cache->buf)
ext2fs_free_mem(&cache->buf);
- retval = ext2fs_get_memalign(channel->block_size,
- data->align, &cache->buf);
+ retval = io_channel_alloc_buf(channel, 0, &cache->buf);
if (retval)
return retval;
}
- if (data->align) {
+ if (channel->align) {
if (data->bounce)
ext2fs_free_mem(&data->bounce);
- retval = ext2fs_get_memalign(channel->block_size, data->align,
- &data->bounce);
+ retval = io_channel_alloc_buf(channel, 0, &data->bounce);
}
return retval;
}
@@ -439,16 +393,48 @@ static errcode_t flush_cached_blocks(io_channel channel,
#endif
#endif
+int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
+{
+ if (mode)
+#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return open64(pathname, flags, mode);
+ else
+ return open64(pathname, flags);
+#else
+ return open(pathname, flags, mode);
+ else
+ return open(pathname, flags);
+#endif
+}
+
+int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return stat64(path, buf);
+#else
+ return stat(path, buf);
+#endif
+}
+
+int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return fstat64(fd, buf);
+#else
+ return fstat(fd, buf);
+#endif
+}
+
static errcode_t unix_open(const char *name, int flags, io_channel *channel)
{
io_channel io = NULL;
struct unix_private_data *data = NULL;
errcode_t retval;
- int open_flags, zeroes = 0;
+ int open_flags;
int f_nocache = 0;
ext2fs_struct_stat st;
#ifdef __linux__
- struct utsname ut;
+ struct utsname ut;
#endif
if (name == 0)
@@ -477,16 +463,21 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
memset(data, 0, sizeof(struct unix_private_data));
data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
data->io_stats.num_fields = 2;
+ data->dev = -1;
open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
if (flags & IO_FLAG_EXCLUSIVE)
open_flags |= O_EXCL;
#if defined(O_DIRECT)
- if (flags & IO_FLAG_DIRECT_IO)
+ if (flags & IO_FLAG_DIRECT_IO) {
open_flags |= O_DIRECT;
+ io->align = ext2fs_get_dio_alignment(data->dev);
+ }
#elif defined(F_NOCACHE)
- if (flags & IO_FLAG_DIRECT_IO)
+ if (flags & IO_FLAG_DIRECT_IO) {
f_nocache = F_NOCACHE;
+ io->align = 4096;
+ }
#endif
data->flags = flags;
@@ -516,17 +507,13 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
}
-#ifdef BLKSSZGET
- if (flags & IO_FLAG_DIRECT_IO) {
- if (ioctl(data->dev, BLKSSZGET, &data->align) != 0)
- data->align = io->block_size;
- }
-#endif
-
#ifdef BLKDISCARDZEROES
- ioctl(data->dev, BLKDISCARDZEROES, &zeroes);
- if (zeroes)
- io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ {
+ int zeroes = 0;
+ if (ioctl(data->dev, BLKDISCARDZEROES, &zeroes) == 0 &&
+ zeroes)
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ }
#endif
#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -534,7 +521,8 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
* Some operating systems require that the buffers be aligned,
* regardless of O_DIRECT
*/
- data->align = 512;
+ if (!io->align)
+ io->align = 512;
#endif
@@ -549,7 +537,6 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
/* Is the block device actually writable? */
error = ioctl(data->dev, BLKROGET, &readonly);
if (!error && readonly) {
- close(data->dev);
retval = EPERM;
goto cleanup;
}
@@ -595,11 +582,17 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
cleanup:
if (data) {
+ if (data->dev >= 0)
+ close(data->dev);
free_cache(data);
ext2fs_free_mem(&data);
}
- if (io)
+ if (io) {
+ if (io->name) {
+ ext2fs_free_mem(&io->name);
+ }
ext2fs_free_mem(&io);
+ }
return retval;
}
@@ -810,7 +803,7 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
data = (struct unix_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
- if (data->align != 0) {
+ if (channel->align != 0) {
#ifdef ALIGN_DEBUG
printf("unix_write_byte: O_DIRECT fallback\n");
#endif
@@ -888,7 +881,6 @@ static errcode_t unix_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
struct unix_private_data *data;
- __uint64_t range[2];
int ret;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
@@ -897,8 +889,10 @@ static errcode_t unix_discard(io_channel channel, unsigned long long block,
if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
#ifdef BLKDISCARD
- range[0] = (__uint64_t)(block) * channel->block_size;
- range[1] = (__uint64_t)(count) * channel->block_size;
+ __u64 range[2];
+
+ range[0] = (__u64)(block) * channel->block_size;
+ range[1] = (__u64)(count) * channel->block_size;
ret = ioctl(data->dev, BLKDISCARD, &range);
#else
@@ -927,3 +921,22 @@ static errcode_t unix_discard(io_channel channel, unsigned long long block,
unimplemented:
return EXT2_ET_UNIMPLEMENTED;
}
+
+static struct struct_io_manager struct_unix_manager = {
+ EXT2_ET_MAGIC_IO_MANAGER,
+ "Unix I/O Manager",
+ unix_open,
+ unix_close,
+ unix_set_blksize,
+ unix_read_blk,
+ unix_write_blk,
+ unix_flush,
+ unix_write_byte,
+ unix_set_option,
+ unix_get_stats,
+ unix_read_blk64,
+ unix_write_blk64,
+ unix_discard,
+};
+
+io_manager unix_io_manager = &struct_unix_manager;
diff --git a/lib/quota/Makefile.in b/lib/quota/Makefile.in
index 5a12d4f9..92df2681 100644
--- a/lib/quota/Makefile.in
+++ b/lib/quota/Makefile.in
@@ -12,9 +12,6 @@ INSTALL = @INSTALL@
all::
-SMANPAGES=
-
-
OBJS= mkquota.o quotaio.o quotaio_v2.o quotaio_tree.o dict.o
SRCS= $(srcdir)/mkquota.c \
@@ -31,7 +28,7 @@ LIBDIR= quota
#ELF_IMAGE = libquota
#ELF_MYDIR = quota
#ELF_INSTALL_DIR = $(root_libdir)
-#ELF_OTHER_LIBS = -L../.. -lext2fs
+#ELF_OTHER_LIBS = -lext2fs
#BSDLIB_VERSION = 1.0
#BSDLIB_IMAGE = libquota
@@ -47,17 +44,12 @@ LIBDIR= quota
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
#ELF_CMT# $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
#BSDLIB_CMT# $(Q) $(CC) $(ALL_CFLAGS) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
-all:: $(SMANPAGES) quota.pc
-
-quota.pc: $(srcdir)/quota.pc.in $(top_builddir)/config.status
- $(E) " CONFIG.STATUS $@"
- $(Q) cd $(top_builddir); CONFIG_FILES=lib/quota/quota.pc ./config.status
-
dict.o:
$(E) " CC $<"
$(Q) $(CC) -c $(ALL_CFLAGS) $(top_srcdir)/e2fsck/dict.c -o $@
@@ -71,32 +63,10 @@ dict.o:
#BSDLIB_CMT# $(top_srcdir)/e2fsck/dict.c
installdirs::
- $(E) " MKINSTALLDIRS $(libdir) $(includedir)/quota $(man3dir)"
- $(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
- $(DESTDIR)$(includedir)/quota $(DESTDIR)$(man3dir) \
- $(DESTDIR)$(libdir)/pkgconfig
-
-install:: all installdirs
- $(E) " INSTALL_DATA $(libdir)/libquota.a"
- $(Q) $(INSTALL_DATA) libquota.a $(DESTDIR)$(libdir)/libquota.a
- -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libquota.a
- $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libquota.a
- $(E) " INSTALL_DATA $(includedir)/quota/mkquota.h"
- $(Q) $(INSTALL_DATA) $(srcdir)/mkquota.h $(DESTDIR)$(includedir)/quota/mkquota.h
- $(Q) for i in $(SMANPAGES); do \
- $(RM) -f $(DESTDIR)$(man3dir)/$$i.gz; \
- echo " INSTALL_DATA $(man3dir)/$$i"; \
- $(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \
- done
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/quota.pc"
- $(Q) $(INSTALL_DATA) quota.pc $(DESTDIR)$(libdir)/pkgconfig/quota.pc
+
+install:: all
uninstall::
- $(RM) -f $(DESTDIR)$(libdir)/libquota.a \
- $(DESTDIR)$(libdir)/pkgconfig/quota.pc
- for i in $(SMANPAGES); do \
- $(RM) -f $(DESTDIR)$(man3dir)/$$i; \
- done
clean::
$(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* checker/*
@@ -107,7 +77,7 @@ clean::
mostlyclean:: clean
distclean:: clean
- $(RM) -f .depend Makefile quota.pc \
+ $(RM) -f .depend Makefile \
$(srcdir)/TAGS $(srcdir)/Makefile.in.old
#
@@ -130,33 +100,36 @@ mkquota.o: $(srcdir)/mkquota.c $(top_builddir)/lib/config.h \
$(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(top_srcdir)/lib/e2p/e2p.h $(srcdir)/quota.h $(srcdir)/quotaio.h \
- $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h $(srcdir)/quotaio_v2.h \
- $(srcdir)/mkquota.h $(top_srcdir)/lib/../e2fsck/dict.h $(srcdir)/common.h
+ $(top_srcdir)/lib/e2p/e2p.h $(srcdir)/quotaio.h $(srcdir)/dqblk_v2.h \
+ $(srcdir)/quotaio_tree.h $(top_srcdir)/lib/../e2fsck/dict.h \
+ $(srcdir)/quotaio_v2.h $(srcdir)/common.h
quotaio.o: $(srcdir)/quotaio.c $(top_builddir)/lib/config.h \
- $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h $(srcdir)/quotaio.h \
- $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
- $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
- $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
- $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(srcdir)/quota.h $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h
+ $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h \
+ $(top_srcdir)/lib/../e2fsck/dict.h
quotaio_tree.o: $(srcdir)/quotaio_tree.c $(top_builddir)/lib/config.h \
- $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h $(srcdir)/quotaio_tree.h \
- $(srcdir)/quota.h $(top_builddir)/lib/ext2fs/ext2_types.h \
- $(srcdir)/quotaio.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
- $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio_tree.h \
+ $(srcdir)/quotaio.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
$(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
$(top_builddir)/lib/ext2fs/ext2_err.h \
$(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
- $(srcdir)/dqblk_v2.h
+ $(srcdir)/dqblk_v2.h $(top_srcdir)/lib/../e2fsck/dict.h
quotaio_v2.o: $(srcdir)/quotaio_v2.c $(top_builddir)/lib/config.h \
- $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h $(srcdir)/quotaio_v2.h \
- $(srcdir)/quota.h $(top_builddir)/lib/ext2fs/ext2_types.h \
- $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h $(srcdir)/quotaio.h \
- $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
- $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
- $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio_v2.h \
+ $(srcdir)/quotaio.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
+ $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h \
+ $(top_srcdir)/lib/../e2fsck/dict.h
dict.o: $(srcdir)/../../e2fsck/dict.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/../../e2fsck/dict.h
diff --git a/lib/quota/common.h b/lib/quota/common.h
index b5e83316..f1ad79f2 100644
--- a/lib/quota/common.h
+++ b/lib/quota/common.h
@@ -7,20 +7,28 @@
#ifndef __QUOTA_COMMON_H__
#define __QUOTA_COMMON_H__
+#if EXT2_FLAT_INCLUDES
+#include "e2_types.h"
+#else
+#include <ext2fs/ext2_types.h>
+#endif /* EXT2_FLAT_INCLUDES */
+
+/* #define DEBUG_QUOTA 1 */
+
#ifndef __attribute__
# if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
# define __attribute__(x)
# endif
#endif
-#define log_err(format, ...) fprintf(stderr, \
- "[ERROR] %s:%d:%s:: " format "\n", \
- __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_err(format, arg ...) \
+ fprintf(stderr, "[ERROR] %s:%d:%s:: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
#ifdef DEBUG_QUOTA
-# define log_debug(format, ...) fprintf(stderr, \
- "[DEBUG] %s:%d:%s:: " format "\n", \
- __FILE__, __LINE__, __func__, __VA_ARGS__)
+# define log_debug(format, arg ...) \
+ fprintf(stderr, "[DEBUG] %s:%d:%s:: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
#else
# define log_debug(format, ...)
#endif
diff --git a/lib/quota/dqblk_v2.h b/lib/quota/dqblk_v2.h
index 18055c69..d12512a6 100644
--- a/lib/quota/dqblk_v2.h
+++ b/lib/quota/dqblk_v2.h
@@ -7,28 +7,16 @@
#ifndef __QUOTA_DQBLK_V2_H__
#define __QUOTA_DQBLK_V2_H__
-#include <sys/types.h>
#include "quotaio_tree.h"
-#define Q_V2_GETQUOTA 0x0D00 /* Get limits and usage */
-#define Q_V2_SETQUOTA 0x0E00 /* Set limits and usage */
-#define Q_V2_SETUSE 0x0F00 /* Set only usage */
-#define Q_V2_SETQLIM 0x0700 /* Set only limits */
-#define Q_V2_GETINFO 0x0900 /* Get information about quota */
-#define Q_V2_SETINFO 0x0A00 /* Set information about quota */
-#define Q_V2_SETGRACE 0x0B00 /* Set just grace times in quotafile
- * information */
-#define Q_V2_SETFLAGS 0x0C00 /* Set just flags in quotafile information */
-#define Q_V2_GETSTATS 0x1100 /* get collected stats (before proc was used) */
-
/* Structure for format specific information */
struct v2_mem_dqinfo {
struct qtree_mem_dqinfo dqi_qtree;
- uint dqi_flags; /* Flags set in quotafile */
- uint dqi_used_entries; /* Number of entries in file -
- updated by scan_dquots */
- uint dqi_data_blocks; /* Number of data blocks in file -
- updated by scan_dquots */
+ unsigned int dqi_flags; /* Flags set in quotafile */
+ unsigned int dqi_used_entries; /* Number of entries in file -
+ updated by scan_dquots */
+ unsigned int dqi_data_blocks; /* Number of data blocks in file -
+ updated by scan_dquots */
};
struct v2_mem_dqblk {
diff --git a/lib/quota/mkquota.c b/lib/quota/mkquota.c
index fbfde927..58803d04 100644
--- a/lib/quota/mkquota.c
+++ b/lib/quota/mkquota.c
@@ -15,17 +15,16 @@
#include "ext2fs/ext2fs.h"
#include "e2p/e2p.h"
-#include "quota.h"
#include "quotaio.h"
#include "quotaio_v2.h"
#include "quotaio_tree.h"
-#include "mkquota.h"
#include "common.h"
/* Needed for architectures where sizeof(int) != sizeof(void *) */
#define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
#define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr))
+#if DEBUG_QUOTA
static void print_inode(struct ext2_inode *inode)
{
if (!inode)
@@ -46,17 +45,21 @@ static void print_inode(struct ext2_inode *inode)
return;
}
-int quota_is_on(ext2_filsys fs, int type)
+static void print_dquot(const char *desc, struct dquot *dq)
{
- char tmp[1024];
- qid_t id = (type == USRQUOTA) ? getuid() : getgid();
-
-#ifdef HAVE_QUOTACTL
- if (!quotactl(QCMD(Q_V2_GETQUOTA, type), fs->device_name, id, tmp))
- return 1;
-#endif
- return 0;
+ if (desc)
+ fprintf(stderr, "%s: ", desc);
+ fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
+ dq->dq_id, dq->dq_dqb.dqb_curspace,
+ dq->dq_dqb.dqb_bsoftlimit, dq->dq_dqb.dqb_bhardlimit,
+ dq->dq_dqb.dqb_curinodes,
+ dq->dq_dqb.dqb_isoftlimit, dq->dq_dqb.dqb_ihardlimit);
}
+#else
+static void print_dquot(const char *desc, struct dquot *dq)
+{
+}
+#endif
/*
* Returns 0 if not able to find the quota file, otherwise returns its
@@ -71,7 +74,7 @@ int quota_file_exists(ext2_filsys fs, int qtype, int fmt)
if (qtype >= MAXQUOTAS)
return -EINVAL;
- quota_get_qf_name(qtype, fmt, qf_name);
+ quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name);
ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
&ino);
@@ -100,8 +103,13 @@ void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype)
errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
{
ext2_ino_t qf_ino;
+ errcode_t retval;
- ext2fs_read_bitmaps(fs);
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ log_err("Couldn't read bitmaps: %s", error_message(retval));
+ return retval;
+ }
qf_ino = (qtype == USRQUOTA) ? fs->super->s_usr_quota_inum :
fs->super->s_grp_quota_inum;
quota_set_sb_inum(fs, 0, qtype);
@@ -110,7 +118,12 @@ errcode_t quota_remove_inode(ext2_filsys fs, int qtype)
quota_inode_truncate(fs, qf_ino);
ext2fs_mark_super_dirty(fs);
- ext2fs_write_bitmaps(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ retval = ext2fs_write_bitmaps(fs);
+ if (retval) {
+ log_err("Couldn't write bitmaps: %s", error_message(retval));
+ return retval;
+ }
return 0;
}
@@ -122,6 +135,7 @@ static void write_dquots(dict_t *dict, struct quota_handle *qh)
for (n = dict_first(dict); n; n = dict_next(dict, n)) {
dq = dnode_get(n);
if (dq) {
+ print_dquot("write", dq);
dq->dq_h = qh;
update_grace_times(dq);
qh->qh_ops->commit_dquot(dq);
@@ -134,7 +148,7 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
int retval = 0, i;
dict_t *dict;
ext2_filsys fs;
- struct quota_handle *h;
+ struct quota_handle *h = NULL;
int fmt = QFMT_VFS_V1;
if (!qctx)
@@ -143,11 +157,16 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
fs = qctx->fs;
retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
if (retval) {
- log_err("Unable to allocate quota handle", "");
+ log_err("Unable to allocate quota handle: %s",
+ error_message(retval));
goto out;
}
- ext2fs_read_bitmaps(fs);
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ log_err("Couldn't read bitmaps: %s", error_message(retval));
+ goto out;
+ }
for (i = 0; i < MAXQUOTAS; i++) {
if ((qtype != -1) && (i != qtype))
@@ -159,12 +178,12 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
retval = quota_file_create(h, fs, i, fmt);
if (retval < 0) {
- log_err("Cannot initialize io on quotafile", "");
+ log_err("Cannot initialize io on quotafile");
continue;
}
write_dquots(dict, h);
- retval = quota_file_close(h);
+ retval = quota_file_close(qctx, h);
if (retval < 0) {
log_err("Cannot finish IO on new quotafile: %s",
strerror(errno));
@@ -181,7 +200,11 @@ errcode_t quota_write_inode(quota_ctx_t qctx, int qtype)
fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
}
- ext2fs_write_bitmaps(fs);
+ retval = ext2fs_write_bitmaps(fs);
+ if (retval) {
+ log_err("Couldn't write bitmaps: %s", error_message(retval));
+ goto out;
+ }
out:
if (h)
ext2fs_free_mem(&h);
@@ -199,7 +222,12 @@ static int dict_uint_cmp(const void *a, const void *b)
c = VOIDPTR_TO_UINT(a);
d = VOIDPTR_TO_UINT(b);
- return c - d;
+ if (c == d)
+ return 0;
+ else if (c > d)
+ return 1;
+ else
+ return -1;
}
static inline qid_t get_qid(struct ext2_inode *inode, int qtype)
@@ -223,23 +251,26 @@ static void quota_dnode_free(dnode_t *node,
*/
errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
{
- int i, err = 0;
+ errcode_t err;
dict_t *dict;
quota_ctx_t ctx;
+ int i;
err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
if (err) {
- log_err("Failed to allocate quota context", "");
+ log_err("Failed to allocate quota context");
return err;
}
memset(ctx, 0, sizeof(struct quota_ctx));
for (i = 0; i < MAXQUOTAS; i++) {
+ ctx->quota_file[i] = NULL;
if ((qtype != -1) && (i != qtype))
continue;
err = ext2fs_get_mem(sizeof(dict_t), &dict);
if (err) {
- log_err("Failed to allocate dictionary", "");
+ log_err("Failed to allocate dictionary");
+ quota_release_context(&ctx);
return err;
}
ctx->quota_dict[i] = dict;
@@ -254,6 +285,7 @@ errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype)
void quota_release_context(quota_ctx_t *qctx)
{
+ errcode_t err;
dict_t *dict;
int i;
quota_ctx_t ctx;
@@ -269,6 +301,14 @@ void quota_release_context(quota_ctx_t *qctx)
dict_free_nodes(dict);
free(dict);
}
+ if (ctx->quota_file[i]) {
+ err = quota_file_close(ctx, ctx->quota_file[i]);
+ if (err) {
+ log_err("Cannot close quotafile: %s",
+ strerror(errno));
+ ext2fs_free_mem(&ctx->quota_file[i]);
+ }
+ }
}
*qctx = NULL;
free(ctx);
@@ -284,7 +324,7 @@ static struct dquot *get_dq(dict_t *dict, __u32 key)
dq = dnode_get(n);
else {
if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
- log_err("Unable to allocate dquot", "");
+ log_err("Unable to allocate dquot");
return NULL;
}
memset(dq, 0, sizeof(struct dquot));
@@ -399,7 +439,9 @@ errcode_t quota_compute_usage(quota_ctx_t qctx)
}
if (ino == 0)
break;
- if (inode.i_links_count) {
+ if (inode.i_links_count &&
+ (ino == EXT2_ROOT_INO ||
+ ino >= EXT2_FIRST_INODE(fs->super))) {
space = ext2fs_inode_i_blocks(fs, &inode) << 9;
quota_data_add(qctx, &inode, ino, space);
quota_data_inodes(qctx, &inode, ino, +1);
@@ -412,29 +454,49 @@ errcode_t quota_compute_usage(quota_ctx_t qctx)
}
struct scan_dquots_data {
- quota_ctx_t qctx;
- int limit_only; /* read limit only */
+ dict_t *quota_dict;
+ int update_limits; /* update limits from disk */
+ int update_usage;
+ int usage_is_inconsistent;
};
static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
{
- struct scan_dquots_data *scan_data =
- (struct scan_dquots_data *)cb_data;
- quota_ctx_t qctx = scan_data->qctx;
+ struct scan_dquots_data *scan_data = cb_data;
+ dict_t *quota_dict = scan_data->quota_dict;
struct dquot *dq;
- dq = get_dq(qctx->quota_dict[dquot->dq_h->qh_type], dquot->dq_id);
-
+ dq = get_dq(quota_dict, dquot->dq_id);
dq->dq_id = dquot->dq_id;
- if (scan_data->limit_only) {
- dq->dq_dqb.u.v2_mdqb.dqb_off = dquot->dq_dqb.u.v2_mdqb.dqb_off;
+ dq->dq_flags |= DQF_SEEN;
+
+ print_dquot("mem", dq);
+ print_dquot("dsk", dquot);
+
+ /* Check if there is inconsistancy. */
+ if (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
+ dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes) {
+ scan_data->usage_is_inconsistent = 1;
+ fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %d:"
+ "actual (%llu, %llu) != expected (%llu, %llu)\n",
+ dq->dq_id, (long long)dq->dq_dqb.dqb_curspace,
+ (long long)dq->dq_dqb.dqb_curinodes,
+ (long long)dquot->dq_dqb.dqb_curspace,
+ (long long)dquot->dq_dqb.dqb_curinodes);
+ }
+
+ if (scan_data->update_limits) {
dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
- } else {
- dq->dq_dqb = dquot->dq_dqb;
}
+
+ if (scan_data->update_usage) {
+ dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
+ dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
+ }
+
return 0;
}
@@ -442,12 +504,13 @@ static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
* Read all dquots from quota file into memory
*/
static errcode_t quota_read_all_dquots(struct quota_handle *qh,
- quota_ctx_t qctx, int limit_only)
+ quota_ctx_t qctx, int update_limits)
{
struct scan_dquots_data scan_data;
- scan_data.qctx = qctx;
- scan_data.limit_only = limit_only;
+ scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
+ scan_data.update_limits = update_limits;
+ scan_data.update_usage = 0;
return qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
}
@@ -455,6 +518,7 @@ static errcode_t quota_read_all_dquots(struct quota_handle *qh,
/*
* Write all memory dquots into quota file
*/
+#if 0 /* currently unused, but may be useful in the future? */
static errcode_t quota_write_all_dquots(struct quota_handle *qh,
quota_ctx_t qctx)
{
@@ -469,11 +533,12 @@ static errcode_t quota_write_all_dquots(struct quota_handle *qh,
ext2fs_write_bitmaps(qctx->fs);
return 0;
}
+#endif
/*
- * Update usage of in quota file, limits keep unchaged
+ * Updates the in-memory quota limits from the given quota inode.
*/
-errcode_t quota_update_inode(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
+errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
{
struct quota_handle *qh;
errcode_t err;
@@ -483,20 +548,19 @@ errcode_t quota_update_inode(quota_ctx_t qctx, ext2_ino_t qf_ino, int type)
err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
if (err) {
- log_err("Unable to allocate quota handle", "");
+ log_err("Unable to allocate quota handle");
return err;
}
- err = quota_file_open(qh, qctx->fs, qf_ino, type, -1, EXT2_FILE_WRITE);
+ err = quota_file_open(qctx, qh, qf_ino, type, -1, 0);
if (err) {
- log_err("Open quota file failed", "");
+ log_err("Open quota file failed");
goto out;
}
quota_read_all_dquots(qh, qctx, 1);
- quota_write_all_dquots(qh, qctx);
- err = quota_file_close(qh);
+ err = quota_file_close(qctx, qh);
if (err) {
log_err("Cannot finish IO on new quotafile: %s",
strerror(errno));
@@ -507,3 +571,61 @@ out:
ext2fs_free_mem(&qh);
return err;
}
+
+/*
+ * Compares the measured quota in qctx->quota_dict with that in the quota inode
+ * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
+ * set to 1 if the supplied and on-disk quota usage values are not identical.
+ */
+errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
+ int *usage_inconsistent)
+{
+ ext2_filsys fs = qctx->fs;
+ struct quota_handle qh;
+ struct scan_dquots_data scan_data;
+ struct dquot *dq;
+ dnode_t *n;
+ dict_t *dict = qctx->quota_dict[qtype];
+ errcode_t err = 0;
+
+ if (!dict)
+ goto out;
+
+ err = quota_file_open(qctx, &qh, 0, qtype, -1, 0);
+ if (err) {
+ log_err("Open quota file failed");
+ goto out;
+ }
+
+ scan_data.quota_dict = qctx->quota_dict[qtype];
+ scan_data.update_limits = 1;
+ scan_data.update_usage = 0;
+ scan_data.usage_is_inconsistent = 0;
+ err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
+ if (err) {
+ log_err("Error scanning dquots");
+ goto out_close_qh;
+ }
+
+ for (n = dict_first(dict); n; n = dict_next(dict, n)) {
+ dq = dnode_get(n);
+ if (!dq)
+ continue;
+ if ((dq->dq_flags & DQF_SEEN) == 0) {
+ fprintf(stderr, "[QUOTA WARNING] "
+ "Missing quota entry ID %d\n", dq->dq_id);
+ scan_data.usage_is_inconsistent = 1;
+ }
+ }
+ *usage_inconsistent = scan_data.usage_is_inconsistent;
+
+out_close_qh:
+ err = quota_file_close(qctx, &qh);
+ if (err) {
+ log_err("Cannot close quotafile: %s", error_message(errno));
+ if (qh.qh_qf.e2_file)
+ ext2fs_file_close(qh.qh_qf.e2_file);
+ }
+out:
+ return err;
+}
diff --git a/lib/quota/mkquota.h b/lib/quota/mkquota.h
deleted file mode 100644
index a5fa74ba..00000000
--- a/lib/quota/mkquota.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/** mkquota.h
- *
- * Interface to the quota library.
- *
- * The quota library provides interface for creating and updating the quota
- * files and the ext4 superblock fields. It supports the new VFS_V1 quota
- * format. The quota library also provides support for keeping track of quotas
- * in memory.
- * The typical way to use the quota library is as follows:
- * {
- * quota_ctx_t qctx;
- *
- * quota_init_context(&qctx, fs, -1);
- * {
- * quota_compute_usage(qctx, -1);
- * AND/OR
- * quota_data_add/quota_data_sub/quota_data_inodes();
- * }
- * quota_write_inode(qctx, USRQUOTA);
- * quota_write_inode(qctx, GRPQUOTA);
- * quota_release_context(&qctx);
- * }
- *
- * This initial version does not support reading the quota files. This support
- * will be added in near future.
- *
- * Aditya Kali <adityakali@google.com>
- */
-
-#ifndef __QUOTA_QUOTAIO_H__
-#define __QUOTA_QUOTAIO_H__
-
-#include "ext2fs/ext2_fs.h"
-#include "ext2fs/ext2fs.h"
-#include "quota.h"
-#include "../e2fsck/dict.h"
-
-typedef struct quota_ctx *quota_ctx_t;
-
-struct quota_ctx {
- ext2_filsys fs;
- dict_t *quota_dict[MAXQUOTAS];
-};
-
-/* In mkquota.c */
-errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype);
-void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
- int adjust);
-void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
- qsize_t space);
-void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
- qsize_t space);
-errcode_t quota_write_inode(quota_ctx_t qctx, int qtype);
-errcode_t quota_update_inode(quota_ctx_t qctx, ext2_ino_t qf_ino, int type);
-errcode_t quota_compute_usage(quota_ctx_t qctx);
-void quota_release_context(quota_ctx_t *qctx);
-
-errcode_t quota_remove_inode(ext2_filsys fs, int qtype);
-int quota_is_on(ext2_filsys fs, int type);
-int quota_file_exists(ext2_filsys fs, int qtype, int fmt);
-void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype);
-
-#endif /* __QUOTA_QUOTAIO_H__ */
diff --git a/lib/quota/quota.h b/lib/quota/quota.h
deleted file mode 100644
index a943ec61..00000000
--- a/lib/quota/quota.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 1982, 1986 Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Robert Elz at The University of Melbourne.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _LINUX_QUOTA_
-#define _LINUX_QUOTA_
-
-#include <errno.h>
-#include <sys/types.h>
-#include <ext2fs/ext2_types.h>
-
-#define __DQUOT_VERSION__ "dquot_6.5.2"
-
-typedef u_int32_t qid_t; /* Type in which we store ids in memory */
-typedef int64_t qsize_t; /* Type in which we store size limitations */
-
-#define MAXQUOTAS 2
-#define USRQUOTA 0 /* element used for user quotas */
-#define GRPQUOTA 1 /* element used for group quotas */
-
-/*
- * Definitions for the default names of the quotas files.
- */
-#define INITQFNAMES { \
- "user", /* USRQUOTA */ \
- "group", /* GRPQUOTA */ \
- "undefined", \
-};
-
-/*
- * Definitions of magics and versions of current quota files
- */
-#define INITQMAGICS {\
- 0xd9c01f11, /* USRQUOTA */\
- 0xd9c01927 /* GRPQUOTA */\
-}
-
-/* Size of blocks in which are counted size limits in generic utility parts */
-#define QUOTABLOCK_BITS 10
-#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
-
-/* Conversion routines from and to quota blocks */
-#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
-#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
-#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
-
-/*
- * Command definitions for the 'quotactl' system call.
- * The commands are broken into a main command defined below
- * and a subcommand that is used to convey the type of
- * quota that is being manipulated (see above).
- */
-#define SUBCMDMASK 0x00ff
-#define SUBCMDSHIFT 8
-#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
-
-#define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */
-#define Q_QUOTAON 0x800002 /* turn quotas on */
-#define Q_QUOTAOFF 0x800003 /* turn quotas off */
-#define Q_GETFMT 0x800004 /* get quota format used on given filesystem */
-#define Q_GETINFO 0x800005 /* get information about quota files */
-#define Q_SETINFO 0x800006 /* set information about quota files */
-#define Q_GETQUOTA 0x800007 /* get user quota structure */
-#define Q_SETQUOTA 0x800008 /* set user quota structure */
-
-/* Quota format type IDs */
-#define QFMT_VFS_OLD 1
-#define QFMT_VFS_V0 2
-#define QFMT_OCFS2 3
-#define QFMT_VFS_V1 4
-
-/* Size of block in which space limits are passed through the quota
- * interface */
-#define QIF_DQBLKSIZE_BITS 10
-#define QIF_DQBLKSIZE (1 << QIF_DQBLKSIZE_BITS)
-
-/*
- * Structure used for setting quota information about file via quotactl
- * Following flags are used to specify which fields are valid
- */
-#define IIF_BGRACE 1
-#define IIF_IGRACE 2
-#define IIF_FLAGS 4
-#define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
-
-struct if_dqinfo {
- __u64 dqi_bgrace;
- __u64 dqi_igrace;
- __u32 dqi_flags;
- __u32 dqi_valid;
-};
-
-/*
- * Definitions for quota netlink interface
- */
-#define QUOTA_NL_NOWARN 0
-#define QUOTA_NL_IHARDWARN 1 /* Inode hardlimit reached */
-#define QUOTA_NL_ISOFTLONGWARN 2 /* Inode grace time expired */
-#define QUOTA_NL_ISOFTWARN 3 /* Inode softlimit reached */
-#define QUOTA_NL_BHARDWARN 4 /* Block hardlimit reached */
-#define QUOTA_NL_BSOFTLONGWARN 5 /* Block grace time expired */
-#define QUOTA_NL_BSOFTWARN 6 /* Block softlimit reached */
-#define QUOTA_NL_IHARDBELOW 7 /* Usage got below inode hardlimit */
-#define QUOTA_NL_ISOFTBELOW 8 /* Usage got below inode softlimit */
-#define QUOTA_NL_BHARDBELOW 9 /* Usage got below block hardlimit */
-#define QUOTA_NL_BSOFTBELOW 10 /* Usage got below block softlimit */
-
-enum {
- QUOTA_NL_C_UNSPEC,
- QUOTA_NL_C_WARNING,
- __QUOTA_NL_C_MAX,
-};
-#define QUOTA_NL_C_MAX (__QUOTA_NL_C_MAX - 1)
-
-enum {
- QUOTA_NL_A_UNSPEC,
- QUOTA_NL_A_QTYPE,
- QUOTA_NL_A_EXCESS_ID,
- QUOTA_NL_A_WARNING,
- QUOTA_NL_A_DEV_MAJOR,
- QUOTA_NL_A_DEV_MINOR,
- QUOTA_NL_A_CAUSED_ID,
- __QUOTA_NL_A_MAX,
-};
-#define QUOTA_NL_A_MAX (__QUOTA_NL_A_MAX - 1)
-
-#endif /* _QUOTA_ */
diff --git a/lib/quota/quota.pc.in b/lib/quota/quota.pc.in
deleted file mode 100644
index bcc3c441..00000000
--- a/lib/quota/quota.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: quota
-Description: Quota management library
-Version: @E2FSPROGS_VERSION@
-Requires:
-Cflags: -I${includedir}/quota
-Libs: -L${libdir} -lquota
diff --git a/lib/quota/quotaio.c b/lib/quota/quotaio.c
index 481d5f57..65fccaa9 100644
--- a/lib/quota/quotaio.c
+++ b/lib/quota/quotaio.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
@@ -18,7 +19,7 @@
#include "common.h"
#include "quotaio.h"
-static const char extensions[MAXQUOTAS + 2][20] = INITQFNAMES;
+static const char * const extensions[MAXQUOTAS] = {"user", "group"};
static const char * const basenames[] = {
"", /* undefined */
"quota", /* QFMT_VFS_OLD */
@@ -27,18 +28,10 @@ static const char * const basenames[] = {
"aquota" /* QFMT_VFS_V1 */
};
-static const char * const fmtnames[] = {
- "vfsold",
- "vfsv0",
- "vfsv1",
- "rpc",
- "xfs"
-};
-
/* Header in all newer quotafiles */
struct disk_dqheader {
- u_int32_t dqh_magic;
- u_int32_t dqh_version;
+ __u32 dqh_magic;
+ __u32 dqh_version;
} __attribute__ ((packed));
/**
@@ -65,7 +58,6 @@ const char *quota_get_qf_name(int type, int fmt, char *buf)
const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt,
char *path_buf, size_t path_buf_size)
{
- struct stat qf_stat;
char qf_name[QUOTA_NAME_LEN];
if (!mntpt || !path_buf || !path_buf_size)
@@ -106,26 +98,12 @@ void update_grace_times(struct dquot *q)
}
}
-static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
- e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
- blk64_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *private EXT2FS_ATTR((unused)))
-{
- blk64_t block;
-
- block = *blocknr;
- ext2fs_block_alloc_stats2(fs, block, -1);
- return 0;
-}
-
static int compute_num_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
blk64_t ref_block EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
void *private)
{
- blk64_t block;
blk64_t *num_blocks = private;
*num_blocks += 1;
@@ -136,19 +114,22 @@ errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
errcode_t err;
- int i;
if ((err = ext2fs_read_inode(fs, ino, &inode)))
return err;
- inode.i_dtime = fs->now ? fs->now : time(0);
- if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
- return 0;
-
- ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, NULL,
- release_blocks_proc, NULL);
-
- memset(&inode, 0, sizeof(struct ext2_inode));
+ if ((ino == EXT4_USR_QUOTA_INO) || (ino == EXT4_GRP_QUOTA_INO)) {
+ inode.i_dtime = fs->now ? fs->now : time(0);
+ if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+ return 0;
+ err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL);
+ if (err)
+ return err;
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ } else {
+ inode.i_flags &= ~EXT2_IMMUTABLE_FL;
+ }
err = ext2fs_write_inode(fs, ino, &inode);
return err;
}
@@ -216,11 +197,16 @@ static unsigned int quota_read_nomount(struct quota_file *qf,
/*
* Detect quota format and initialize quota IO
*/
-errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
+errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
ext2_ino_t qf_ino, int type, int fmt, int flags)
{
+ ext2_filsys fs = qctx->fs;
ext2_file_t e2_file;
errcode_t err;
+ int allocated_handle = 0;
+
+ if (type >= MAXQUOTAS)
+ return EINVAL;
if (fmt == -1)
fmt = QFMT_VFS_V1;
@@ -229,18 +215,42 @@ errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
if (err)
return err;
+ if (qf_ino == 0) {
+ if (type == USRQUOTA)
+ qf_ino = fs->super->s_usr_quota_inum;
+ else
+ qf_ino = fs->super->s_grp_quota_inum;
+ }
+
log_debug("Opening quota ino=%lu, type=%d", qf_ino, type);
err = ext2fs_file_open(fs, qf_ino, flags, &e2_file);
if (err) {
log_err("ext2fs_file_open failed: %s", error_message(err));
return err;
}
- h->qh_qf.e2_file = e2_file;
+ if (!h) {
+ if (qctx->quota_file[type]) {
+ h = qctx->quota_file[type];
+ if (((flags & EXT2_FILE_WRITE) == 0) ||
+ (h->qh_file_flags & EXT2_FILE_WRITE))
+ return 0;
+ (void) quota_file_close(qctx, h);
+ }
+ err = ext2fs_get_mem(sizeof(struct quota_handle), &h);
+ if (err) {
+ log_err("Unable to allocate quota handle");
+ return err;
+ }
+ allocated_handle = 1;
+ }
+
+ h->qh_qf.e2_file = e2_file;
h->qh_qf.fs = fs;
h->qh_qf.ino = qf_ino;
h->e2fs_write = quota_write_nomount;
h->e2fs_read = quota_read_nomount;
+ h->qh_file_flags = flags;
h->qh_io_flags = 0;
h->qh_type = type;
h->qh_fmt = fmt;
@@ -249,18 +259,23 @@ errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
if (h->qh_ops->check_file &&
(h->qh_ops->check_file(h, type, fmt) == 0)) {
- log_err("qh_ops->check_file failed", "");
- ext2fs_file_close(e2_file);
- return -1;
+ log_err("qh_ops->check_file failed");
+ goto errout;
}
if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
- log_err("qh_ops->init_io failed", "");
- ext2fs_file_close(e2_file);
- return -1;
+ log_err("qh_ops->init_io failed");
+ goto errout;
}
+ if (allocated_handle)
+ qctx->quota_file[type] = h;
return 0;
+errout:
+ ext2fs_file_close(e2_file);
+ if (allocated_handle)
+ ext2fs_free_mem(&h);
+ return -1;
}
static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
@@ -270,7 +285,7 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
err = ext2fs_read_inode(fs, ino, &inode);
if (err) {
- log_err("ex2fs_read_inode failed", "");
+ log_err("ex2fs_read_inode failed");
return err;
}
@@ -322,16 +337,16 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, in
err = quota_inode_init_new(fs, qf_inum);
if (err) {
- log_err("init_new_quota_inode failed", "");
+ log_err("init_new_quota_inode failed");
goto out_err;
}
h->qh_qf.ino = qf_inum;
+ h->qh_file_flags = EXT2_FILE_WRITE | EXT2_FILE_CREATE;
h->e2fs_write = quota_write_nomount;
h->e2fs_read = quota_read_nomount;
log_debug("Creating quota ino=%lu, type=%d", qf_inum, type);
- err = ext2fs_file_open(fs, qf_inum,
- EXT2_FILE_WRITE | EXT2_FILE_CREATE, &e2_file);
+ err = ext2fs_file_open(fs, qf_inum, h->qh_file_flags, &e2_file);
if (err) {
log_err("ext2fs_file_open failed: %d", err);
goto out_err;
@@ -345,7 +360,7 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, int type, in
h->qh_ops = &quotafile_ops_2;
if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
- log_err("qh_ops->new_io failed", "");
+ log_err("qh_ops->new_io failed");
goto out_err1;
}
@@ -364,7 +379,7 @@ out_err:
/*
* Close quotafile and release handle
*/
-errcode_t quota_file_close(struct quota_handle *h)
+errcode_t quota_file_close(quota_ctx_t qctx, struct quota_handle *h)
{
if (h->qh_io_flags & IOFL_INFODIRTY) {
if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
@@ -375,12 +390,18 @@ errcode_t quota_file_close(struct quota_handle *h)
if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
return -1;
if (h->qh_qf.e2_file) {
+ __u64 new_size, size;
+
+ new_size = compute_inode_size(h->qh_qf.fs, h->qh_qf.ino);
ext2fs_file_flush(h->qh_qf.e2_file);
- ext2fs_file_set_size2(h->qh_qf.e2_file,
- compute_inode_size(h->qh_qf.fs, h->qh_qf.ino));
+ if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &size))
+ new_size = 0;
+ if (size != new_size)
+ ext2fs_file_set_size2(h->qh_qf.e2_file, new_size);
ext2fs_file_close(h->qh_qf.e2_file);
}
-
+ if (qctx->quota_file[h->qh_type] == h)
+ ext2fs_free_mem(&qctx->quota_file[h->qh_type]);
return 0;
}
@@ -392,7 +413,7 @@ struct dquot *get_empty_dquot(void)
struct dquot *dquot;
if (ext2fs_get_memzero(sizeof(struct dquot), &dquot)) {
- log_err("Failed to allocate dquot", "");
+ log_err("Failed to allocate dquot");
return NULL;
}
diff --git a/lib/quota/quotaio.h b/lib/quota/quotaio.h
index 91a1ff27..7ca7830e 100644
--- a/lib/quota/quotaio.h
+++ b/lib/quota/quotaio.h
@@ -1,6 +1,32 @@
/** quotaio.h
*
+ * Interface to the quota library.
+ *
+ * The quota library provides interface for creating and updating the quota
+ * files and the ext4 superblock fields. It supports the new VFS_V1 quota
+ * format. The quota library also provides support for keeping track of quotas
+ * in memory.
+ * The typical way to use the quota library is as follows:
+ * {
+ * quota_ctx_t qctx;
+ *
+ * quota_init_context(&qctx, fs, -1);
+ * {
+ * quota_compute_usage(qctx, -1);
+ * AND/OR
+ * quota_data_add/quota_data_sub/quota_data_inodes();
+ * }
+ * quota_write_inode(qctx, USRQUOTA);
+ * quota_write_inode(qctx, GRPQUOTA);
+ * quota_release_context(&qctx);
+ * }
+ *
+ * This initial version does not support reading the quota files. This support
+ * will be added in near future.
+ *
+ * Aditya Kali <adityakali@google.com>
* Header of IO operations for quota utilities
+ *
* Jan Kara <jack@suse.cz>
*/
@@ -11,14 +37,44 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include "ext2fs/ext2_fs.h"
#include "ext2fs/ext2fs.h"
-#include "quota.h"
#include "dqblk_v2.h"
+#include "../e2fsck/dict.h"
+
+typedef int64_t qsize_t; /* Type in which we store size limitations */
+
+#define MAXQUOTAS 2
+#define USRQUOTA 0
+#define GRPQUOTA 1
+
+typedef struct quota_ctx *quota_ctx_t;
+
+struct quota_ctx {
+ ext2_filsys fs;
+ dict_t *quota_dict[MAXQUOTAS];
+ struct quota_handle *quota_file[MAXQUOTAS];
+};
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+/* Size of blocks in which are counted size limits in generic utility parts */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
+/* Quota format type IDs */
+#define QFMT_VFS_OLD 1
+#define QFMT_VFS_V0 2
+#define QFMT_VFS_V1 4
/*
- * Definitions for disk quotas imposed on the average user
- * (big brother finally hits Linux).
- *
* The following constants define the default amount of time given a user
* before the soft limits are treated as hard limits (usually resulting
* in an allocation failure). The timer is started when the user crosses
@@ -27,11 +83,7 @@
#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
-#define IOFL_QUOTAON 0x01 /* Is quota enabled in kernel? */
-#define IOFL_INFODIRTY 0x02 /* Did info change? */
-#define IOFL_RO 0x04 /* Just RO access? */
-#define IOFL_NFS_MIXED_PATHS 0x08 /* Should we trim leading slashes
- from NFSv4 mountpoints? */
+#define IOFL_INFODIRTY 0x01 /* Did info change? */
struct quotafile_ops;
@@ -54,6 +106,7 @@ struct quota_file {
struct quota_handle {
int qh_type; /* Type of quotafile */
int qh_fmt; /* Quotafile format */
+ int qh_file_flags;
int qh_io_flags; /* IO flags for file */
struct quota_file qh_qf;
unsigned int (*e2fs_read)(struct quota_file *qf, ext2_loff_t offset,
@@ -64,19 +117,6 @@ struct quota_handle {
struct util_dqinfo qh_info; /* Generic quotafile info */
};
-/* Statistics gathered from kernel */
-struct util_dqstats {
- u_int32_t lookups;
- u_int32_t drops;
- u_int32_t reads;
- u_int32_t writes;
- u_int32_t cache_hits;
- u_int32_t allocated_dquots;
- u_int32_t free_dquots;
- u_int32_t syncs;
- u_int32_t version;
-};
-
/* Utility quota block */
struct util_dqblk {
qsize_t dqb_ihardlimit;
@@ -101,6 +141,8 @@ struct dquot {
struct util_dqblk dq_dqb; /* Parsed data of dquot */
};
+#define DQF_SEEN 0x0001
+
/* Structure of quotafile operations */
struct quotafile_ops {
/* Check whether quotafile is in our format */
@@ -129,17 +171,9 @@ struct quotafile_ops {
/* This might go into a special header file but that sounds a bit silly... */
extern struct quotafile_ops quotafile_ops_meta;
-static inline void mark_quotafile_info_dirty(struct quota_handle *h)
-{
- h->qh_io_flags |= IOFL_INFODIRTY;
-}
-
-#define QIO_ENABLED(h) ((h)->qh_io_flags & IOFL_QUOTAON)
-#define QIO_RO(h) ((h)->qh_io_flags & IOFL_RO)
-
/* Open existing quotafile of given type (and verify its format) on given
* filesystem. */
-errcode_t quota_file_open(struct quota_handle *h, ext2_filsys fs,
+errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
ext2_ino_t qf_ino, int type, int fmt, int flags);
@@ -148,7 +182,7 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
int type, int fmt);
/* Close quotafile */
-errcode_t quota_file_close(struct quota_handle *h);
+errcode_t quota_file_close(quota_ctx_t qctx, struct quota_handle *h);
/* Get empty quota structure */
struct dquot *get_empty_dquot(void);
@@ -167,4 +201,25 @@ const char *quota_get_qf_name(int type, int fmt, char *buf);
const char *quota_get_qf_path(const char *mntpt, int qtype, int fmt,
char *path_buf, size_t path_buf_size);
+/* In mkquota.c */
+errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs, int qtype);
+void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
+ int adjust);
+void quota_data_add(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
+ qsize_t space);
+void quota_data_sub(quota_ctx_t qctx, struct ext2_inode *inode, ext2_ino_t ino,
+ qsize_t space);
+errcode_t quota_write_inode(quota_ctx_t qctx, int qtype);
+errcode_t quota_update_limits(quota_ctx_t qctx, ext2_ino_t qf_ino, int type);
+errcode_t quota_compute_usage(quota_ctx_t qctx);
+void quota_release_context(quota_ctx_t *qctx);
+
+errcode_t quota_remove_inode(ext2_filsys fs, int qtype);
+int quota_file_exists(ext2_filsys fs, int qtype, int fmt);
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, int qtype);
+errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
+ int *usage_inconsistent);
+
+
+
#endif /* GUARD_QUOTAIO_H */
diff --git a/lib/quota/quotaio_tree.c b/lib/quota/quotaio_tree.c
index 9080e77f..4d7a9ab1 100644
--- a/lib/quota/quotaio_tree.c
+++ b/lib/quota/quotaio_tree.c
@@ -24,7 +24,7 @@ static inline dqbuf_t getdqbuf(void)
{
dqbuf_t buf;
if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
- log_err("Failed to allocate dqbuf", "");
+ log_err("Failed to allocate dqbuf");
return NULL;
}
@@ -53,8 +53,13 @@ static int get_index(qid_t id, int depth)
return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
}
+static inline void mark_quotafile_info_dirty(struct quota_handle *h)
+{
+ h->qh_io_flags |= IOFL_INFODIRTY;
+}
+
/* Read given block */
-static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
+static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
{
int err;
@@ -67,7 +72,7 @@ static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
}
/* Write block */
-static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
+static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
{
int err;
@@ -101,7 +106,7 @@ static int get_free_dqblk(struct quota_handle *h)
if (write_blk(h, info->dqi_blocks, buf) < 0) {
freedqbuf(buf);
log_err("Cannot allocate new quota block "
- "(out of disk space).", "");
+ "(out of disk space).");
return -ENOSPC;
}
blk = info->dqi_blocks++;
@@ -112,7 +117,8 @@ static int get_free_dqblk(struct quota_handle *h)
}
/* Put given block to free list */
-static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
+static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
{
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
@@ -126,11 +132,12 @@ static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
}
/* Remove given block from the list of blocks with free entries */
-static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
+static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
- uint nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
+ unsigned int nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
ext2fs_le32_to_cpu(dh->dqdh_prev_free);
@@ -159,7 +166,8 @@ static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
}
/* Insert given block to the beginning of list with free entries */
-static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
+static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
{
dqbuf_t tmpbuf = getdqbuf();
struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
@@ -183,8 +191,8 @@ static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
}
/* Find space for dquot */
-static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
- int *err)
+static unsigned int find_free_dqentry(struct quota_handle *h,
+ struct dquot *dquot, int *err)
{
int blk, i;
struct qt_disk_dqdbheader *dh;
@@ -230,8 +238,7 @@ static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
ddquot += info->dqi_entry_size;
if (i == qtree_dqstr_in_blk(info))
- log_err("find_free_dqentry(): Data block full but it "
- "shouldn't.", "");
+ log_err("find_free_dqentry(): Data block full unexpectedly.");
write_blk(h, blk, buf);
dquot->dq_dqb.u.v2_mdqb.dqb_off =
@@ -243,12 +250,12 @@ static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot,
/* Insert reference to structure into the trie */
static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
- uint * treeblk, int depth)
+ unsigned int * treeblk, int depth)
{
dqbuf_t buf;
int newson = 0, newact = 0;
- u_int32_t *ref;
- uint newblk;
+ __u32 *ref;
+ unsigned int newblk;
int ret = 0;
log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
@@ -267,7 +274,7 @@ static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
read_blk(h, *treeblk, buf);
}
- ref = (u_int32_t *) buf;
+ ref = (__u32 *) buf;
newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
if (!newblk)
newson = 1;
@@ -297,11 +304,11 @@ out_buf:
/* Wrapper for inserting quota structure into tree */
static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
{
- uint tmp = QT_TREEOFF;
+ unsigned int tmp = QT_TREEOFF;
if (do_insert_tree(h, dquot, &tmp, 0) < 0)
log_err("Cannot write quota (id %u): %s",
- (uint) dquot->dq_id, strerror(errno));
+ (unsigned int) dquot->dq_id, strerror(errno));
}
/* Write dquot to file */
@@ -319,9 +326,10 @@ void qtree_write_dquot(struct dquot *dquot)
if (ret) {
errno = ENOMEM;
log_err("Quota write failed (id %u): %s",
- (uint)dquot->dq_id, strerror(errno));
+ (unsigned int)dquot->dq_id, strerror(errno));
return;
}
+ memset(ddquot, 0, info->dqi_entry_size);
if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
dq_insert_tree(dquot->dq_h, dquot);
@@ -336,13 +344,14 @@ void qtree_write_dquot(struct dquot *dquot)
if (ret > 0)
errno = ENOSPC;
log_err("Quota write failed (id %u): %s",
- (uint)dquot->dq_id, strerror(errno));
+ (unsigned int)dquot->dq_id, strerror(errno));
}
ext2fs_free_mem(&ddquot);
}
/* Free dquot entry in data block */
-static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
+static void free_dqentry(struct quota_handle *h, struct dquot *dquot,
+ unsigned int blk)
{
struct qt_disk_dqdbheader *dh;
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
@@ -354,7 +363,7 @@ static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
log_err("Quota structure has offset to other block (%u) "
"than it should (%u).", blk,
- (uint) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
+ (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
QT_BLKSIZE_BITS));
read_blk(h, blk, buf);
@@ -384,11 +393,11 @@ static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
/* Remove reference to dquot from tree */
static void remove_tree(struct quota_handle *h, struct dquot *dquot,
- uint * blk, int depth)
+ unsigned int * blk, int depth)
{
dqbuf_t buf = getdqbuf();
- uint newblk;
- u_int32_t *ref = (u_int32_t *) buf;
+ unsigned int newblk;
+ __u32 *ref = (__u32 *) buf;
if (!buf)
return;
@@ -424,7 +433,7 @@ static void remove_tree(struct quota_handle *h, struct dquot *dquot,
/* Delete dquot from tree */
void qtree_delete_dquot(struct dquot *dquot)
{
- uint tmp = QT_TREEOFF;
+ unsigned int tmp = QT_TREEOFF;
if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
return;
@@ -433,7 +442,7 @@ void qtree_delete_dquot(struct dquot *dquot)
/* Find entry in block */
static ext2_loff_t find_block_dqentry(struct quota_handle *h,
- struct dquot *dquot, uint blk)
+ struct dquot *dquot, unsigned int blk)
{
struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
dqbuf_t buf = getdqbuf();
@@ -460,11 +469,11 @@ static ext2_loff_t find_block_dqentry(struct quota_handle *h,
/* Find entry for given id in the tree */
static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
struct dquot *dquot,
- uint blk, int depth)
+ unsigned int blk, int depth)
{
dqbuf_t buf = getdqbuf();
ext2_loff_t ret = 0;
- u_int32_t *ref = (u_int32_t *) buf;
+ __u32 *ref = (__u32 *) buf;
if (!buf)
return -ENOMEM;
@@ -536,7 +545,7 @@ struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
-static int report_block(struct dquot *dquot, uint blk, char *bitmap,
+static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
@@ -570,7 +579,7 @@ static int report_block(struct dquot *dquot, uint blk, char *bitmap,
return entries;
}
-static void check_reference(struct quota_handle *h, uint blk)
+static void check_reference(struct quota_handle *h, unsigned int blk)
{
if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
log_err("Illegal reference (%u >= %u) in %s quota file. "
@@ -581,13 +590,14 @@ static void check_reference(struct quota_handle *h, uint blk)
type2name(h->qh_type));
}
-static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
+static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
+ char *bitmap,
int (*process_dquot) (struct dquot *, void *),
void *data)
{
int entries = 0, i;
dqbuf_t buf = getdqbuf();
- u_int32_t *ref = (u_int32_t *) buf;
+ __u32 *ref = (__u32 *) buf;
if (!buf)
return 0;
@@ -616,9 +626,9 @@ static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
return entries;
}
-static uint find_set_bits(char *bmp, int blocks)
+static unsigned int find_set_bits(char *bmp, int blocks)
{
- uint i, used = 0;
+ unsigned int i, used = 0;
for (i = 0; i < blocks; i++)
if (get_bit(bmp, i))
diff --git a/lib/quota/quotaio_tree.h b/lib/quota/quotaio_tree.h
index 37c15cef..be34edbc 100644
--- a/lib/quota/quotaio_tree.h
+++ b/lib/quota/quotaio_tree.h
@@ -6,7 +6,8 @@
#define _LINUX_QUOTA_TREE_H
#include <sys/types.h>
-#include "quota.h"
+
+typedef __u32 qid_t; /* Type in which we store ids in memory */
#define QT_TREEOFF 1 /* Offset of tree in file in blocks */
#define QT_TREEDEPTH 4 /* Depth of quota tree */
@@ -19,13 +20,13 @@
* so there will be space for exactly 21 quota-entries in a block
*/
struct qt_disk_dqdbheader {
- u_int32_t dqdh_next_free; /* Number of next block with free
+ __u32 dqdh_next_free; /* Number of next block with free
* entry */
- u_int32_t dqdh_prev_free; /* Number of previous block with free
+ __u32 dqdh_prev_free; /* Number of previous block with free
* entry */
- u_int16_t dqdh_entries; /* Number of valid entries in block */
- u_int16_t dqdh_pad1;
- u_int32_t dqdh_pad2;
+ __u16 dqdh_entries; /* Number of valid entries in block */
+ __u16 dqdh_pad1;
+ __u32 dqdh_pad2;
} __attribute__ ((packed));
struct dquot;
diff --git a/lib/quota/quotaio_v2.c b/lib/quota/quotaio_v2.c
index e6587068..e7bf29c3 100644
--- a/lib/quota/quotaio_v2.c
+++ b/lib/quota/quotaio_v2.c
@@ -18,8 +18,6 @@
#include "quotaio.h"
#include "quotaio_tree.h"
-typedef char *dqbuf_t;
-
static int v2_check_file(struct quota_handle *h, int type, int fmt);
static int v2_init_io(struct quota_handle *h);
static int v2_new_io(struct quota_handle *h);
@@ -43,9 +41,6 @@ struct quotafile_ops quotafile_ops_2 = {
.report = v2_report,
};
-#define getdqbuf() smalloc(V2_DQBLKSIZE)
-#define freedqbuf(buf) free(buf)
-
/*
* Copy dquot from disk to memory
*/
@@ -140,34 +135,6 @@ static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
}
-/* Convert kernel quotablock format to utility one */
-static inline void v2_kern2utildqblk(struct util_dqblk *u,
- struct v2_kern_dqblk *k)
-{
- u->dqb_ihardlimit = k->dqb_ihardlimit;
- u->dqb_isoftlimit = k->dqb_isoftlimit;
- u->dqb_bhardlimit = k->dqb_bhardlimit;
- u->dqb_bsoftlimit = k->dqb_bsoftlimit;
- u->dqb_curinodes = k->dqb_curinodes;
- u->dqb_curspace = k->dqb_curspace;
- u->dqb_itime = k->dqb_itime;
- u->dqb_btime = k->dqb_btime;
-}
-
-/* Convert utility quotablock format to kernel one */
-static inline void v2_util2kerndqblk(struct v2_kern_dqblk *k,
- struct util_dqblk *u)
-{
- k->dqb_ihardlimit = u->dqb_ihardlimit;
- k->dqb_isoftlimit = u->dqb_isoftlimit;
- k->dqb_bhardlimit = u->dqb_bhardlimit;
- k->dqb_bsoftlimit = u->dqb_bsoftlimit;
- k->dqb_curinodes = u->dqb_curinodes;
- k->dqb_curspace = u->dqb_curspace;
- k->dqb_itime = u->dqb_itime;
- k->dqb_btime = u->dqb_btime;
-}
-
static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh)
{
if (h->e2fs_read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) !=
@@ -184,27 +151,19 @@ static int v2_check_file(struct quota_handle *h, int type, int fmt)
{
struct v2_disk_dqheader dqh;
int file_magics[] = INITQMAGICS;
- int known_versions[] = INIT_V2_VERSIONS;
- int version;
- if (!v2_read_header(h, &dqh))
+ if (fmt != QFMT_VFS_V1)
return 0;
- if (fmt == QFMT_VFS_V0)
- version = 0;
- else if (fmt == QFMT_VFS_V1)
- version = 1;
- else
+
+ if (!v2_read_header(h, &dqh))
return 0;
if (ext2fs_le32_to_cpu(dqh.dqh_magic) != file_magics[type]) {
if (ext2fs_be32_to_cpu(dqh.dqh_magic) == file_magics[type])
- log_err("Your quota file is stored in wrong "
- "endianity.", "");
+ log_err("Your quota file is stored in wrong endianity");
return 0;
}
- if (ext2fs_le32_to_cpu(dqh.dqh_version) > known_versions[type])
- return 0;
- if (version != ext2fs_le32_to_cpu(dqh.dqh_version))
+ if (V2_VERSION != ext2fs_le32_to_cpu(dqh.dqh_version))
return 0;
return 1;
}
@@ -236,14 +195,13 @@ static int v2_new_io(struct quota_handle *h)
int file_magics[] = INITQMAGICS;
struct v2_disk_dqheader ddqheader;
struct v2_disk_dqinfo ddqinfo;
- int version = 1;
if (h->qh_fmt != QFMT_VFS_V1)
return -1;
/* Write basic quota header */
ddqheader.dqh_magic = ext2fs_cpu_to_le32(file_magics[h->qh_type]);
- ddqheader.dqh_version = ext2fs_cpu_to_le32(version);
+ ddqheader.dqh_version = ext2fs_cpu_to_le32(V2_VERSION);
if (h->e2fs_write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) !=
sizeof(ddqheader))
return -1;
@@ -320,6 +278,6 @@ static int v2_scan_dquots(struct quota_handle *h,
*/
static int v2_report(struct quota_handle *h, int verbose)
{
- log_err("Not Implemented.", "");
+ log_err("Not Implemented.");
return -1;
}
diff --git a/lib/quota/quotaio_v2.h b/lib/quota/quotaio_v2.h
index 072e36fc..646c698b 100644
--- a/lib/quota/quotaio_v2.h
+++ b/lib/quota/quotaio_v2.h
@@ -8,15 +8,16 @@
#define GUARD_QUOTAIO_V2_H
#include <sys/types.h>
-#include "quota.h"
+#include "quotaio.h"
/* Offset of info header in file */
#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader)
-#define INIT_V2_VERSIONS { 1, 1}
+/* Supported version of quota-tree format */
+#define V2_VERSION 1
struct v2_disk_dqheader {
- u_int32_t dqh_magic; /* Magic number identifying file */
- u_int32_t dqh_version; /* File version */
+ __u32 dqh_magic; /* Magic number identifying file */
+ __u32 dqh_version; /* File version */
} __attribute__ ((packed));
/* Flags for version specific files */
@@ -24,80 +25,30 @@ struct v2_disk_dqheader {
/* Header with type and version specific information */
struct v2_disk_dqinfo {
- u_int32_t dqi_bgrace; /* Time before block soft limit becomes
+ __u32 dqi_bgrace; /* Time before block soft limit becomes
* hard limit */
- u_int32_t dqi_igrace; /* Time before inode soft limit becomes
+ __u32 dqi_igrace; /* Time before inode soft limit becomes
* hard limit */
- u_int32_t dqi_flags; /* Flags for quotafile (DQF_*) */
- u_int32_t dqi_blocks; /* Number of blocks in file */
- u_int32_t dqi_free_blk; /* Number of first free block in the list */
- u_int32_t dqi_free_entry; /* Number of block with at least one
+ __u32 dqi_flags; /* Flags for quotafile (DQF_*) */
+ __u32 dqi_blocks; /* Number of blocks in file */
+ __u32 dqi_free_blk; /* Number of first free block in the list */
+ __u32 dqi_free_entry; /* Number of block with at least one
* free entry */
} __attribute__ ((packed));
-/* Structure of quota for one user on disk */
-struct v2r0_disk_dqblk {
- u_int32_t dqb_id; /* id this quota applies to */
- u_int32_t dqb_ihardlimit; /* absolute limit on allocated inodes */
- u_int32_t dqb_isoftlimit; /* preferred inode limit */
- u_int32_t dqb_curinodes; /* current # allocated inodes */
- u_int32_t dqb_bhardlimit; /* absolute limit on disk space
- * (in QUOTABLOCK_SIZE) */
- u_int32_t dqb_bsoftlimit; /* preferred limit on disk space
- * (in QUOTABLOCK_SIZE) */
- u_int64_t dqb_curspace; /* current space occupied (in bytes) */
- u_int64_t dqb_btime; /* time limit for excessive disk use */
- u_int64_t dqb_itime; /* time limit for excessive inode use */
-} __attribute__ ((packed));
-
struct v2r1_disk_dqblk {
- u_int32_t dqb_id; /* id this quota applies to */
- u_int32_t dqb_pad;
- u_int64_t dqb_ihardlimit; /* absolute limit on allocated inodes */
- u_int64_t dqb_isoftlimit; /* preferred inode limit */
- u_int64_t dqb_curinodes; /* current # allocated inodes */
- u_int64_t dqb_bhardlimit; /* absolute limit on disk space
+ __u32 dqb_id; /* id this quota applies to */
+ __u32 dqb_pad;
+ __u64 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __u64 dqb_isoftlimit; /* preferred inode limit */
+ __u64 dqb_curinodes; /* current # allocated inodes */
+ __u64 dqb_bhardlimit; /* absolute limit on disk space
* (in QUOTABLOCK_SIZE) */
- u_int64_t dqb_bsoftlimit; /* preferred limit on disk space
+ __u64 dqb_bsoftlimit; /* preferred limit on disk space
* (in QUOTABLOCK_SIZE) */
- u_int64_t dqb_curspace; /* current space occupied (in bytes) */
- u_int64_t dqb_btime; /* time limit for excessive disk use */
- u_int64_t dqb_itime; /* time limit for excessive inode use */
+ __u64 dqb_curspace; /* current space occupied (in bytes) */
+ __u64 dqb_btime; /* time limit for excessive disk use */
+ __u64 dqb_itime; /* time limit for excessive inode use */
} __attribute__ ((packed));
-/* Structure of quota for communication with kernel */
-struct v2_kern_dqblk {
- unsigned int dqb_ihardlimit;
- unsigned int dqb_isoftlimit;
- unsigned int dqb_curinodes;
- unsigned int dqb_bhardlimit;
- unsigned int dqb_bsoftlimit;
- qsize_t dqb_curspace;
- time_t dqb_btime;
- time_t dqb_itime;
-};
-
-/* Structure of quotafile info for communication with kernel (obsolete) */
-struct v2_kern_dqinfo {
- unsigned int dqi_bgrace;
- unsigned int dqi_igrace;
- unsigned int dqi_flags;
- unsigned int dqi_blocks;
- unsigned int dqi_free_blk;
- unsigned int dqi_free_entry;
-};
-
-/* Structure with gathered statistics from kernel */
-struct v2_dqstats {
- u_int32_t lookups;
- u_int32_t drops;
- u_int32_t reads;
- u_int32_t writes;
- u_int32_t cache_hits;
- u_int32_t allocated_dquots;
- u_int32_t free_dquots;
- u_int32_t syncs;
- u_int32_t version;
-};
-
#endif
diff --git a/lib/ss/Makefile.in b/lib/ss/Makefile.in
index 19413cc2..8f85bf1b 100644
--- a/lib/ss/Makefile.in
+++ b/lib/ss/Makefile.in
@@ -20,7 +20,7 @@ ELF_SO_VERSION = 2
ELF_IMAGE = libss
ELF_MYDIR = ss
ELF_INSTALL_DIR = $(root_libdir)
-ELF_OTHER_LIBS = -L../.. -lcom_err $(DLOPEN_LIB)
+ELF_OTHER_LIBS = -lcom_err $(DLOPEN_LIB)
BSDLIB_VERSION = 1.0
BSDLIB_IMAGE = libss
@@ -34,6 +34,7 @@ MK_CMDS=_SS_DIR_OVERRIDE=. ./mk_cmds
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $<
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -DSHARED_ELF_LIB -fPIC -o elfshared/$*.o -c $<
@@ -127,7 +128,7 @@ installdirs::
$(E) " MKINSTALLDIRS $(libdir) $(includedir)/ss $(datadir)/ss $(bindir)"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
$(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss \
- $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(bindir) $(DESTDIR)$(pkgconfigdir)
install:: libss.a $(INSTALL_HFILES) installdirs ss_err.h mk_cmds ss.pc
$(E) " INSTALL_DATA $(DESTDIR)$(libdir)/libss.a"
@@ -149,19 +150,19 @@ install:: libss.a $(INSTALL_HFILES) installdirs ss_err.h mk_cmds ss.pc
$(Q) $(INSTALL) mk_cmds $(DESTDIR)$(bindir)/mk_cmds
$(E) " INSTALL_DATA $(man1dir)/mk_cmds.1"
$(Q) $(INSTALL_DATA) $(srcdir)/mk_cmds.1 $(DESTDIR)$(man1dir)/mk_cmds.1
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/ss.pc"
- $(Q) $(INSTALL_DATA) ss.pc $(DESTDIR)$(libdir)/pkgconfig/ss.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/ss.pc"
+ $(Q) $(INSTALL_DATA) ss.pc $(DESTDIR)$(pkgconfigdir)/ss.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libss.a $(DESTDIR)$(bindir)/mk_cmds \
- $(DESTDIR)$(libdir)/pkgconfig/ss.pc \
+ $(DESTDIR)$(pkgconfigdir)/ss.pc \
$(DESTDIR)$(man1dir)/mk_cmds.1
$(RM) -rf $(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss
test_ss: test_ss.o test_cmd.o $(DEPLIBSS) $(DEPLIBCOM_ERR)
$(E) " LD $@"
$(Q) $(CC) -o $@ test_ss.o test_cmd.o $(ALL_CFLAGS) \
- $(LIBSS) $(LIBCOM_ERR)
+ $(LIBSS) $(LIBCOM_ERR) $(SYSLIBS)
check:: all test_ss
$(E) " RUN TEST test_ss"
diff --git a/lib/ss/error.c b/lib/ss/error.c
index 781ba82d..8d345a9f 100644
--- a/lib/ss/error.c
+++ b/lib/ss/error.c
@@ -20,8 +20,7 @@
#include <stdarg.h>
-char * ss_name(sci_idx)
- int sci_idx;
+char *ss_name(int sci_idx)
{
register char *ret_val;
register ss_data *infop;
@@ -70,10 +69,7 @@ void ss_error (int sci_idx, long code, const char * fmt, ...)
va_end(pvar);
}
-void ss_perror (sci_idx, code, msg) /* for compatibility */
- int sci_idx;
- long code;
- char const *msg;
+void ss_perror(int sci_idx, long code, char const *msg) /* for compatibility */
{
ss_error (sci_idx, code, "%s", msg);
}
diff --git a/lib/ss/execute_cmd.c b/lib/ss/execute_cmd.c
index 53c10c9c..d443a468 100644
--- a/lib/ss/execute_cmd.c
+++ b/lib/ss/execute_cmd.c
@@ -82,11 +82,8 @@ static struct _ss_request_entry * get_request (tbl, idx)
* Notes:
*/
-static int check_request_table (rqtbl, argc, argv, sci_idx)
- register ss_request_table *rqtbl;
- int argc;
- char *argv[];
- int sci_idx;
+static int check_request_table(register ss_request_table *rqtbl, int argc,
+ char *argv[], int sci_idx)
{
#ifdef __SABER__
struct _ss_request_entry *request;
@@ -135,10 +132,7 @@ static int check_request_table (rqtbl, argc, argv, sci_idx)
* Notes:
*/
-static int really_execute_command (sci_idx, argc, argv)
- int sci_idx;
- int argc;
- char **argv[];
+static int really_execute_command(int sci_idx, int argc, char **argv[])
{
register ss_request_table **rqtbl;
register ss_data *info;
@@ -168,9 +162,7 @@ static int really_execute_command (sci_idx, argc, argv)
* Notes:
*/
-int ss_execute_command(sci_idx, argv)
- int sci_idx;
- register char *argv[];
+int ss_execute_command(int sci_idx, register char *argv[])
{
register int i, argc;
char **argp;
@@ -202,9 +194,7 @@ int ss_execute_command(sci_idx, argv)
* Notes:
*/
-int ss_execute_line (sci_idx, line_ptr)
- int sci_idx;
- char *line_ptr;
+int ss_execute_line(int sci_idx, char *line_ptr)
{
char **argv;
int argc, ret;
diff --git a/lib/ss/help.c b/lib/ss/help.c
index 6a61e70a..5204401b 100644
--- a/lib/ss/help.c
+++ b/lib/ss/help.c
@@ -36,11 +36,7 @@ extern int errno;
#endif
#include "ss_internal.h"
-void ss_help (argc, argv, sci_idx, info_ptr)
- int argc;
- char const * const *argv;
- int sci_idx;
- pointer info_ptr;
+void ss_help(int argc, char const * const *argv, int sci_idx, pointer info_ptr)
{
char *buffer;
char const *request_name;
@@ -110,13 +106,14 @@ void ss_help (argc, argv, sci_idx, info_ptr)
switch (child = fork()) {
case -1:
ss_perror(sci_idx, errno, "Can't fork for pager");
+ (void) close(fd);
return;
case 0:
(void) dup2(fd, 0); /* put file on stdin */
ss_page_stdin();
default:
(void) close(fd); /* what can we do if it fails? */
- while (wait(0) != child) {
+ while (wait(NULL) != child) {
/* do nothing if wrong pid */
};
}
@@ -128,10 +125,7 @@ void ss_help (argc, argv, sci_idx, info_ptr)
#include <dirent.h>
#endif
-void ss_add_info_dir(sci_idx, info_dir, code_ptr)
- int sci_idx;
- char *info_dir;
- int *code_ptr;
+void ss_add_info_dir(int sci_idx, char *info_dir, int *code_ptr)
{
register ss_data *info;
DIR *d;
@@ -165,10 +159,7 @@ void ss_add_info_dir(sci_idx, info_dir, code_ptr)
*code_ptr = 0;
}
-void ss_delete_info_dir(sci_idx, info_dir, code_ptr)
- int sci_idx;
- char *info_dir;
- int *code_ptr;
+void ss_delete_info_dir(int sci_idx, char *info_dir, int *code_ptr)
{
register char **i_d;
register char **info_dirs;
diff --git a/lib/ss/invocation.c b/lib/ss/invocation.c
index a7110500..457e7a2c 100644
--- a/lib/ss/invocation.c
+++ b/lib/ss/invocation.c
@@ -20,13 +20,11 @@
#ifdef HAVE_DLOPEN
#include <dlfcn.h>
#endif
+#include <errno.h>
-int ss_create_invocation(subsystem_name, version_string, info_ptr,
- request_table_ptr, code_ptr)
- const char *subsystem_name, *version_string;
- void *info_ptr;
- ss_request_table *request_table_ptr;
- int *code_ptr;
+int ss_create_invocation(const char *subsystem_name, const char *version_string,
+ void *info_ptr, ss_request_table *request_table_ptr,
+ int *code_ptr)
{
register int sci_idx;
register ss_data *new_table;
@@ -46,6 +44,11 @@ int ss_create_invocation(subsystem_name, version_string, info_ptr,
;
table = (ss_data **) realloc((char *)table,
((unsigned)sci_idx+2)*size);
+ if (table == NULL) {
+ *code_ptr = ENOMEM;
+ free(new_table);
+ return 0;
+ }
table[sci_idx+1] = (ss_data *) NULL;
table[sci_idx] = new_table;
@@ -85,8 +88,7 @@ int ss_create_invocation(subsystem_name, version_string, info_ptr,
}
void
-ss_delete_invocation(sci_idx)
- int sci_idx;
+ss_delete_invocation(int sci_idx)
{
register ss_data *t;
int ignored_code;
diff --git a/lib/ss/list_rqs.c b/lib/ss/list_rqs.c
index 38e6aef0..021a3835 100644
--- a/lib/ss/list_rqs.c
+++ b/lib/ss/list_rqs.c
@@ -18,20 +18,15 @@
typedef void sigret_t;
-static char const twentyfive_spaces[26] =
- " ";
-static char const NL[2] = "\n";
-
void ss_list_requests(int argc __SS_ATTR((unused)),
const char * const *argv __SS_ATTR((unused)),
int sci_idx, void *infop __SS_ATTR((unused)))
{
ss_request_entry *entry;
char const * const *name;
- int spacing;
+ int i, spacing;
ss_request_table **table;
- char buffer[BUFSIZ];
FILE *output;
int fd;
sigset_t omask, igmask;
@@ -45,6 +40,11 @@ void ss_list_requests(int argc __SS_ATTR((unused)),
sigprocmask(SIG_BLOCK, &igmask, &omask);
func = signal(SIGINT, SIG_IGN);
fd = ss_pager_create();
+ if (fd < 0) {
+ perror("ss_pager_create");
+ (void) signal(SIGINT, func);
+ return;
+ }
output = fdopen(fd, "w");
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
@@ -55,27 +55,24 @@ void ss_list_requests(int argc __SS_ATTR((unused)),
entry = (*table)->requests;
for (; entry->command_names; entry++) {
spacing = -2;
- buffer[0] = '\0';
if (entry->flags & SS_OPT_DONT_LIST)
continue;
for (name = entry->command_names; *name; name++) {
int len = strlen(*name);
- strncat(buffer, *name, len);
+ fputs(*name, output);
spacing += len + 2;
if (name[1]) {
- strcat(buffer, ", ");
+ fputs(", ", output);
}
}
if (spacing > 23) {
- strcat(buffer, NL);
- fputs(buffer, output);
+ fputc('\n', output);
spacing = 0;
- buffer[0] = '\0';
}
- strncat(buffer, twentyfive_spaces, 25-spacing);
- strcat(buffer, entry->info_string);
- strcat(buffer, NL);
- fputs(buffer, output);
+ for (i = 0; i < 25 - spacing; i++)
+ fputc(' ', output);
+ fputs(entry->info_string, output);
+ fputc('\n', output);
}
}
fclose(output);
diff --git a/lib/ss/pager.c b/lib/ss/pager.c
index 2b3a4665..330249ec 100644
--- a/lib/ss/pager.c
+++ b/lib/ss/pager.c
@@ -15,6 +15,9 @@
*/
#include "config.h"
+#if HAVE_SECURE_GETENV
+#define _GNU_SOURCE
+#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -39,7 +42,6 @@ extern int errno;
#endif
static char MORE[] = "more";
-extern char *_ss_pager_name;
extern char *getenv PROTOTYPE((const char *));
char *ss_safe_getenv(const char *arg)
@@ -56,7 +58,9 @@ char *ss_safe_getenv(const char *arg)
#endif
#endif
-#ifdef HAVE___SECURE_GETENV
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(arg);
#else
return getenv(arg);
@@ -126,7 +130,7 @@ static int write_all(int fd, char *buf, size_t count)
return c;
}
-void ss_page_stdin()
+void ss_page_stdin(void)
{
int i;
sigset_t mask;
diff --git a/lib/ss/parse.c b/lib/ss/parse.c
index b70ad165..7c6c6791 100644
--- a/lib/ss/parse.c
+++ b/lib/ss/parse.c
@@ -43,12 +43,9 @@ enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING };
#define NEW_ARGV(old,n) (char **)realloc((char *)old,\
(unsigned)(n+2)*sizeof(char*))
-char **ss_parse (sci_idx, line_ptr, argc_ptr)
- int sci_idx;
- register char *line_ptr;
- int *argc_ptr;
+char **ss_parse(int sci_idx, register char *line_ptr, int *argc_ptr)
{
- register char **argv, *cp;
+ register char **argv, **new_argv, *cp;
register int argc;
register enum parse_mode parse_mode;
@@ -81,7 +78,13 @@ char **ss_parse (sci_idx, line_ptr, argc_ptr)
/* go to quoted-string mode */
parse_mode = QUOTED_STRING;
cp = line_ptr++;
- argv = NEW_ARGV (argv, argc);
+ new_argv = NEW_ARGV (argv, argc);
+ if (new_argv == NULL) {
+ free(argv);
+ *argc_ptr = 0;
+ return NULL;
+ }
+ argv = new_argv;
argv[argc++] = cp;
argv[argc] = NULL;
}
@@ -89,7 +92,13 @@ char **ss_parse (sci_idx, line_ptr, argc_ptr)
/* random-token mode */
parse_mode = TOKEN;
cp = line_ptr;
- argv = NEW_ARGV (argv, argc);
+ new_argv = NEW_ARGV (argv, argc);
+ if (new_argv == NULL) {
+ free(argv);
+ *argc_ptr = 0;
+ return NULL;
+ }
+ argv = new_argv;
argv[argc++] = line_ptr;
argv[argc] = NULL;
}
diff --git a/lib/ss/request_tbl.c b/lib/ss/request_tbl.c
index b0b6f959..135cb28b 100644
--- a/lib/ss/request_tbl.c
+++ b/lib/ss/request_tbl.c
@@ -20,11 +20,7 @@
#define ssrt ss_request_table /* for some readable code... */
-void ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr)
- int sci_idx;
- ssrt *rqtbl_ptr;
- int position; /* 1 -> becomes second... */
- int *code_ptr;
+void ss_add_request_table(int sci_idx, ssrt *rqtbl_ptr, int position, int *code_ptr)
{
register ss_data *info;
register int i, size;
@@ -35,7 +31,7 @@ void ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr)
;
/* size == C subscript of NULL == #elements */
size += 2; /* new element, and NULL */
- t = (ssrt **)realloc(info->rqt_tables, (unsigned)size*sizeof(ssrt));
+ t = (ssrt **)realloc(info->rqt_tables, (unsigned)size*sizeof(ssrt *));
if (t == (ssrt **)NULL) {
*code_ptr = errno;
return;
@@ -53,10 +49,7 @@ void ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr)
*code_ptr = 0;
}
-void ss_delete_request_table(sci_idx, rqtbl_ptr, code_ptr)
- int sci_idx;
- ssrt *rqtbl_ptr;
- int *code_ptr;
+void ss_delete_request_table(int sci_idx, ssrt *rqtbl_ptr, int *code_ptr)
{
register ss_data *info;
register ssrt **rt1, **rt2;
diff --git a/lib/ss/ss.pc.in b/lib/ss/ss.pc.in
index cf893611..5c9eccb1 100644
--- a/lib/ss/ss.pc.in
+++ b/lib/ss/ss.pc.in
@@ -7,6 +7,6 @@ Name: ss
Description: Subsystem command parsing library
Version: @E2FSPROGS_VERSION@
Requires.private: com_err
-Cflags: -I${includedir}/ss
+Cflags: -I${includedir}/ss -I${includedir}
Libs: -L${libdir} -lss
Libs.private: @DLOPEN_LIB@
diff --git a/lib/ss/ss_internal.h b/lib/ss/ss_internal.h
index 15d618ee..19a6be7a 100644
--- a/lib/ss/ss_internal.h
+++ b/lib/ss/ss_internal.h
@@ -94,6 +94,7 @@ char **ss_rl_completion(const char *text, int start, int end);
extern ss_data **_ss_table;
extern char *ss_et_msgs[];
+extern char *_ss_pager_name;
#ifdef USE_SIGPROCMASK
/* fake sigmask, sigblock, sigsetmask */
diff --git a/lib/uuid/Makefile.in b/lib/uuid/Makefile.in
index b85c7203..a23e6520 100644
--- a/lib/uuid/Makefile.in
+++ b/lib/uuid/Makefile.in
@@ -62,6 +62,7 @@ BSDLIB_INSTALL_DIR = $(root_libdir)
.c.o:
$(E) " CC $<"
$(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
@CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
@@ -76,7 +77,7 @@ uuid.h: $(srcdir)/uuid.h.in
$(top_builddir)/lib/uuid/uuid_types.h: $(srcdir)/uuid_types.h.in $(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=$(my_dir)/uuid_types.h ./config.status
-tst_uuid.o: $(srcdir)/tst_uuid.c
+tst_uuid.o: $(srcdir)/tst_uuid.c uuid.h
$(E) " CC $@"
$(Q) $(CC) $(ALL_CFLAGS) -c $(srcdir)/tst_uuid.c -o tst_uuid.o
@@ -86,8 +87,8 @@ tst_uuid: tst_uuid.o $(DEPSTATIC_LIBUUID)
uuid_time: $(srcdir)/uuid_time.c $(DEPLIBUUID)
$(E) " LD $@"
- $(Q) $(CC) $(ALL_CFLAGS) -DDEBUG -o uuid_time $(srcdir)/uuid_time.c \
- $(LIBUUID)
+ $(Q) $(CC) $(ALL_CFLAGS) $(LDFLAGS) -DDEBUG -o uuid_time \
+ $(srcdir)/uuid_time.c $(LIBUUID)
uuid.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid.3.in
$(E) " SUBST $@"
@@ -133,7 +134,7 @@ installdirs::
$(E) " MKINSTALLDIRS $(libdir) $(includedir)/uuid $(man3dir)"
$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \
$(DESTDIR)$(includedir)/uuid $(DESTDIR)$(man3dir) \
- $(DESTDIR)$(libdir)/pkgconfig
+ $(DESTDIR)$(pkgconfigdir)
install:: all installdirs
$(E) " INSTALL_DATA $(libdir)/libuuid.a"
@@ -155,12 +156,12 @@ install:: all installdirs
$(E) " LINK $(man3dir)/uuid_generate_time.3"
$(Q) (cd $(DESTDIR)$(man3dir); \
$(LN) $(LINK_INSTALL_FLAGS) uuid_generate.3 uuid_generate_time.3)
- $(E) " INSTALL_DATA $(libdir)/pkgconfig/uuid.pc"
- $(Q) $(INSTALL_DATA) uuid.pc $(DESTDIR)$(libdir)/pkgconfig/uuid.pc
+ $(E) " INSTALL_DATA $(pkgconfigdir)/uuid.pc"
+ $(Q) $(INSTALL_DATA) uuid.pc $(DESTDIR)$(pkgconfigdir)/uuid.pc
uninstall::
$(RM) -f $(DESTDIR)$(libdir)/libuuid.a \
- $(DESTDIR)$(libdir)/pkgconfig/uuid.pc
+ $(DESTDIR)$(pkgconfigdir)/uuid.pc
for i in $(SMANPAGES); do \
$(RM) -f $(DESTDIR)$(man3dir)/$$i; \
done
diff --git a/lib/uuid/gen_uuid.c b/lib/uuid/gen_uuid.c
index 42d7563d..22b45134 100644
--- a/lib/uuid/gen_uuid.c
+++ b/lib/uuid/gen_uuid.c
@@ -176,8 +176,7 @@ static void get_random_bytes(void *buf, int nbytes)
{
int i, n = nbytes, fd = get_random_fd();
int lose_counter = 0;
- unsigned char *cp = (unsigned char *) buf;
- unsigned short tmp_seed[3];
+ unsigned char *cp = buf;
if (fd >= 0) {
while (n > 0) {
@@ -200,12 +199,16 @@ static void get_random_bytes(void *buf, int nbytes)
for (cp = buf, i = 0; i < nbytes; i++)
*cp++ ^= (rand() >> 7) & 0xFF;
#ifdef DO_JRAND_MIX
- memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
- jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
- for (cp = buf, i = 0; i < nbytes; i++)
- *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
- memcpy(jrand_seed, tmp_seed,
- sizeof(jrand_seed)-sizeof(unsigned short));
+ {
+ unsigned short tmp_seed[3];
+
+ memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
+ jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+ memcpy(jrand_seed, tmp_seed,
+ sizeof(jrand_seed) - sizeof(unsigned short));
+ }
#endif
return;
@@ -323,10 +326,12 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
state_fd = open("/var/lib/libuuid/clock.txt",
O_RDWR|O_CREAT, 0660);
(void) umask(save_umask);
- state_f = fdopen(state_fd, "r+");
- if (!state_f) {
- close(state_fd);
- state_fd = -1;
+ if (state_fd >= 0) {
+ state_f = fdopen(state_fd, "r+");
+ if (!state_f) {
+ close(state_fd);
+ state_fd = -1;
+ }
}
}
fl.l_type = F_WRLCK;
@@ -340,7 +345,6 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
if ((errno == EAGAIN) || (errno == EINTR))
continue;
fclose(state_f);
- close(state_fd);
state_fd = -1;
break;
}
@@ -398,9 +402,10 @@ try_again:
if (state_fd > 0) {
rewind(state_f);
- len = fprintf(state_f,
+ len = fprintf(state_f,
"clock: %04x tv: %016lu %08lu adj: %08d\n",
- clock_seq, last.tv_sec, last.tv_usec, adjustment);
+ clock_seq, last.tv_sec, (long)last.tv_usec,
+ adjustment);
fflush(state_f);
if (ftruncate(state_fd, len) < 0) {
fprintf(state_f, " \n");
@@ -408,7 +413,10 @@ try_again:
}
rewind(state_f);
fl.l_type = F_UNLCK;
- fcntl(state_fd, F_SETLK, &fl);
+ if (fcntl(state_fd, F_SETLK, &fl) < 0) {
+ fclose(state_f);
+ state_fd = -1;
+ }
}
*clock_high = clock_reg >> 32;
diff --git a/lib/uuid/tst_uuid.c b/lib/uuid/tst_uuid.c
index 5884ad92..88d928fe 100644
--- a/lib/uuid/tst_uuid.c
+++ b/lib/uuid/tst_uuid.c
@@ -74,7 +74,7 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
uuid_t buf, tst;
char str[100];
struct timeval tv;
- time_t time_reg;
+ time_t time_reg, time_gen;
unsigned char *cp;
int i;
int failed = 0;
@@ -104,7 +104,8 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
printf("%02x", *cp++);
}
printf("\n");
- type = uuid_type(buf); variant = uuid_variant(buf);
+ type = uuid_type(buf);
+ variant = uuid_variant(buf);
printf("UUID type = %d, UUID variant = %d\n", type, variant);
if (variant != UUID_VARIANT_DCE) {
printf("Incorrect UUID Variant; was expecting DCE!\n");
@@ -117,6 +118,7 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
}
printf("\n");
+ time_gen = time(0);
uuid_generate_time(buf);
uuid_unparse(buf, str);
printf("UUID string = %s\n", str);
@@ -125,7 +127,8 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
printf("%02x", *cp++);
}
printf("\n");
- type = uuid_type(buf); variant = uuid_variant(buf);
+ type = uuid_type(buf);
+ variant = uuid_variant(buf);
printf("UUID type = %d, UUID variant = %d\n", type, variant);
if (variant != UUID_VARIANT_DCE) {
printf("Incorrect UUID Variant; was expecting DCE!\n");
@@ -136,15 +139,28 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
"1 (time-based type)!\\n");
failed++;
}
+
tv.tv_sec = 0;
tv.tv_usec = 0;
time_reg = uuid_time(buf, &tv);
- printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
- ctime(&time_reg));
- uuid_parse(str, tst);
- if (!uuid_compare(buf, tst))
+ printf("UUID generated at %lu reports %lu (%ld.%ld)\n",
+ time_gen, time_reg, tv.tv_sec, (long)tv.tv_usec);
+ /* allow 1s margin in case of rollover between sampling
+ * the current time and when the UUID is generated. */
+ if (time_reg > time_gen + 1) {
+ printf("UUID time comparison failed!\n");
+ failed++;
+ } else {
+ printf("UUID time comparison succeeded.\n");
+ }
+
+ if (uuid_parse(str, tst) < 0) {
+ printf("UUID parse failed\n");
+ failed++;
+ }
+ if (!uuid_compare(buf, tst)) {
printf("UUID parse and compare succeeded.\n");
- else {
+ } else {
printf("UUID parse and compare failed!\n");
failed++;
}
@@ -162,6 +178,7 @@ main(int argc ATTR((unused)) , char **argv ATTR((unused)))
printf("UUID copy and compare failed!\n");
failed++;
}
+
failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981b", 1);
failed += test_uuid("84949CC5-4701-4A84-895B-354C584A981B", 1);
failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981bc", 0);
diff --git a/lib/uuid/uuid.pc.in b/lib/uuid/uuid.pc.in
index 9e38a326..eaa4e4d5 100644
--- a/lib/uuid/uuid.pc.in
+++ b/lib/uuid/uuid.pc.in
@@ -7,5 +7,5 @@ Name: uuid
Description: Universally unique id library
Version: @E2FSPROGS_VERSION@
Requires:
-Cflags: -I${includedir}/uuid
+Cflags: -I${includedir}/uuid -I${includedir}
Libs: -L${libdir} -luuid
diff --git a/lib/uuid/uuid_time.c b/lib/uuid/uuid_time.c
index 4c3536b4..97fd3358 100644
--- a/lib/uuid/uuid_time.c
+++ b/lib/uuid/uuid_time.c
@@ -165,7 +165,7 @@ main(int argc, char **argv)
printf("Warning: not a time-based UUID, so UUID time "
"decoding will likely not work!\n");
}
- printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+ printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, (long)tv.tv_usec,
ctime(&time_reg));
return 0;