diff options
Diffstat (limited to 'lib')
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\}\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\ }} + +\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(¶m, 0, sizeof(param)); ext2fs_blocks_count_set(¶m, 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, ¶m, 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, ¤t_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(¶m, blocks); param.s_inodes_count = inodes; - retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, + retval = ext2fs_initialize("test fs", flags, ¶m, 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 = "afile_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; |