diff -u --recursive --new-file v2.2.0-pre3/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.2.0-pre3/linux/Documentation/Configure.help	Fri Jan  1 12:58:14 1999
+++ linux/Documentation/Configure.help	Sat Jan  2 17:50:15 1999
@@ -1631,9 +1631,13 @@
   information.
 
   Say Y here and to the driver for your graphics board below if you
-  are compiling a kernel for a non-x86 architecture. If you are
-  compiling for the x86 architecture, you can say Y if you want to
-  play with it, but it is not essential.
+  are compiling a kernel for a non-x86 architecture.
+
+  If you are compiling for the x86 architecture, you can say Y if you
+  want to play with it, but it is not essential. Please note that
+  running graphical applications that directly touch the hardware (e.g.
+  and accelerated X server) and that are not frame buffer device-aware
+  may cause unexpected results. If unsure, say N.
 
 Acorn VIDC support
 CONFIG_FB_ACORN
@@ -4356,23 +4360,44 @@
   drive: it will be supported automatically if you said Y to the
   generic "SCSI disk support", above.
 
+  The ZIP Plus drive is supported by the imm driver, also more recent
+  parallel port ZIP drives use an imm compatible interface. If the
+  supplied cable with the drive is labeled "AutoDetect" then you will
+  need the imm driver.
+
   This driver is also available as a module which can be inserted in
   and removed from the running kernel whenever you want. To compile
   this driver as a module, say M here and read
   Documentation/modules.txt. The module will be called ppa.o. 
 
-EPP FIFO Checking
-CONFIG_SCSI_PPA_HAVE_PEDANTIC
+IOMEGA Parallel Port ZIP drive SCSI support
+CONFIG_SCSI_IMM
+  All "new and improved" parallel port to SCSI interface from Iomega.
+  Please read the comments for the ppa driver for further information
+
+Force the Iomega ZIP drivers to use EPP-16
+CONFIG_SCSI_IZIP_EPP16
   EPP (Enhanced Parallel Port) is a standard for parallel ports which
   allows them to act as expansion buses that can handle up to 64
-  peripheral devices. Some parallel port chipsets are slower than
-  their motherboard, and so we have to control the state of the
-  chipset's FIFO queue every now and then to avoid data loss. This
-  will be done if you say Y here. 
+  peripheral devices.
+
+  Some parallel port chipsets are slower than their motherboard, and
+  so we have to control the state of the chipset's FIFO queue every
+  now and then to avoid data loss. This will be done if you say Y here. 
   
-  If your EPP chipset is from the SMC series, you are likely to have
-  to say Y here. Generally, saying Y is the safe option and slows
-  things down a bit.
+  Generally, saying Y is the safe option and slows things down a bit.
+
+Assume slow parallel port control register
+CONFIG_SCSI_IZIP_SLOW_CTR
+  Some parallel ports are known to have excessive delays between changing
+  the parallel port control register and good data being available on
+  the parallel port data/status register. This option forces a small delay
+  (1.0 usec to be exact) after changing the control register to let things
+  settle out. Enabling this option may result in a big drop in performance
+  but some very old parallel ports (found in 386 vintage machines) will
+  not work properly.
+
+  Generally, saying N is fine.
 
 SCSI Debug host simulator.
 CONFIG_SCSI_DEBUG
diff -u --recursive --new-file v2.2.0-pre3/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt
--- v2.2.0-pre3/linux/Documentation/oops-tracing.txt	Sat May  2 14:19:51 1998
+++ linux/Documentation/oops-tracing.txt	Sat Jan  2 10:02:04 1999
@@ -1,3 +1,24 @@
+Quick Summary
+-------------
+
+cd /usr/src/linux/scripts
+g++ -o ksymoops ksymoops.cc
+./ksymoops ../System.map < the_oops.txt
+
+and send the output the maintainer of the kernel area that seems to be
+involved with the problem. Don't worry too much about getting the wrong
+person. If you are unsure send it to the person responsible for the code
+relevant to what you were doing. If it occurs repeatably try and describe
+how to recreate it. Thats worth even more than the oops
+
+If you are totally stumped as to whom to send the report, send it to 
+linux-kernel@vger.rutgers.edu. Thanks for your help in making Linux as
+stable as humanly possible.
+
+
+Full Information
+----------------
+
 From: Linus Torvalds <torvalds@cs.helsinki.fi>
 
 How to track down an Oops.. [originally a mail to linux-kernel]
diff -u --recursive --new-file v2.2.0-pre3/linux/Makefile linux/Makefile
--- v2.2.0-pre3/linux/Makefile	Fri Jan  1 12:58:14 1999
+++ linux/Makefile	Fri Jan  1 12:58:29 1999
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 0
-EXTRAVERSION =-pre3
+EXTRAVERSION =-pre4
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --recursive --new-file v2.2.0-pre3/linux/README linux/README
--- v2.2.0-pre3/linux/README	Fri Oct 23 22:01:19 1998
+++ linux/README	Sat Jan  2 10:04:36 1999
@@ -1,22 +1,8 @@
-	Linux kernel release 2.1.xx
+	Linux kernel release 2.2.xx
 
-These are the release notes for Linux version 2.1.  Read them carefully,
+These are the release notes for Linux version 2.2.  Read them carefully,
 as they tell you what this is all about, explain how to install the
 kernel, and what to do if something goes wrong. 
-
-Linux version 2.1 is a DEVELOPMENT kernel, and not intended for general
-public use.  Different releases may have various and sometimes severe
-bugs.  It is *strongly* recommended that you back up the previous kernel
-before installing any new 2.1.xx release.
-
-If you need to use a proven and stable Linux kernel, please use 1.0.9,
-1.2.13, or 2.0.xx.  All features which will be in the 2.1.xx releases will
-be contained in 2.2.xx when the code base has stabilized again.
-
-If you decide to use 2.1, it is recommended that you join the kernel mailing
-list.  To do this, e-mail majordomo@vger.rutgers.edu, and put in the body
-of the message "subscribe linux-kernel" or "subscribe linux-kernel-digest"
-for a daily digest of the mailing list (it is a high-traffic list.)
 
 However, please make sure you don't ask questions which are already answered
 in various files in the Documentation directory.  See DOCUMENTATION below.
diff -u --recursive --new-file v2.2.0-pre3/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile
--- v2.2.0-pre3/linux/arch/i386/boot/Makefile	Tue Jul 21 00:15:30 1998
+++ linux/arch/i386/boot/Makefile	Sat Jan  2 10:27:52 1999
@@ -40,7 +40,7 @@
 	if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
 
 install: $(CONFIGURE) $(BOOTIMAGE)
-	sh -x ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)"
 
 tools/build: tools/build.c
 	$(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include
diff -u --recursive --new-file v2.2.0-pre3/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.2.0-pre3/linux/arch/i386/kernel/setup.c	Fri Jan  1 12:58:19 1999
+++ linux/arch/i386/kernel/setup.c	Sat Jan  2 18:03:33 1999
@@ -675,7 +675,7 @@
 			p += sprintf(p, "stepping\t: unknown\n");
 
 		if (c->x86_capability & X86_FEATURE_TSC) {
-			p += sprintf(p, "cpu MHz\t\t: %lu.%02lu\n",
+			p += sprintf(p, "cpu MHz\t\t: %lu.%06lu\n",
 				cpu_hz / 1000000, (cpu_hz % 1000000));
 		}
 
@@ -689,6 +689,8 @@
 		} else if (c->x86_vendor == X86_VENDOR_AMD) {
 			x86_cap_flags[16] = "fcmov";
 			x86_cap_flags[31] = "3dnow";
+			if (c->x86 == 5 && c->x86_model == 6)
+				x86_cap_flags[10] = "sep";
 		} else if (c->x86_vendor == X86_VENDOR_INTEL) {
 			x86_cap_flags[6] = "pae";
 			x86_cap_flags[9] = "apic";
diff -u --recursive --new-file v2.2.0-pre3/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.2.0-pre3/linux/arch/i386/kernel/smp.c	Fri Jan  1 12:58:19 1999
+++ linux/arch/i386/kernel/smp.c	Sat Jan  2 10:18:07 1999
@@ -1126,6 +1126,7 @@
 	{
 		printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n");
 		io_apic_irqs = 0;
+		cpu_online_map = cpu_present_map;
 		goto smp_done;
 	}
 
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.2.0-pre3/linux/drivers/char/misc.c	Fri Nov 27 13:09:23 1998
+++ linux/drivers/char/misc.c	Sat Jan  2 17:55:05 1999
@@ -86,7 +86,6 @@
 extern int pc110pad_init(void);
 extern int pmu_device_init(void);
 
-#ifdef CONFIG_PROC_FS
 static int misc_read_proc(char *buf, char **start, off_t offset,
 			  int len, int *eof, void *private)
 {
@@ -99,7 +98,6 @@
 	return len > offset ? len - offset : 0;
 }
 
-#endif /* PROC_FS */
 
 static int misc_open(struct inode * inode, struct file * file)
 {
@@ -185,17 +183,13 @@
 EXPORT_SYMBOL(misc_register);
 EXPORT_SYMBOL(misc_deregister);
 
-#if defined(CONFIG_PROC_FS)
 static struct proc_dir_entry *proc_misc;	
-#endif
 
 int __init misc_init(void)
 {
-#ifdef CONFIG_PROC_FS
 	proc_misc = create_proc_entry("misc", 0, 0);
 	if (proc_misc)
 		proc_misc->read_proc = misc_read_proc;
-#endif /* PROC_FS */
 #ifdef CONFIG_BUSMOUSE
 	bus_mouse_init();
 #endif
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.2.0-pre3/linux/drivers/char/tty_io.c	Wed Dec 16 10:32:55 1998
+++ linux/drivers/char/tty_io.c	Sat Jan  2 17:55:05 1999
@@ -77,9 +77,7 @@
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/poll.h>
-#ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
-#endif
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 
@@ -1966,9 +1964,7 @@
 	if (tty_drivers) tty_drivers->prev = driver;
 	tty_drivers = driver;
 	
-#ifdef CONFIG_PROC_FS
 	proc_tty_register_driver(driver);
-#endif	
 	return error;
 }
 
@@ -2010,9 +2006,7 @@
 	if (driver->next)
 		driver->next->prev = driver->prev;
 
-#ifdef CONFIG_PROC_FS
 	proc_tty_unregister_driver(driver);
-#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c
--- v2.2.0-pre3/linux/drivers/misc/parport_arc.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/misc/parport_arc.c	Sat Jan  2 17:55:05 1999
@@ -142,9 +142,7 @@
 
 	printk(KERN_INFO "%s: Archimedes on-board port, using irq %d\n",
 	       p->irq);
-#ifdef	CONFIG_PROC_FS
 	parport_proc_register(p);
-#endif
 	p->flags |= PARPORT_FLAG_COMA;
 
 	if (parport_probe_hook)
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.2.0-pre3/linux/drivers/misc/parport_ax.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/misc/parport_ax.c	Sat Jan  2 17:55:05 1999
@@ -586,9 +586,7 @@
 		printmode(ECPPS2);
 	}
 	printk("]\n");
-#ifdef	CONFIG_PROC_FS
 	parport_proc_register(p);
-#endif
 	p->flags |= PARPORT_FLAG_COMA;
 
 	p->ops->write_control(p, 0x0c);
@@ -631,9 +629,7 @@
 		if (p->modes & PARPORT_MODE_PCSPP) { 
 			if (!(p->flags & PARPORT_FLAG_COMA)) 
 				parport_quiesce(p);
-#ifdef	CONFIG_PROC_FS
 			parport_proc_unregister(p);
-#endif
 			parport_unregister_port(p);
 		}
 		p = tmp;
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- v2.2.0-pre3/linux/drivers/misc/parport_init.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/misc/parport_init.c	Sat Jan  2 17:55:05 1999
@@ -98,19 +98,17 @@
 #ifdef MODULE
 int init_module(void)
 {
-#ifdef	CONFIG_PROC_FS
 	(void)parport_proc_init();	/* We can go on without it. */
-#endif
 	return 0;
 }
 
 void cleanup_module(void)
 {
-#ifdef	CONFIG_PROC_FS
 	parport_proc_cleanup();
-#endif
 }
+
 #else
+
 __initfunc(int parport_init(void))
 {
 	if (io[0] == PARPORT_DISABLE) 
@@ -145,10 +143,8 @@
 EXPORT_SYMBOL(parport_enumerate);
 EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
 EXPORT_SYMBOL(parport_wait_peripheral);
-#ifdef	CONFIG_PROC_FS
 EXPORT_SYMBOL(parport_proc_register);
 EXPORT_SYMBOL(parport_proc_unregister);
-#endif
 EXPORT_SYMBOL(parport_probe_hook);
 EXPORT_SYMBOL(parport_parse_irqs);
 
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.2.0-pre3/linux/drivers/misc/parport_pc.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/misc/parport_pc.c	Sat Jan  2 17:55:05 1999
@@ -759,8 +759,8 @@
 #ifdef	CONFIG_PROC_FS
 	if (probedirq != PARPORT_IRQ_NONE) 
 		printk("%s: detected irq %d; use procfs to enable interrupt-driven operation.\n", p->name, probedirq);
-	parport_proc_register(p);
 #endif
+	parport_proc_register(p);
 	p->flags |= PARPORT_FLAG_COMA;
 
 	/* Done probing.  Now put the port into a sensible start-up state. */
@@ -824,9 +824,7 @@
 		if (p->modes & PARPORT_MODE_PCSPP) { 
 			if (!(p->flags & PARPORT_FLAG_COMA)) 
 				parport_quiesce(p);
-#ifdef	CONFIG_PROC_FS
 			parport_proc_unregister(p);
-#endif
 			parport_unregister_port(p);
 		}
 		p = tmp;
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/misc/parport_procfs.c linux/drivers/misc/parport_procfs.c
--- v2.2.0-pre3/linux/drivers/misc/parport_procfs.c	Sun Nov  8 14:02:59 1998
+++ linux/drivers/misc/parport_procfs.c	Sat Jan  2 17:55:05 1999
@@ -27,6 +27,8 @@
 #include <asm/dma.h>
 #include <asm/irq.h>
 
+#ifdef CONFIG_PROC_FS
+
 struct proc_dir_entry *base = NULL;
 
 static int irq_write_proc(struct file *file, const char *buffer,
@@ -305,12 +307,11 @@
 {
 	base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT,
 			      NULL);
-	base->fill_inode = &parport_modcount;
-
 	if (base == NULL) {
 		printk(KERN_ERR "Unable to initialise /proc/parport.\n");
 		return 0;
 	}
+	base->fill_inode = &parport_modcount;
 
 	return 1;
 }
@@ -385,3 +386,26 @@
 	destroy_proc_tree(pp);
 	return 0;
 }
+
+#else
+
+int parport_proc_register(struct parport *p) 
+{
+	return 0;
+}
+
+int parport_proc_unregister(struct parport *p)
+{
+	return 0;
+}
+
+int parport_proc_init(void)
+{
+	return 0;
+}
+
+void parport_proc_cleanup(void)
+{
+}
+
+#endif
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.2.0-pre3/linux/drivers/net/ibmtr.c	Tue Dec 22 14:16:55 1998
+++ linux/drivers/net/ibmtr.c	Sat Jan  2 10:27:22 1999
@@ -66,8 +66,17 @@
  *      + lifted 2000 byte mtu limit. now depends on shared-RAM size.
  *        May 25 1998)
  *      + can't allocate 2k recv buff at 8k shared-RAM. (20 October 1998)
+ *
+ *      Changes by Joel Sloan (jjs@c-me.com) :
+ *      + disable verbose debug messages by default - to enable verbose
+ *	  debugging, edit the IBMTR_DEBUG_MESSAGES define below 
  */
 
+/* change the define of IBMTR_DEBUG_MESSAGES to a nonzero value 
+in the event that chatty debug messages are desired - jjs 12/30/98 */
+
+#define IBMTR_DEBUG_MESSAGES 0
+
 #ifdef PCMCIA
 #define MODULE
 #endif
@@ -95,7 +104,8 @@
 /* version and credits */
 static char *version =
 "ibmtr.c: v1.3.57   8/ 7/94 Peter De Schrijver and Mark Swanson\n"
-"         v2.1.125 10/20/98 Paul Norton <pnorton@ieee.org>\n";
+"         v2.1.125 10/20/98 Paul Norton <pnorton@ieee.org>\n"
+"         v2.2.0   12/30/98 Joel Sloan <jjs@c-me.com>\n";
 
 static char pcchannelid[] = {
 	0x05, 0x00, 0x04, 0x09,
@@ -999,6 +1009,7 @@
 						DPRINTK("error on dir_read_log: %02X\n",
 							(int)readb(ti->srb+offsetof(struct srb_read_log, ret_code)));
 					else
+					    if (IBMTR_DEBUG_MESSAGES) {
 						DPRINTK(
 							"Line errors %02X, Internal errors %02X, Burst errors %02X\n"
 							"A/C errors %02X, Abort delimiters %02X, Lost frames %02X\n"
@@ -1023,6 +1034,7 @@
 										    frequency_errors)),
 							(int)readb(ti->srb+offsetof(struct srb_read_log,
 												    token_errors)));
+					    }
 					dev->tbusy=0;
 					break;
 
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.2.0-pre3/linux/drivers/pci/oldproc.c	Thu Dec 31 10:29:01 1998
+++ linux/drivers/pci/oldproc.c	Sat Jan  2 18:11:22 1999
@@ -541,6 +541,7 @@
 	DEVICE( ADAPTEC2,	ADAPTEC2_2940U2,"AHA-2940U2"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_7890,	"AIC-7890/1"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_3940U2,"AHA-3940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_3950U2D,"AHA-3950U2D"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_7896,	"AIC-7896/7"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.2.0-pre3/linux/drivers/scsi/Config.in	Thu Dec 31 10:29:01 1998
+++ linux/drivers/scsi/Config.in	Sat Jan  2 10:21:06 1999
@@ -58,13 +58,6 @@
 fi
 dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
 dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
-if [ "$CONFIG_PARPORT" != "n" ]; then
-  dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
-  if [ "$CONFIG_SCSI_PPA" != "n" ]; then
-    bool  '  Pedantic EPP-checking'   CONFIG_SCSI_PPA_HAVE_PEDANTIC
-  fi
-  dep_tristate 'IOMEGA ZIP Plus drive SCSI support' CONFIG_SCSI_IMM $CONFIG_SCSI $CONFIG_PARPORT
-fi
 if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
 	bool '   Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
 	choice 'NCR5380/53c400 mapping method (use Port for T130B)' \
@@ -73,6 +66,14 @@
 fi
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI
+fi
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate 'IOMEGA parallel port (ppa - older drives)' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
+  dep_tristate 'IOMEGA parallel port (imm - newer drives)' CONFIG_SCSI_IMM $CONFIG_SCSI $CONFIG_PARPORT
+  if [ "$CONFIG_SCSI_PPA" != "n" -o "$CONFIG_SCSI_IMM" != "n" ]; then
+    bool  '  ppa/imm option - Use slow (but safe) EPP-16' CONFIG_SCSI_IZIP_EPP16
+    bool  '  ppa/imm option - Assume slow parport control register' CONFIG_SCSI_IZIP_SLOW_CTR
+  fi
 fi
 dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.2.0-pre3/linux/drivers/scsi/Makefile	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/Makefile	Sat Jan  2 17:55:05 1999
@@ -41,9 +41,7 @@
   endif
   L_OBJS += scsi_n_syms.o hosts.o scsi_ioctl.o constants.o scsicam.o
   L_OBJS += scsi_error.o scsi_obsolete.o scsi_queue.o
-  ifeq ($(CONFIG_PROC_FS),y)
-    L_OBJS += scsi_proc.o
-  endif
+  L_OBJS += scsi_proc.o
 else
   ifeq ($(CONFIG_SCSI),m)
     MIX_OBJS += scsi_syms.o
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.2.0-pre3/linux/drivers/scsi/aic7xxx.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/aic7xxx.c	Sat Jan  2 18:11:22 1999
@@ -354,7 +354,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.6"
+#define AIC7XXX_C_VERSION  "5.1.7"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -580,6 +580,7 @@
   "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
   "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
+  "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
 };
 
@@ -5815,23 +5816,19 @@
        * Put this SCB back on the free list.
        */
       aic7xxx_add_curscb_to_free_list(p);
-      /*
-       * XXX - If we queued an abort tag, go clean up the disconnected list.
-       * We know that this particular SCB had to be the queued abort since
-       * the disconnected SCB would have gotten a reconnect instead.
-       * However, if this is an abort command, then DID_TIMEOUT isn't
-       * appropriate, neither is returning the command for that matter.
-       * What we need to do then is to let the command timeout again so
-       * we get a reset since this abort just failed.
-       */
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
       if (aic7xxx_verbose > 0xffff)
         printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
 #endif
-      if (p->flags & SCB_QUEUED_ABORT)
+      if (scb->flags & SCB_QUEUED_ABORT)
       {
+        /*
+         * We know that this particular SCB had to be the queued abort since
+         * the disconnected SCB would have gotten a reconnect instead.
+         * What we need to do then is to let the command timeout again so
+         * we get a reset since this abort just failed.
+         */
         cmd->result = 0;
-        scb->flags &= ~SCB_QUEUED_ABORT;
         scb = NULL;
       }
     }
@@ -7659,7 +7656,7 @@
     aic_outb(p, p->scsi_id_b, SCSIID);
     scsi_conf = aic_inb(p, SCSICONF + 1);
     aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-    aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+    aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | 
          ENSTIMER | ACTNEGEN, SXFRCTL1);
     aic_outb(p, 0, SIMODE0);
     aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
@@ -7676,7 +7673,7 @@
     aic_outb(p, p->scsi_id, SCSIID);
   scsi_conf = aic_inb(p, SCSICONF);
   aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-  aic_outb(p, (scsi_conf & ENSPCHK) | term | 
+  aic_outb(p, (scsi_conf & ENSPCHK) | STIMESEL | term | 
        ENSTIMER | ACTNEGEN, SXFRCTL1);
   aic_outb(p, 0, SIMODE0);
   aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
@@ -8856,9 +8853,13 @@
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
        AHC_AIC7896_FE,                                      23,
        32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      24,
+       32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                      24,
+       AHC_AIC7860_FE,                                      25,
        32, C46 },
     };
 
@@ -9104,6 +9105,7 @@
             case 15:
             case 18:
             case 19:
+            case 20:
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -9271,13 +9273,10 @@
           }
 
           /*
-           * We do another switch based on i so that we can exclude all
-           * 3895 devices from the next option since the 3895 cards use
-           * shared external SCB RAM while all other cards have dedicated
-           * external SCB RAM per channel.  Also exclude the 7850 and
-           * 7860 based stuff since they can have garbage in the bit
-           * that indicates external RAM and get some of this stuff
-           * wrong as a result.
+           * We only support external SCB RAM on the 7895/6/7 chipsets.
+           * We could support it on the 7890/1 easy enough, but I don't
+           * know of any 7890/1 based cards that have it.  I do know
+           * of 7895/6/7 cards that have it and they work properly.
            */
           switch(temp_p->chip & AHC_CHIPID_MASK)
           {
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/eata_dma_proc.c linux/drivers/scsi/eata_dma_proc.c
--- v2.2.0-pre3/linux/drivers/scsi/eata_dma_proc.c	Thu Dec 31 10:29:01 1998
+++ linux/drivers/scsi/eata_dma_proc.c	Sat Jan  2 17:55:06 1999
@@ -69,8 +69,6 @@
 		   int hostno, int inout)
 {
 
-#ifdef CONFIG_PROC_FS
-
     Scsi_Device *scd, SDev;
     struct Scsi_Host *HBA_ptr;
     Scsi_Cmnd scmd;
@@ -469,9 +467,6 @@
     DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
     
     return (len);     
-#else /* CONFIG_PROC_FS */
-    return 0;
-#endif        
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c
--- v2.2.0-pre3/linux/drivers/scsi/eata_pio_proc.c	Thu Dec 31 10:29:01 1998
+++ linux/drivers/scsi/eata_pio_proc.c	Sat Jan  2 17:55:06 1999
@@ -26,7 +26,6 @@
 int eata_pio_proc_info(char *buffer, char **start, off_t offset, int length, 
 		       int hostno, int inout)
 {
-#ifdef CONFIG_PROC_FS
     Scsi_Device *scd;
     struct Scsi_Host *HBA_ptr;
     static u8 buff[512];
@@ -110,9 +109,6 @@
     DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
     
     return (len);     
-#else
-    return 0;
-#endif        
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/imm.c linux/drivers/scsi/imm.c
--- v2.2.0-pre3/linux/drivers/scsi/imm.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/scsi/imm.c	Sat Jan  2 10:21:06 1999
@@ -4,8 +4,7 @@
  * (The IMM is the embedded controller in the ZIP Plus drive.)
  * 
  * Current Maintainer: David Campbell (Perth, Western Australia)
- *                     campbell@gear.torque.net
- *                     dcampbel@p01.as17.honeywell.com.au
+ *                     campbell@torque.net
  *
  * My unoffical company acronym list is 21 pages long:
  *      FLA:    Four letter acronym with built in facility for
@@ -31,7 +30,7 @@
 #include "sd.h"
 #include "hosts.h"
 typedef struct {
-    struct pardevice *dev;      /* Parport device entry         */
+    struct pardevice *dev;	/* Parport device entry         */
     int base;			/* Actual port address          */
     int mode;			/* Transfer mode                */
     int host;			/* Host number (for proc)       */
@@ -65,23 +64,24 @@
 
 #define IMM_BASE(x)	imm_hosts[(x)].base
 
-int parbus_base[NO_HOSTS] = {0x03bc, 0x0378, 0x0278, 0x0000};
+int parbus_base[NO_HOSTS] =
+{0x03bc, 0x0378, 0x0278, 0x0000};
 
 void imm_wakeup(void *ref)
 {
     imm_struct *imm_dev = (imm_struct *) ref;
 
     if (!imm_dev->p_busy)
-        return;
+	return;
 
     if (parport_claim(imm_dev->dev)) {
-        printk("imm: bug in imm_wakeup\n");
-        return;
+	printk("imm: bug in imm_wakeup\n");
+	return;
     }
     imm_dev->p_busy = 0;
     imm_dev->base = imm_dev->dev->port->base;
     if (imm_dev->cur_cmd)
-        imm_dev->cur_cmd->SCp.phase++;
+	imm_dev->cur_cmd->SCp.phase++;
     return;
 }
 
@@ -97,12 +97,11 @@
 static int imm_pb_claim(int host_no)
 {
     if (parport_claim(imm_hosts[host_no].dev)) {
-        imm_hosts[host_no].p_busy = 1;
-        return 1;
+	imm_hosts[host_no].p_busy = 1;
+	return 1;
     }
-
     if (imm_hosts[host_no].cur_cmd)
-        imm_hosts[host_no].cur_cmd->SCp.phase++;
+	imm_hosts[host_no].cur_cmd->SCp.phase++;
     return 0;
 }
 
@@ -112,40 +111,7 @@
  *                   Parallel port probing routines                        *
  ***************************************************************************/
 
-#ifndef MODULE
-/*
- * Command line parameters (for built-in driver):
- *
- * Syntax:  imm=base[,mode[,use_sg]]
- *
- * For example:  imm=0x378   or   imm=0x378,0,3
- *
- */
-
-void imm_setup(char *str, int *ints)
-{
-    static int x = 0;
-
-    if (x == 0) {		/* Disable ALL known ports */
-	int i;
-
-	for (i = 0; i < NO_HOSTS; i++)
-	    parbus_base[i] = 0x0000;
-    }
-    switch (ints[0]) {
-    case 3:
-	imm_sg = ints[3];
-    case 2:
-	imm_hosts[x].mode = ints[2];
-	parbus_base[x] = ints[1];
-	break;
-    default:
-	printk("IMM: I only use between 2 to 3 parameters.\n");
-	break;
-    }
-    x++;
-}
-#else
+#ifdef MODULE
 Scsi_Host_Template driver_template = IMM;
 #include  "scsi_module.c"
 #endif
@@ -162,100 +128,95 @@
     try_again = 0;
 
     if (!pb) {
-        printk("imm: parport reports no devices.\n");
-        return 0;
+	printk("imm: parport reports no devices.\n");
+	return 0;
     }
-
   retry_entry:
     for (i = 0; pb; i++, pb = pb->next) {
-        int modes, ppb;
+	int modes, ppb;
 
-        imm_hosts[i].dev =
-            parport_register_device(pb, "imm", NULL, imm_wakeup,
-                         NULL, 0, (void *) &imm_hosts[i]);
+	imm_hosts[i].dev =
+	    parport_register_device(pb, "imm", NULL, imm_wakeup,
+				    NULL, 0, (void *) &imm_hosts[i]);
 
 	if (!imm_hosts[i].dev)
-		continue;
+	    continue;
 
-        /* Claim the bus so it remembers what we do to the control
-         * registers. [ CTR and ECP ]
-         */
-	if (imm_pb_claim(i))
-	{
+	/* Claim the bus so it remembers what we do to the control
+	 * registers. [ CTR and ECP ]
+	 */
+	if (imm_pb_claim(i)) {
 	    unsigned long now = jiffies;
-	    while (imm_hosts[i].p_busy)
-	    {
+	    while (imm_hosts[i].p_busy) {
 		schedule();	/* We are safe to schedule here */
-		if (time_after(jiffies,now + 3*HZ))
-		{
+		if (time_after(jiffies, now + 3 * HZ)) {
 		    printk(KERN_ERR "imm%d: failed to claim parport because a "
-			   "pardevice is owning the port for too longtime!\n",
+		      "pardevice is owning the port for too longtime!\n",
 			   i);
 		    return 0;
 		}
 	    }
 	}
+	ppb = IMM_BASE(i) = imm_hosts[i].dev->port->base;
+	w_ctr(ppb, 0x0c);
+	modes = imm_hosts[i].dev->port->modes;
 
-        ppb = IMM_BASE(i) = imm_hosts[i].dev->port->base;
-        w_ctr(ppb, 0x0c);
-        modes = imm_hosts[i].dev->port->modes;
-
-        /* Mode detection works up the chain of speed
-         * This avoids a nasty if-then-else-if-... tree
-         */
-        imm_hosts[i].mode = IMM_NIBBLE;
-
-        if (modes & PARPORT_MODE_PCPS2)
-            imm_hosts[i].mode = IMM_PS2;
-
-        if (modes & PARPORT_MODE_PCECPPS2) {
-            w_ecr(ppb, 0x20);
-            imm_hosts[i].mode = IMM_PS2;
-        }
-        if (modes & PARPORT_MODE_PCECPEPP)
-            w_ecr(ppb, 0x80);
-
-        /* Done configuration */
-        imm_pb_release(i);
-
-        if (imm_init(i)) {
-            parport_unregister_device(imm_hosts[i].dev);
-            continue;
-        }
-        /* now the glue ... */
-        switch (imm_hosts[i].mode) {
-        case IMM_NIBBLE:
-            ports = 3;
-            break;
-        case IMM_PS2:
-            ports = 3;
-            break;
-        case IMM_EPP_8:
-        case IMM_EPP_16:
-        case IMM_EPP_32:
-            ports = 8;
-            break;
-        default:                /* Never gets here */
-            continue;
-        }
-
-        host->can_queue = IMM_CAN_QUEUE;
-        host->sg_tablesize = imm_sg;
-        hreg = scsi_register(host, 0);
-        hreg->io_port = pb->base;
-        hreg->n_io_port = ports;
-        hreg->dma_channel = -1;
-        hreg->unique_id = i;
-        imm_hosts[i].host = hreg->host_no;
-        nhosts++;
+	/* Mode detection works up the chain of speed
+	 * This avoids a nasty if-then-else-if-... tree
+	 */
+	imm_hosts[i].mode = IMM_NIBBLE;
+
+	if (modes & PARPORT_MODE_PCPS2)
+	    imm_hosts[i].mode = IMM_PS2;
+
+	if (modes & PARPORT_MODE_PCECPPS2) {
+	    w_ecr(ppb, 0x20);
+	    imm_hosts[i].mode = IMM_PS2;
+	}
+	if (modes & PARPORT_MODE_PCECPEPP)
+	    w_ecr(ppb, 0x80);
+
+	/* Done configuration */
+	imm_pb_release(i);
+
+	if (imm_init(i)) {
+	    parport_unregister_device(imm_hosts[i].dev);
+	    continue;
+	}
+	/* now the glue ... */
+	switch (imm_hosts[i].mode) {
+	case IMM_NIBBLE:
+	    ports = 3;
+	    break;
+	case IMM_PS2:
+	    ports = 3;
+	    break;
+	case IMM_EPP_8:
+	case IMM_EPP_16:
+	case IMM_EPP_32:
+	    ports = 8;
+	    break;
+	default:		/* Never gets here */
+	    continue;
+	}
+
+	host->can_queue = IMM_CAN_QUEUE;
+	host->sg_tablesize = imm_sg;
+	hreg = scsi_register(host, 0);
+	hreg->io_port = pb->base;
+	hreg->n_io_port = ports;
+	hreg->dma_channel = -1;
+	hreg->unique_id = i;
+	imm_hosts[i].host = hreg->host_no;
+	nhosts++;
     }
     if (nhosts == 0) {
-        if (try_again == 1)
-            return 0;
-        try_again = 1;
-        goto retry_entry;
+	if (try_again == 1)
+	    return 0;
+	try_again = 1;
+	goto retry_entry;
     } else
-        return 1;               /* return number of hosts detected */
+	return 1;		/* return number of hosts detected */
 }
 
 /* This is to give the imm driver a way to modify the timings (and other
@@ -265,22 +226,11 @@
  * testing...
  * Also gives a method to use a script to obtain optimum timings (TODO)
  */
-
-static inline int imm_strncmp(const char *a, const char *b, int len)
-{
-    int loop;
-    for (loop = 0; loop < len; loop++)
-	if (a[loop] != b[loop])
-	    return 1;
-
-    return 0;
-}
-
 static inline int imm_proc_write(int hostno, char *buffer, int length)
 {
     unsigned long x;
 
-    if ((length > 5) && (imm_strncmp(buffer, "mode=", 5) == 0)) {
+    if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
 	x = simple_strtoul(buffer + 5, NULL, 0);
 	imm_hosts[hostno].mode = x;
 	return length;
@@ -445,42 +395,34 @@
     int i;
 
     if ((r_ecr(ppb) & 0xe0) != 0x80)
-        return;
+	return;
 
     for (i = 0; i < 100; i++) {
-        if (r_ecr(ppb) & 0x01)
-            return;
-        udelay(5);
+	if (r_ecr(ppb) & 0x01)
+	    return;
+	udelay(5);
     }
     printk("imm: ECP sync failed as data still present in FIFO.\n");
 }
 
-static inline int imm_byte_out(unsigned short base, const char *buffer, int len)
+static int imm_byte_out(unsigned short base, const char *buffer, int len)
 {
     int i;
 
-    w_ctr(base, 0x4);		/* aimmrently a sane mode */
+    w_ctr(base, 0x4);		/* apparently a sane mode */
     for (i = len >> 1; i; i--) {
 	w_dtr(base, *buffer++);
 	w_ctr(base, 0x5);	/* Drop STROBE low */
 	w_dtr(base, *buffer++);
 	w_ctr(base, 0x0);	/* STROBE high + INIT low */
     }
-    w_ctr(base, 0x4);		/* aimmrently a sane mode */
+    w_ctr(base, 0x4);		/* apparently a sane mode */
     return 1;			/* All went well - we hope! */
 }
 
-static inline int imm_epp_out(unsigned short base, char *buffer, int len)
+static int imm_nibble_in(unsigned short base, char *buffer, int len)
 {
-    int i;
-    for (i = len; i; i--)
-	w_epp(base, *buffer++);
-    return 1;
-}
-
-static inline int imm_nibble_in(unsigned short base, char *buffer, int len)
-{
-    unsigned char h, l;
+    unsigned char l;
     int i;
 
     /*
@@ -489,16 +431,15 @@
     w_ctr(base, 0x4);
     for (i = len; i; i--) {
 	w_ctr(base, 0x6);
-	l = r_str(base);
+	l = (r_str(base) & 0xf0) >> 4;
 	w_ctr(base, 0x5);
-	h = r_str(base);
+	*buffer++ = (r_str(base) & 0xf0) | l;
 	w_ctr(base, 0x4);
-	*buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
     }
     return 1;			/* All went well - we hope! */
 }
 
-static inline int imm_byte_in(unsigned short base, char *buffer, int len)
+static int imm_byte_in(unsigned short base, char *buffer, int len)
 {
     int i;
 
@@ -514,14 +455,6 @@
     return 1;			/* All went well - we hope! */
 }
 
-static inline int imm_epp_in(unsigned short base, char *buffer, int len)
-{
-    int i;
-    for (i = len; i; i--)
-	*buffer++ = r_epp(base);
-    return 1;
-}
-
 static int imm_out(int host_no, char *buffer, int len)
 {
     int r;
@@ -543,11 +476,21 @@
     case IMM_EPP_32:
     case IMM_EPP_16:
     case IMM_EPP_8:
-        epp_reset(ppb);
-        w_ctr(ppb, 0x4);
-	r = imm_epp_out(ppb, buffer, len);
-        w_ctr(ppb, 0xc);
-        ecp_sync(ppb);
+	epp_reset(ppb);
+	w_ctr(ppb, 0x4);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+	if (!(((long) buffer | len) & 0x01))
+	    outsw(ppb + 4, buffer, len >> 1);
+#else
+	if (!(((long) buffer | len) & 0x03))
+	    outsl(ppb + 4, buffer, len >> 2);
+#endif
+	else
+	    outsb(ppb + 4, buffer, len);
+	w_ctr(ppb, 0xc);
+	r = !(r_str(ppb) & 0x01);
+	w_ctr(ppb, 0xc);
+	ecp_sync(ppb);
 	break;
 
     case IMM_NIBBLE:
@@ -595,11 +538,21 @@
     case IMM_EPP_32:
     case IMM_EPP_16:
     case IMM_EPP_8:
-        epp_reset(ppb);
-        w_ctr(ppb, 0x24);
-	r = imm_epp_in(ppb, buffer, len);
-        w_ctr(ppb, 0x2c);
-        ecp_sync(ppb);
+	epp_reset(ppb);
+	w_ctr(ppb, 0x24);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+	if (!(((long) buffer | len) & 0x01))
+	    insw(ppb + 4, buffer, len >> 1);
+#else
+	if (!(((long) buffer | len) & 0x03))
+	    insl(ppb + 4, buffer, len >> 2);
+#endif
+	else
+	    insb(ppb + 4, buffer, len);
+	w_ctr(ppb, 0x2c);
+	r = !(r_str(ppb) & 0x01);
+	w_ctr(ppb, 0x2c);
+	ecp_sync(ppb);
 	break;
 
     default:
@@ -756,8 +709,8 @@
 
 #if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
     if (imm_pb_claim(host_no))
-        while (imm_hosts[host_no].p_busy)
-            schedule();         /* We can safe schedule here */
+	while (imm_hosts[host_no].p_busy)
+	    schedule();		/* We can safe schedule here */
 #endif
     retv = imm_connect(host_no, 0);
 
@@ -770,7 +723,6 @@
 	imm_pb_release(host_no);
 	return retv;
     }
-
     imm_pb_release(host_no);
     return 1;
 }
@@ -831,7 +783,7 @@
 	 * If we have been running for more than a full timer tick
 	 * then take a rest.
 	 */
-	if (time_after(jiffies,start_jiffies + 1))
+	if (time_after(jiffies, start_jiffies + 1))
 	    return 0;
 
 	/*
@@ -1173,6 +1125,7 @@
 
 int imm_abort(Scsi_Cmnd * cmd)
 {
+    int host_no = cmd->host->unique_id;
     /*
      * There is no method for aborting commands since Iomega
      * have tied the SCSI_MESSAGE line high in the interface
@@ -1181,12 +1134,11 @@
     switch (cmd->SCp.phase) {
     case 0:			/* Do not have access to parport */
     case 1:			/* Have not connected to interface */
-	cmd->result = DID_ABORT;
-	cmd->done(cmd);
-	return SCSI_ABORT_SUCCESS;
+	imm_hosts[host_no].cur_cmd = NULL;	/* Forget the problem */
+	return SUCCESS;
 	break;
     default:			/* SCSI command sent, can not abort */
-	return SCSI_ABORT_BUSY;
+	return FAILED;
 	break;
     }
 }
@@ -1203,46 +1155,20 @@
     w_ctr(base, 0x04);
 }
 
-int imm_reset(Scsi_Cmnd * cmd, unsigned int x)
+int imm_reset(Scsi_Cmnd * cmd)
 {
     int host_no = cmd->host->unique_id;
 
-    /*
-     * PHASE1:
-     * Bring the interface crashing down on whatever is running
-     * hopefully this will kill the request.
-     * Bring back up the interface, reset the drive (and anything
-     * attached for that manner)
-     */
-    if (cmd)
-	if (cmd->SCp.phase)
-	    imm_disconnect(cmd->host->unique_id);
+    if (cmd->SCp.phase)
+	imm_disconnect(host_no);
+    imm_hosts[host_no].cur_cmd = NULL;	/* Forget the problem */
 
     imm_connect(host_no, CONNECT_NORMAL);
     imm_reset_pulse(IMM_BASE(host_no));
-    udelay(1000);		/* delay for devices to settle down */
+    udelay(1000);		/* device settle delay */
     imm_disconnect(host_no);
-    udelay(1000);		/* Additional delay to allow devices to settle down */
-
-    /*
-     * PHASE2:
-     * Sanity check for the sake of mid-level driver
-     */
-    if (!cmd) {
-	printk("imm bus reset called for invalid command.\n");
-	return SCSI_RESET_NOT_RUNNING;
-    }
-    /*
-     * PHASE3:
-     * Flag the current command as having died due to reset
-     */
-    imm_connect(host_no, CONNECT_NORMAL);
-    imm_fail(host_no, DID_RESET);
-
-    /* Since the command was already on the timer queue imm_interrupt
-     * will be called shortly.
-     */
-    return SCSI_RESET_PENDING;
+    udelay(1000);		/* device settle delay */
+    return SUCCESS;
 }
 
 static int device_check(int host_no)
@@ -1257,79 +1183,78 @@
 
     old_mode = imm_hosts[host_no].mode;
     for (loop = 0; loop < 8; loop++) {
-        /* Attempt to use EPP for Test Unit Ready */
-        if ((ppb & 0x0007) == 0x0000)
-            imm_hosts[host_no].mode = IMM_EPP_32;
+	/* Attempt to use EPP for Test Unit Ready */
+	if ((ppb & 0x0007) == 0x0000)
+	    imm_hosts[host_no].mode = IMM_EPP_32;
 
       second_pass:
-        imm_connect(host_no, CONNECT_EPP_MAYBE);
-        /* Select SCSI device */
-        if (!imm_select(host_no, loop)) {
-            imm_disconnect(host_no);
-            continue;
-        }
-        printk("imm: Found device at ID %i, Attempting to use %s\n", loop,
-               IMM_MODE_STRING[imm_hosts[host_no].mode]);
-
-        /* Send SCSI command */
-        status = 1;
-        w_ctr(ppb, 0x0c);
-        for (l = 0; (l < 3) && (status); l++)
-            status = imm_out(host_no, &cmd[l<<1], 2);
-
-        if (!status) {
-            imm_disconnect(host_no);
-            imm_connect(host_no, CONNECT_EPP_MAYBE);
-            w_dtr(ppb, 0x40);
-            w_ctr(ppb, 0x08);
-            udelay(30);
-            w_ctr(ppb, 0x0c);
-            udelay(1000);
-            imm_disconnect(host_no);
-            udelay(1000);
-            if (imm_hosts[host_no].mode == IMM_EPP_32) {
-                imm_hosts[host_no].mode = old_mode;
-                goto second_pass;
-            }
-            printk("imm: Unable to establish communication, aborting driver load.\n");
-            return 1;
-        }
-        w_ctr(ppb, 0x0c);
-
-        k = 1000000;            /* 1 Second */
-        do {
-            l = r_str(ppb);
-            k--;
-            udelay(1);
-        } while (!(l & 0x80) && (k));
-
-        l &= 0xb8;
-
-        if (l != 0xb8) {
-            imm_disconnect(host_no);
-            imm_connect(host_no, CONNECT_EPP_MAYBE);
+	imm_connect(host_no, CONNECT_EPP_MAYBE);
+	/* Select SCSI device */
+	if (!imm_select(host_no, loop)) {
+	    imm_disconnect(host_no);
+	    continue;
+	}
+	printk("imm: Found device at ID %i, Attempting to use %s\n", loop,
+	       IMM_MODE_STRING[imm_hosts[host_no].mode]);
+
+	/* Send SCSI command */
+	status = 1;
+	w_ctr(ppb, 0x0c);
+	for (l = 0; (l < 3) && (status); l++)
+	    status = imm_out(host_no, &cmd[l << 1], 2);
+
+	if (!status) {
+	    imm_disconnect(host_no);
+	    imm_connect(host_no, CONNECT_EPP_MAYBE);
+	    w_dtr(ppb, 0x40);
+	    w_ctr(ppb, 0x08);
+	    udelay(30);
+	    w_ctr(ppb, 0x0c);
+	    udelay(1000);
+	    imm_disconnect(host_no);
+	    udelay(1000);
+	    if (imm_hosts[host_no].mode == IMM_EPP_32) {
+		imm_hosts[host_no].mode = old_mode;
+		goto second_pass;
+	    }
+	    printk("imm: Unable to establish communication, aborting driver load.\n");
+	    return 1;
+	}
+	w_ctr(ppb, 0x0c);
+
+	k = 1000000;		/* 1 Second */
+	do {
+	    l = r_str(ppb);
+	    k--;
+	    udelay(1);
+	} while (!(l & 0x80) && (k));
+
+	l &= 0xb8;
+
+	if (l != 0xb8) {
+	    imm_disconnect(host_no);
+	    imm_connect(host_no, CONNECT_EPP_MAYBE);
 	    imm_reset_pulse(IMM_BASE(host_no));
-            udelay(1000);
-            imm_disconnect(host_no);
-            udelay(1000);
-            if (imm_hosts[host_no].mode == IMM_EPP_32) {
-                imm_hosts[host_no].mode = old_mode;
-                goto second_pass;
-            }
-            printk("imm: Unable to establish communication, aborting driver load.\n");
-            return 1;
-        }
-        imm_disconnect(host_no);
-        printk("imm: Communication established with ID %i using %s\n", loop,
-               IMM_MODE_STRING[imm_hosts[host_no].mode]);
-        imm_connect(host_no, CONNECT_EPP_MAYBE);
+	    udelay(1000);
+	    imm_disconnect(host_no);
+	    udelay(1000);
+	    if (imm_hosts[host_no].mode == IMM_EPP_32) {
+		imm_hosts[host_no].mode = old_mode;
+		goto second_pass;
+	    }
+	    printk("imm: Unable to establish communication, aborting driver load.\n");
+	    return 1;
+	}
+	imm_disconnect(host_no);
+	printk("imm: Communication established with ID %i using %s\n", loop,
+	       IMM_MODE_STRING[imm_hosts[host_no].mode]);
+	imm_connect(host_no, CONNECT_EPP_MAYBE);
 	imm_reset_pulse(IMM_BASE(host_no));
-        udelay(1000);
-        imm_disconnect(host_no);
-        udelay(1000);
-        return 0;
+	udelay(1000);
+	imm_disconnect(host_no);
+	udelay(1000);
+	return 0;
     }
     printk("imm: No devices found, aborting driver load.\n");
     return 1;
 }
-
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/imm.h linux/drivers/scsi/imm.h
--- v2.2.0-pre3/linux/drivers/scsi/imm.h	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/imm.h	Sat Jan  2 10:21:06 1999
@@ -1,7 +1,8 @@
+
 /*  Driver for the Iomega MatchMaker parallel port SCSI HBA embedded in 
  * the Iomega ZIP Plus drive
  * 
- * (c) 1998     David Campbell	   campbell@torque.net
+ * (c) 1998     David Campbell     campbell@torque.net
  *
  * Please note that I live in Perth, Western Australia. GMT+0800
  */
@@ -9,13 +10,13 @@
 #ifndef _IMM_H
 #define _IMM_H
 
-#define   IMM_VERSION   "2.00"
+#define   IMM_VERSION   "2.03 (for Linux 2.0.0)"
 
 /* 
  * 10 Apr 1998 (Good Friday) - Received EN144302 by email from Iomega.
  * Scarry thing is the level of support from one of their managers.
  * The onus is now on us (the developers) to shut up and start coding.
- *						11Apr98	[ 0.10 ]
+ *                                              11Apr98 [ 0.10 ]
  *
  * --- SNIP ---
  *
@@ -24,35 +25,40 @@
  * Removing "Phase" debug messages.
  *
  * PS: Took four hours of coding after I bought a drive.
- *	ANZAC Day (Aus "War Veterans Holiday")	25Apr98 [ 0.14 ]
+ *      ANZAC Day (Aus "War Veterans Holiday")  25Apr98 [ 0.14 ]
  *
  * Ten minutes later after a few fixes.... (LITERALLY!!!)
  * Have mounted disk, copied file, dismounted disk, remount disk, diff file
  *                    -----  It actually works!!! -----
- *						25Apr98 [ 0.15 ]
+ *                                              25Apr98 [ 0.15 ]
  *
  * Twenty minutes of mucking around, rearanged the IEEE negotiate mechanism.
  * Now have byte mode working (only EPP and ECP to go now... :=)
- *						26Apr98 [ 0.16 ]
+ *                                              26Apr98 [ 0.16 ]
  *
  * Thirty minutes of further coding results in EPP working on my machine.
- *						27Apr98 [ 0.17 ]
+ *                                              27Apr98 [ 0.17 ]
  *
  * Due to work commitments and inability to get a "true" ECP mode functioning
  * I have decided to code the parport support into imm.
- *						09Jun98 [ 0.18 ]
+ *                                              09Jun98 [ 0.18 ]
  *
  * Driver is now out of beta testing.
  * Support for parport has been added.
  * Now distributed with the ppa driver.
- *						12Jun98 [ 2.00 ]
+ *                                              12Jun98 [ 2.00 ]
  *
  * Err.. It appears that imm-2.00 was broken....
- *						18Jun98 [ 2.01 ]
+ *                                              18Jun98 [ 2.01 ]
  *
  * Patch applied to sync this against the Linux 2.1.x kernel code
  * Included qboot_zip.sh
- *						21Jun98 [ 2.02 ]
+ *                                              21Jun98 [ 2.02 ]
+ *
+ * Other clean ups include the follow changes:
+ *    CONFIG_SCSI_PPA_HAVE_PEDANTIC => CONFIG_SCSI_IZIP_EPP16
+ *    added CONFIG_SCSI_IZIP_SLOW_CTR option
+ *                                                      [2.03]
  */
 /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
 
@@ -92,7 +98,11 @@
     "PS/2",
     "EPP 8 bit",
     "EPP 16 bit",
+#ifdef CONFIG_SCSI_IZIP_EPP16
+    "EPP 16 bit",
+#else
     "EPP 32 bit",
+#endif
     "Unknown"};
 
 /* This is a global option */
@@ -110,21 +120,24 @@
 #define CONNECT_EPP_MAYBE 1
 #define CONNECT_NORMAL  0
 
-#define inb_x inb
-#define r_dtr(x)        (unsigned char)inb_x((x))
-#define r_str(x)        (unsigned char)inb_x((x)+1)
-#define r_ctr(x)        (unsigned char)inb_x((x)+2)
-#define r_epp(x)        (unsigned char)inb_x((x)+4)
-#define r_fifo(x)       (unsigned char)inb_x((x)+0x400)
-#define r_ecr(x)        (unsigned char)inb_x((x)+0x402)
-
-#define outb_x outb
-#define w_dtr(x,y)      outb_x(y, (x))
-#define w_str(x,y)      outb_x(y, (x)+1)
-#define w_ctr(x,y)      outb_x(y, (x)+2)
-#define w_epp(x,y)      outb_x(y, (x)+4)
-#define w_fifo(x,y)     outb_x(y, (x)+0x400)
-#define w_ecr(x,y)      outb_x(y, (x)+0x402)
+#define r_dtr(x)        (unsigned char)inb((x))
+#define r_str(x)        (unsigned char)inb((x)+1)
+#define r_ctr(x)        (unsigned char)inb((x)+2)
+#define r_epp(x)        (unsigned char)inb((x)+4)
+#define r_fifo(x)       (unsigned char)inb((x)+0x400)
+#define r_ecr(x)        (unsigned char)inb((x)+0x402)
+
+#define w_dtr(x,y)      outb(y, (x))
+#define w_str(x,y)      outb(y, (x)+1)
+#define w_epp(x,y)      outb(y, (x)+4)
+#define w_fifo(x,y)     outb(y, (x)+0x400)
+#define w_ecr(x,y)      outb(y, (x)+0x402)
+
+#ifdef CONFIG_SCSI_IZIP_SLOW_CTR
+#define w_ctr(x,y)      outb_p(y, (x)+2)
+#else
+#define w_ctr(x,y)      outb(y, (x)+2)
+#endif
 
 static int imm_engine(imm_struct *, Scsi_Cmnd *);
 static int imm_in(int, char *, int);
@@ -144,23 +157,25 @@
 int imm_command(Scsi_Cmnd *);
 int imm_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int imm_abort(Scsi_Cmnd *);
-int imm_reset(Scsi_Cmnd *, unsigned int);
+int imm_reset(Scsi_Cmnd *);
 int imm_proc_info(char *, char **, off_t, int, int, int);
 int imm_biosparam(Disk *, kdev_t, int *);
 
-#define IMM {	proc_dir:		&proc_scsi_imm,			\
-		proc_info:		imm_proc_info,			\
-		name:			"Iomega ZIP Plus drive",	\
-		detect:			imm_detect,			\
-		release:		imm_release,			\
-		command:		imm_command,			\
-		queuecommand:		imm_queuecommand,		\
-		abort:			imm_abort,			\
-		reset:			imm_reset,			\
-		bios_param:		imm_biosparam,			\
-		this_id:		7,				\
-		sg_tablesize:		SG_ALL,				\
-		cmd_per_lun:		1,				\
-		use_clustering:		ENABLE_CLUSTERING		\
+#define IMM {	proc_dir:			&proc_scsi_imm,		\
+		proc_info:			imm_proc_info,		\
+		name:				"Iomega VPI2 (imm) interface",\
+		detect:				imm_detect,		\
+		release:			imm_release,		\
+		command:			imm_command,		\
+		queuecommand:			imm_queuecommand,	\
+                eh_abort_handler:               imm_abort,              \
+                eh_device_reset_handler:        NULL,                   \
+                eh_bus_reset_handler:           imm_reset,              \
+                eh_host_reset_handler:          imm_reset,              \
+		bios_param:		        imm_biosparam,		\
+		this_id:			7,			\
+		sg_tablesize:			SG_ALL,			\
+		cmd_per_lun:			1,			\
+		use_clustering:			ENABLE_CLUSTERING	\
 }
 #endif				/* _IMM_H */
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.2.0-pre3/linux/drivers/scsi/ppa.c	Mon Dec 28 15:00:52 1998
+++ linux/drivers/scsi/ppa.c	Sat Jan  2 10:21:06 1999
@@ -7,8 +7,7 @@
  * under the terms of the GNU Public License.
  * 
  * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
- *                     campbell@gear.torque.net
- *                     dcampbel@p01.as17.honeywell.com.au
+ *                     campbell@torque.net
  */
 
 #include <linux/config.h>
@@ -68,7 +67,6 @@
 	printk("ppa: bug in ppa_wakeup\n");
 	return;
     }
-
     ppa_dev->p_busy = 0;
     ppa_dev->base = ppa_dev->dev->port->base;
     if (ppa_dev->cur_cmd)
@@ -91,7 +89,6 @@
 	ppa_hosts[host_no].p_busy = 1;
 	return 1;
     }
-
     if (ppa_hosts[host_no].cur_cmd)
 	ppa_hosts[host_no].cur_cmd->SCp.phase++;
     return 0;
@@ -133,30 +130,26 @@
 
 	ppa_hosts[i].dev =
 	    parport_register_device(pb, "ppa", NULL, ppa_wakeup,
-			 NULL, 0, (void *) &ppa_hosts[i]);
+				    NULL, 0, (void *) &ppa_hosts[i]);
 
 	if (!ppa_hosts[i].dev)
-		continue;
+	    continue;
 
 	/* Claim the bus so it remembers what we do to the control
 	 * registers. [ CTR and ECP ]
 	 */
-	if (ppa_pb_claim(i))
-	{
+	if (ppa_pb_claim(i)) {
 	    unsigned long now = jiffies;
-	    while (ppa_hosts[i].p_busy)
-	    {
+	    while (ppa_hosts[i].p_busy) {
 		schedule();	/* We are safe to schedule here */
-		if (time_after(jiffies,now + 3*HZ))
-		{
+		if (time_after(jiffies, now + 3 * HZ)) {
 		    printk(KERN_ERR "ppa%d: failed to claim parport because a "
-			   "pardevice is owning the port for too longtime!\n",
+		      "pardevice is owning the port for too longtime!\n",
 			   i);
 		    return 0;
 		}
 	    }
 	}
-
 	ppb = PPA_BASE(i) = ppa_hosts[i].dev->port->base;
 	w_ctr(ppb, 0x0c);
 	modes = ppa_hosts[i].dev->port->modes;
@@ -211,8 +204,15 @@
 	nhosts++;
     }
     if (nhosts == 0) {
-	if (try_again == 1)
+	if (try_again == 1) {
+	    printk("WARNING - no ppa compatible devices found.\n");
+	    printk("  As of 31/Aug/1998 Iomega started shipping parallel\n");
+	    printk("  port ZIP drives with a different interface which is\n");
+	    printk("  supported by the imm (ZIP Plus) driver. If the\n");
+	    printk("  cable is marked with \"AutoDetect\", this is what has\n");
+	    printk("  happened.\n");
 	    return 0;
+	}
 	try_again = 1;
 	goto retry_entry;
     } else
@@ -227,21 +227,11 @@
  * Also gives a method to use a script to obtain optimum timings (TODO)
  */
 
-static inline int ppa_strncmp(const char *a, const char *b, int len)
-{
-    int loop;
-    for (loop = 0; loop < len; loop++)
-	if (a[loop] != b[loop])
-	    return 1;
-
-    return 0;
-}
-
 static inline int ppa_proc_write(int hostno, char *buffer, int length)
 {
     unsigned long x;
 
-    if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+    if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {
 	x = simple_strtoul(buffer + 5, NULL, 0);
 	ppa_hosts[hostno].mode = x;
 	return length;
@@ -360,245 +350,42 @@
     printk("ppa: ECP sync failed as data still present in FIFO.\n");
 }
 
-/*
- * Here is the asm code for the SPP/PS2 protocols for the i386.
- * This has been optimised for speed on 386/486 machines. There will
- * be very little improvement on the current 586+ machines as it is the
- * IO statements which will limit throughput.
- */
-#ifdef __i386__
-#define BYTE_OUT(reg) \
-	"	movb " #reg ",%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	addl $2,%%edx\n" \
-	"	movb $0x0e,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	movb $0x0c,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	subl $2,%%edx\n"
-
-static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len)
+static int ppa_byte_out(unsigned short base, const char *buffer, int len)
 {
-    /*
-     * %eax scratch
-     * %ebx Data to transfer
-     * %ecx Counter (Don't touch!!)
-     * %edx Port
-     * %esi Source buffer (mem pointer)
-     *
-     * In case you are wondering what the last line of the asm does...
-     * <output allocation> : <input allocation> : <trashed registers>
-     */
-    register int d0;
-
-    asm("shr $2,%%ecx\n" \
-	"	jz .no_more_bulk_bo\n" \
-	"	.p2align 4,,7\n" \
-	".loop_bulk_bo:\n" \
-	"	movl (%%esi),%%ebx\n" \
-	BYTE_OUT(%%bl) \
-	BYTE_OUT(%%bh) \
-	"	rorl $16,%%ebx\n" \
-	BYTE_OUT(%%bl) \
-	BYTE_OUT(%%bh) \
-	"	addl $4,%%esi\n" \
-	"	loop .loop_bulk_bo\n" \
-	"	.p2align 4,,7\n" \
-	".no_more_bulk_bo:" \
-  : "=S"(buffer), "=c"(d0)
-  : "1"(len), "d"(base), "0"(buffer)
-  : "eax", "ebx");
-
-    asm("andl $3,%%ecx\n" \
-	"	jz .no_more_loose_bo\n" \
-	"	.p2align 4,,7\n" \
-	".loop_loose_bo:\n" \
-	BYTE_OUT((%%esi)) \
-	"	incl %%esi\n" \
-	"	loop .loop_loose_bo\n" \
-	".no_more_loose_bo:\n" \
-  : "=c"(d0)
-  : "0"(len), "d"(base), "S"(buffer)
-  : "eax", "ebx");
-    return 1;			/* All went well - we hope! */
-}
-
-#define BYTE_IN(reg) \
-	"	inb (%%dx),%%al\n" \
-	"	movb %%al," #reg "\n" \
-	"	addl $2,%%edx\n" \
-	"	movb $0x27,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	movb $0x25,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	subl $2,%%edx\n"
-
-static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
-{
-    /*
-     * %eax scratch
-     * %ebx Data to transfer
-     * %ecx Counter (Don't touch!!)
-     * %edx Port
-     * %esi Source buffer (mem pointer)
-     *
-     * In case you are wondering what the last line of the asm does...
-     * <output allocation> : <input allocation> : <trashed registers>
-     */
-    register int d0;
-
-    asm("shr $2,%%ecx\n" \
-	"	jz .no_more_bulk_bi\n" \
-	"	.p2align 4,,7\n" \
-	".loop_bulk_bi:\n" \
-	BYTE_IN(%%bl) \
-	BYTE_IN(%%bh) \
-	"	rorl $16,%%ebx\n" \
-	BYTE_IN(%%bl) \
-	BYTE_IN(%%bh) \
-	"	rorl $16,%%ebx\n" \
-	"	movl %%ebx,(%%esi)\n" \
-	"	addl $4,%%esi\n" \
-	"	loop .loop_bulk_bi\n" \
-	"	.p2align 4,,7\n" \
-	".no_more_bulk_bi:" \
-  : "=S"(buffer), "=c"(d0)
-  : "1"(len), "d"(base), "0"(buffer)
-  : "eax", "ebx");
-
-    asm("andl $3,%%ecx\n" \
-	"	jz .no_more_loose_bi\n" \
-	"	.p2align 4,,7\n" \
-	".loop_loose_bi:\n" \
-	BYTE_IN((%%esi)) \
-	"	incl %%esi\n" \
-	"	loop .loop_loose_bi\n" \
-	".no_more_loose_bi:\n" \
-  : "=c"(d0)
-  : "0"(len), "d"(base), "S"(buffer)
-  : "eax", "ebx");
-    return 1;			/* All went well - we hope! */
-}
-
-#define NIBBLE_IN(reg) \
-	"	incl %%edx\n" \
-	"	movb $0x04,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	decl %%edx\n" \
-	"	inb (%%dx),%%al\n" \
-	"	andb $0xf0,%%al\n" \
-	"	movb %%al," #reg "\n" \
-	"	incl %%edx\n" \
-	"	movb $0x06,%%al\n" \
-	"	outb %%al,(%%dx)\n" \
-	"	decl %%edx\n" \
-	"	inb (%%dx),%%al\n" \
-	"	shrb $4,%%al\n" \
-	"	orb %%al," #reg "\n"
-
-static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
-{
-    /*
-     * %eax scratch
-     * %ebx Data to transfer
-     * %ecx Counter (Don't touch!!)
-     * %edx Port
-     * %esi Source buffer (mem pointer)
-     *
-     * In case you are wondering what the last line of the asm does...
-     * <output allocation> : <input allocation> : <trashed registers>
-     */
-    register int d0;
-
-    asm("shr $2,%%ecx\n" \
-	"	jz .no_more_bulk_ni\n" \
-	"	.p2align 4,,7\n" \
-	".loop_bulk_ni:\n" \
-	NIBBLE_IN(%%bl) \
-	NIBBLE_IN(%%bh) \
-	"	rorl $16,%%ebx\n" \
-	NIBBLE_IN(%%bl) \
-	NIBBLE_IN(%%bh) \
-	"	rorl $16,%%ebx\n" \
-	"	movl %%ebx,(%%esi)\n" \
-	"	addl $4,%%esi\n" \
-	"	loop .loop_bulk_ni\n" \
-	"	.p2align 4,,7\n" \
-	".no_more_bulk_ni:" \
-  : "=S"(buffer), "=c"(d0)
-  : "1"(len), "d"(str_p), "0"(buffer)
-  : "eax", "ebx");
-
-    asm("andl $3,%%ecx\n" \
-	"	jz .no_more_loose_ni\n" \
-	"	.p2align 4,,7\n" \
-	".loop_loose_ni:\n" \
-	NIBBLE_IN((%%esi)) \
-	"	incl %%esi\n" \
-	"	loop .loop_loose_ni\n" \
-	".no_more_loose_ni:\n" \
-  : "=c"(d0)
-  : "0"(len), "d"(str_p), "S"(buffer)
-  : "eax", "ebx");
-    return 1;			/* All went well - we hope! */
-}
-#else				/* Old style C routines */
-
-static inline int ppa_byte_out(unsigned short base, const char *buffer, int len)
-{
-    unsigned short ctr_p = base + 2;
     int i;
 
     for (i = len; i; i--) {
-	outb(*buffer++, base);
-	outb(0xe, ctr_p);
-	outb(0xc, ctr_p);
+	w_dtr(base, *buffer++);
+	w_ctr(base, 0xe);
+	w_ctr(base, 0xc);
     }
     return 1;			/* All went well - we hope! */
 }
 
-static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
+static int ppa_byte_in(unsigned short base, char *buffer, int len)
 {
-    unsigned short ctr_p = base + 2;
     int i;
 
     for (i = len; i; i--) {
-	*buffer++ = inb(base);
-	outb(0x27, ctr_p);
-	outb(0x25, ctr_p);
+	*buffer++ = r_dtr(base);
+	w_ctr(base, 0x27);
+	w_ctr(base, 0x25);
     }
     return 1;			/* All went well - we hope! */
 }
 
-static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+static int ppa_nibble_in(unsigned short base, char *buffer, int len)
 {
-    unsigned short ctr_p = str_p + 1;
-    unsigned char h, l;
-    int i;
+    for (; len; len--) {
+	unsigned char h;
 
-    for (i = len; i; i--) {
-	outb(0x4, ctr_p);
-	h = inb(str_p);
-	outb(0x6, ctr_p);
-	l = inb(str_p);
-	*buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+	w_ctr(base, 0x4);
+	h = r_str(base) & 0xf0;
+	w_ctr(base, 0x6);
+	*buffer++ = h | ((r_str(base) & 0xf0) >> 4);
     }
     return 1;			/* All went well - we hope! */
 }
-#endif
-
-static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len)
-{
-    int i;
-    for (i = len; i; i--) {
-	outb(*buffer++, epp_p);
-#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
-	if (inb(str_p) & 0x01)
-	    return 0;
-#endif
-    }
-    return 1;
-}
 
 static int ppa_out(int host_no, char *buffer, int len)
 {
@@ -623,16 +410,17 @@
     case PPA_EPP_8:
 	epp_reset(ppb);
 	w_ctr(ppb, 0x4);
-#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
-	r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+	if (!(((long) buffer | len) & 0x01))
+	    outsw(ppb + 4, buffer, len >> 1);
 #else
 	if (!(((long) buffer | len) & 0x03))
 	    outsl(ppb + 4, buffer, len >> 2);
+#endif
 	else
 	    outsb(ppb + 4, buffer, len);
 	w_ctr(ppb, 0xc);
 	r = !(r_str(ppb) & 0x01);
-#endif
 	w_ctr(ppb, 0xc);
 	ecp_sync(ppb);
 	break;
@@ -644,19 +432,6 @@
     return r;
 }
 
-static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len)
-{
-    int i;
-    for (i = len; i; i--) {
-	*buffer++ = inb(epp_p);
-#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
-	if (inb(str_p) & 0x01)
-	    return 0;
-#endif
-    }
-    return 1;
-}
-
 static int ppa_in(int host_no, char *buffer, int len)
 {
     int r;
@@ -671,7 +446,7 @@
     switch (ppa_hosts[host_no].mode) {
     case PPA_NIBBLE:
 	/* 4 bit input, with a loop */
-	r = ppa_nibble_in(ppb + 1, buffer, len);
+	r = ppa_nibble_in(ppb, buffer, len);
 	w_ctr(ppb, 0xc);
 	break;
 
@@ -688,16 +463,17 @@
     case PPA_EPP_8:
 	epp_reset(ppb);
 	w_ctr(ppb, 0x24);
-#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
-	r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
+#ifdef CONFIG_SCSI_IZIP_EPP16
+	if (!(((long) buffer | len) & 0x01))
+	    insw(ppb + 4, buffer, len >> 1);
 #else
 	if (!(((long) buffer | len) & 0x03))
 	    insl(ppb + 4, buffer, len >> 2);
+#endif
 	else
 	    insb(ppb + 4, buffer, len);
 	w_ctr(ppb, 0x2c);
 	r = !(r_str(ppb) & 0x01);
-#endif
 	w_ctr(ppb, 0x2c);
 	ecp_sync(ppb);
 	break;
@@ -885,7 +661,7 @@
 	 * If we have been running for more than a full timer tick
 	 * then take a rest.
 	 */
-	if (time_after(jiffies,start_jiffies + 1))
+	if (time_after(jiffies, start_jiffies + 1))
 	    return 0;
 
 	if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
@@ -1059,8 +835,7 @@
 	    if ((r_str(ppb) & 0x08) == 0x00)
 		retv--;
 
-	    if (retv)
-	    {
+	    if (retv) {
 		if ((jiffies - tmp->jstart) > (1 * HZ)) {
 		    printk("ppa: Parallel port cable is unplugged!!\n");
 		    ppa_fail(host_no, DID_BUS_BUSY);
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.2.0-pre3/linux/drivers/scsi/ppa.h	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/ppa.h	Sat Jan  2 10:21:06 1999
@@ -2,15 +2,15 @@
  * the Iomega ZIP drive
  * 
  * (c) 1996     Grant R. Guenther  grant@torque.net
- *		David Campbell	   campbell@torque.net
+ *              David Campbell     campbell@torque.net
  *
- *	All comments to David.
+ *      All comments to David.
  */
 
 #ifndef _PPA_H
 #define _PPA_H
 
-#define   PPA_VERSION   "2.01"
+#define   PPA_VERSION   "2.03 (for Linux 2.0.0)"
 
 /* 
  * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
@@ -26,21 +26,31 @@
  *
  * Corrected ppa.h for 2.1.x kernels (>=2.1.85)
  * Modified "Nat Semi Kludge" for extended chipsets
- *							[1.41]
+ *                                                      [1.41]
  *
  * Fixed id_probe for EPP 1.9 chipsets (misdetected as EPP 1.7)
- *							[1.42]
+ *                                                      [1.42]
  *
  * Development solely for 2.1.x kernels from now on!
- *							[2.00]
+ *                                                      [2.00]
  *
  * Hack and slash at the init code (EPP device check routine)
  * Added INSANE option.
- *							[2.01]
+ *                                                      [2.01]
  *
  * Patch applied to sync against the 2.1.x kernel code
  * Included qboot_zip.sh
- *							[2.02]
+ *                                                      [2.02]
+ *
+ * Cleaned up the mess left by someone else trying to fix the
+ * asm section to keep egcc happy. The asm section no longer
+ * exists, the nibble code is *almost* as fast as the asm code
+ * providing it is compiled with egcc.
+ *
+ * Other clean ups include the follow changes:
+ *    CONFIG_SCSI_PPA_HAVE_PEDANTIC => CONFIG_SCSI_IZIP_EPP16
+ *    added CONFIG_SCSI_IZIP_SLOW_CTR option
+ *                                                      [2.03]
  */
 /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
 
@@ -80,7 +90,11 @@
     "PS/2",
     "EPP 8 bit",
     "EPP 16 bit",
+#ifdef CONFIG_SCSI_IZIP_EPP16
+    "EPP 16 bit",
+#else
     "EPP 32 bit",
+#endif
     "Unknown"};
 
 /* This is a global option */
@@ -98,23 +112,6 @@
 #define CONNECT_EPP_MAYBE 1
 #define CONNECT_NORMAL  0
 
-/* INSANE code */
-#define PPA_INSANE 0
-#if PPA_INSANE > 0
-#define r_dtr(x)        (unsigned char)inb_p((x))
-#define r_str(x)        (unsigned char)inb_p((x)+1)
-#define r_ctr(x)        (unsigned char)inb_p((x)+2)
-#define r_epp(x)        (unsigned char)inb_p((x)+4)
-#define r_fifo(x)       (unsigned char)inb_p((x)+0x400)
-#define r_ecr(x)        (unsigned char)inb_p((x)+0x402)
-
-#define w_dtr(x,y)      outb_p(y, (x))
-#define w_str(x,y)      outb_p(y, (x)+1)
-#define w_ctr(x,y)      outb_p(y, (x)+2)
-#define w_epp(x,y)      outb_p(y, (x)+4)
-#define w_fifo(x,y)     outb_p(y, (x)+0x400)
-#define w_ecr(x,y)      outb_p(y, (x)+0x402)
-#else /* PPA_INSANE */
 #define r_dtr(x)        (unsigned char)inb((x))
 #define r_str(x)        (unsigned char)inb((x)+1)
 #define r_ctr(x)        (unsigned char)inb((x)+2)
@@ -124,11 +121,15 @@
 
 #define w_dtr(x,y)      outb(y, (x))
 #define w_str(x,y)      outb(y, (x)+1)
-#define w_ctr(x,y)      outb(y, (x)+2)
 #define w_epp(x,y)      outb(y, (x)+4)
 #define w_fifo(x,y)     outb(y, (x)+0x400)
 #define w_ecr(x,y)      outb(y, (x)+0x402)
-#endif	/* PPA_INSANE */
+
+#ifdef CONFIG_SCSI_IZIP_SLOW_CTR
+#define w_ctr(x,y)      outb_p(y, (x)+2)
+#else
+#define w_ctr(x,y)      outb(y, (x)+2)
+#endif
 
 static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
 static int ppa_in(int, char *, int);
@@ -154,7 +155,7 @@
 
 #define PPA {	proc_dir:			&proc_scsi_ppa,		\
 		proc_info:			ppa_proc_info,		\
-		name:				"Iomega parport ZIP drive",\
+		name:				"Iomega VPI0 (ppa) interface",\
 		detect:				ppa_detect,		\
 		release:			ppa_release,		\
 		command:			ppa_command,		\
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- v2.2.0-pre3/linux/drivers/scsi/scsi_debug.c	Fri Oct 23 22:01:21 1998
+++ linux/drivers/scsi/scsi_debug.c	Sat Jan  2 10:23:01 1999
@@ -518,10 +518,10 @@
 static void scsi_debug_intr_handle(unsigned long indx)
 {
     Scsi_Cmnd * SCtmp;
-    int pending;
     void (*my_done)(Scsi_Cmnd *); 
-    unsigned long flags;
+#ifdef DEBUG
     int to;
+#endif
     
 #if 0
     del_timer(&timeout[indx]);
@@ -565,9 +565,11 @@
 
 int scsi_debug_abort(Scsi_Cmnd * SCpnt)
 {
+#if 0
     int j;
     void (*my_done)(Scsi_Cmnd *);
     unsigned long flags;
+#endif
     
     DEB(printk("scsi_debug_abort\n"));
 #if 0
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.2.0-pre3/linux/drivers/scsi/scsi_proc.c	Thu Nov 19 09:56:28 1998
+++ linux/drivers/scsi/scsi_proc.c	Sat Jan  2 17:55:06 1999
@@ -307,6 +307,13 @@
     *size = y; 
     return;
 }
+
+#else
+
+void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
+{
+}
+
 #endif /* CONFIG_PROC_FS */
 
 /*
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.2.0-pre3/linux/drivers/scsi/scsi_syms.c	Thu May  7 22:51:51 1998
+++ linux/drivers/scsi/scsi_syms.c	Sat Jan  2 17:55:06 1999
@@ -72,9 +72,7 @@
 
 EXPORT_SYMBOL(scsi_sleep);
 
-#if defined(CONFIG_PROC_FS)
 EXPORT_SYMBOL(proc_print_scsidevice);
-#endif
 /*
  * These are here only while I debug the rest of the scsi stuff.
  */
diff -u --recursive --new-file v2.2.0-pre3/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.2.0-pre3/linux/drivers/sound/soundcard.c	Tue Dec 22 14:16:56 1998
+++ linux/drivers/sound/soundcard.c	Sat Jan  2 17:55:06 1999
@@ -909,8 +909,10 @@
 	{
 		return;
 	}
+#ifdef CONFIG_PROC_FS	
         if (proc_unregister(&proc_root, PROC_SOUND))
 		printk(KERN_ERR "sound: unregistering /proc/sound failed\n");
+#endif		
 	if (chrdev_registered)
 		destroy_special_devices();
 
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c
--- v2.2.0-pre3/linux/fs/binfmt_misc.c	Fri Jan  1 12:58:20 1999
+++ linux/fs/binfmt_misc.c	Sat Jan  2 17:55:06 1999
@@ -30,6 +30,16 @@
 #include <asm/uaccess.h>
 #include <asm/spinlock.h>
 
+/*
+ * We should make this work with a "stub-only" /proc,
+ * which would just not be able to be configured.
+ * Right now the /proc-fs support is too black and white,
+ * though, so just remind people that this should be
+ * fixed..
+ */
+#ifndef CONFIG_PROC_FS
+#error You really need /proc support for binfmt_misc. Please reconfigure!
+#endif
 
 #define VERBOSE_STATUS /* undef this to save 400 bytes kernel memory */
 
@@ -67,9 +77,7 @@
 static int free_id = 1;
 static int enabled = 1;
 
-#ifdef CONFIG_SMP
-static rwlock_t entries_lock = RW_LOCK_UNLOCKED;
-#endif
+static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
 
 
 /*
@@ -458,8 +466,10 @@
 {
 	if (!(e->proc_dir = create_proc_entry(e->proc_name,
 			 	S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
-		return -ENOMEM;
-
+	{
+		printk(KERN_WARNING "Unable to create /proc entry.\n");
+		return -ENOENT;
+	}
 	e->proc_dir->data = (void *) (e->id);
 	e->proc_dir->read_proc = proc_read_status;
 	e->proc_dir->write_proc = proc_write_status;
@@ -486,7 +496,7 @@
 
 int __init init_misc_binfmt(void)
 {
-	int error = -ENOMEM;
+	int error = -ENOENT;
 	struct proc_dir_entry *status = NULL, *reg;
 
 	bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.2.0-pre3/linux/fs/fat/inode.c	Fri Jan  1 12:58:20 1999
+++ linux/fs/fat/inode.c	Sat Jan  2 10:35:24 1999
@@ -364,8 +364,8 @@
 		MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster);
 		MSDOS_SB(sb)->fsinfo_offset =
 			CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
-		if (MSDOS_SB(sb)->fsinfo_offset + sizeof(MSDOS_SB(sb)->fsinfo_offset) >= sizeof(struct fat_boot_sector)) {
-			printk("fat_read_super: Bad fsinfo_offset\n");
+		if (MSDOS_SB(sb)->fsinfo_offset + sizeof(MSDOS_SB(sb)->fsinfo_offset) > sb->s_blocksize) {
+			printk("fat_read_super: Bad fsinfo_offset 0x%x\n", MSDOS_SB(sb)->fsinfo_offset);
 			fat_brelse(sb, bh);
 			goto out_invalid;
 		}
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.2.0-pre3/linux/fs/nfs/write.c	Tue Dec 22 14:16:57 1998
+++ linux/fs/nfs/write.c	Sat Jan  2 10:16:39 1999
@@ -263,12 +263,8 @@
 static inline void
 free_write_request(struct nfs_wreq * req)
 {
-	if (!--req->wb_count) {
-		struct inode *inode = req->wb_inode;
-		remove_write_request(&NFS_WRITEBACK(inode), req);
+	if (!--req->wb_count)
 		kfree(req);
-		nr_write_requests--;
-	}
 }
 
 /*
@@ -698,6 +694,8 @@
 		clear_bit(PG_uptodate, &page->flags);
 
 	__free_page(page);
+	remove_write_request(&NFS_WRITEBACK(inode), req);
+	nr_write_requests--;
 	dput(req->wb_dentry);
 
 	wake_up(&req->wb_wait);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.2.0-pre3/linux/fs/nfsd/nfsctl.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/nfsd/nfsctl.c	Sat Jan  2 17:55:06 1999
@@ -54,8 +54,6 @@
 
 static int	initialized = 0;
 
-#ifdef CONFIG_PROC_FS
-
 int exp_procfs_exports(char *buffer, char **start, off_t offset,
                              int length, int *eof, void *data);
 
@@ -70,7 +68,6 @@
 	nfs_export_ent->read_proc = exp_procfs_exports;
 }
 
-#endif
 
 /*
  * Initialize nfsd
@@ -79,16 +76,12 @@
 nfsd_init(void)
 {
 	nfsd_xdr_init();	/* XDR */
-#ifdef CONFIG_PROC_FS
 	nfsd_stat_init();	/* Statistics */
-#endif
 	nfsd_cache_init();	/* RPC reply cache */
 	nfsd_export_init();	/* Exports table */
 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
 	nfsd_fh_init();		/* FH table */
-#ifdef CONFIG_PROC_FS
 	proc_export_init();
-#endif
 	initialized = 1;
 }
 
@@ -310,11 +303,9 @@
 	nfsd_export_shutdown();
 	nfsd_cache_shutdown();
 	nfsd_fh_free();
-#ifdef CONFIG_PROC_FS
 	remove_proc_entry("fs/nfs/exports", NULL);
 	remove_proc_entry("fs/nfs", NULL);
 	nfsd_stat_shutdown();
-#endif
 	nfsd_lockd_shutdown();
 }
 #endif
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.2.0-pre3/linux/fs/nfsd/stats.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/nfsd/stats.c	Sat Jan  2 17:55:05 1999
@@ -27,7 +27,6 @@
 struct nfsd_stats	nfsdstats;
 struct svc_stat		nfsd_svcstats = { &nfsd_program, };
 
-#ifdef CONFIG_PROC_FS
 static int
 nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
 				int *eof, void *data)
@@ -90,4 +89,3 @@
 {
 	svc_proc_unregister("nfsd");
 }
-#endif /* CONFIG_PROC_FS */
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.2.0-pre3/linux/fs/nfsd/vfs.c	Thu Dec 31 10:29:02 1998
+++ linux/fs/nfsd/vfs.c	Sat Jan  2 09:54:30 1999
@@ -993,7 +993,7 @@
 static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
 {
 	if (s1 != s2) {
-		if ((unsigned long) s1 < (unsigned long) s2) {
+		if ((unsigned long) s1 > (unsigned long) s2) {
 			struct semaphore *tmp = s1;
 			s1 = s2;
 			s2 = tmp;
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/Makefile linux/fs/ntfs/Makefile
--- v2.2.0-pre3/linux/fs/ntfs/Makefile	Fri Jan  2 01:42:58 1998
+++ linux/fs/ntfs/Makefile	Sat Jan  2 10:24:46 1999
@@ -3,7 +3,7 @@
 O_TARGET := ntfs.o
 O_OBJS   := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o
 M_OBJS   := $(O_TARGET)
-EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"980101\"
+EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"990102\"
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/attr.c linux/fs/ntfs/attr.c
--- v2.2.0-pre3/linux/fs/ntfs/attr.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ntfs/attr.c	Sat Jan  2 10:24:46 1999
@@ -1,11 +1,12 @@
 /*
  *  attr.c
  *
- *  Copyright (C) 1996-1997 Martin von Löwis
+ *  Copyright (C) 1996-1998 Martin von Löwis
  *  Copyright (C) 1996-1997 Régis Duchesne
+ *  Copyright (C) 1998 Joseph Malicki
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "attr.h"
 
@@ -81,7 +82,7 @@
 
 /* Store in the inode readable information about a run */
 static void
-ntfs_insert_run(ntfs_attribute *attr,int cnum,int cluster,int len)
+ntfs_insert_run(ntfs_attribute *attr,int cnum,ntfs_cluster_t cluster,int len)
 {
 	/* (re-)allocate space if necessary */
 	if(attr->d.r.len % 8 == 0) {
@@ -107,11 +108,17 @@
 {
 	int error=0;
 	ntfs_runlist *rl;
-	int rlen,cluster;
+	int rlen;
+	ntfs_cluster_t cluster;
 	int clen;
 	if(attr->compressed)return EOPNOTSUPP;
-	if(attr->resident)return EOPNOTSUPP;
 	if(ino->record_count>1)return EOPNOTSUPP;
+	if(attr->resident) {
+		error = ntfs_make_attr_nonresident(ino,attr);
+		if(error)
+			return error;
+	}
+
 	rl=attr->d.r.runlist;
 	rlen=attr->d.r.len-1;
 	if(rlen>=0)
@@ -121,18 +128,20 @@
 		cluster=0;
 	/* round up to multiple of cluster size */
 	clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize;
+	if(clen==0)
+		return 0;
 	/* FIXME: try to allocate smaller pieces */
 	error=ntfs_allocate_clusters(ino->vol,&cluster,&clen,
 				     flags|ALLOC_REQUIRE_SIZE);
 	if(error)return error;
-	attr->allocated+=clen;
+	attr->allocated += clen*ino->vol->clustersize;
 	*len=clen*ino->vol->clustersize;
 	/* contiguous chunk */
 	if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){
 		rl[rlen].len+=clen;
 		return 0;
 	}
-	ntfs_insert_run(attr,rlen+1,cluster,*len);
+	ntfs_insert_run(attr,rlen+1,cluster,clen);
 	return 0;
 }
 
@@ -158,6 +167,22 @@
 	return ntfs_readwrite_attr(ino,attr,0,&io);
 }
 
+int
+ntfs_attr_allnonresident(ntfs_inode *ino)
+{
+	int i, error=0;
+        ntfs_volume *vol = ino->vol;
+
+	for (i=0; !error && i<ino->attr_count; i++)
+	{
+		if (ino->attrs[i].type != vol->at_security_descriptor
+		    && ino->attrs[i].type != vol->at_data)
+			continue;
+		error = ntfs_make_attr_nonresident (ino, ino->attrs+i);
+	}
+	return error;
+}
+
 /* Resize the attribute to a newsize */
 int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize)
 {
@@ -175,7 +200,7 @@
 		return EOPNOTSUPP;
 	if(attr->resident){
 		void *v;
-		if(newsize>ino->vol->clustersize){
+		if(newsize>ino->vol->mft_recordsize){
 			error=ntfs_make_attr_nonresident(ino,attr);
 			if(error)return error;
 			return ntfs_resize_attr(ino,attr,newsize);
@@ -185,8 +210,10 @@
 			attr->d.data=ntfs_malloc(newsize);
 			if(!attr->d.data)
 				return ENOMEM;
-			ntfs_bzero(attr->d.data+oldsize,newsize);
-			ntfs_memcpy(attr->d.data,v,min(newsize,oldsize));
+			if(newsize>oldsize)
+				ntfs_bzero((char*)attr->d.data+oldsize,
+					   newsize-oldsize);
+			ntfs_memcpy((char*)attr->d.data,v,min(newsize,oldsize));
 		}else
 			attr->d.data=0;
 		ntfs_free(v);
@@ -199,7 +226,7 @@
 		for(i=0,count=0;i<attr->d.r.len;i++){
 			if((count+rl[i].len)*clustersize>newsize)
 				break;
-			count+=rl[i].len;
+			count+=(int)rl[i].len;
 		}
 		newlen=i+1;
 		/* free unused clusters in current run, unless sparse */
@@ -208,7 +235,7 @@
 			int rounded=newsize-count*clustersize;
 			rounded=(rounded+clustersize-1)/clustersize;
 			error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded,
-						       rl[i].len-rounded);
+						       (int)rl[i].len-rounded);
 			if(error)
 				return error; /* FIXME: incomplete operation */
 			rl[i].len=rounded;
@@ -217,7 +244,7 @@
 		/* free all other runs */
 		for(i++;i<attr->d.r.len;i++)
 			if(rl[i].cluster!=-1){
-				error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,rl[i].len);
+				error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,(int)rl[i].len);
 				if(error)
 					return error; /* FIXME: incomplete operation */
 			}
@@ -232,7 +259,7 @@
 	/* fill in new sizes */
 	attr->allocated = newcount*clustersize;
 	attr->size = newsize;
-	attr->initialized = newsize;
+	/* attr->initialized does not change. */
 	if(!newsize)
 		error=ntfs_make_attr_resident(ino,attr);
 	return error;
@@ -296,7 +323,8 @@
 {
 	int startvcn,endvcn;
 	int vcn,cnum;
-	int cluster,len,ctype;
+	ntfs_cluster_t cluster;
+	int len,ctype;
 	startvcn = NTFS_GETU64(data+0x10);
 	endvcn = NTFS_GETU64(data+0x18);
 
@@ -383,13 +411,29 @@
 	return 0;
 }
 
+int
+ntfs_read_zero(ntfs_io *dest,int size)
+{
+	char *sparse=ntfs_calloc(512);
+	if(!sparse)
+		return ENOMEM;
+	while(size){
+		int i=min(size,512);
+		dest->fn_put(dest,sparse,i);
+		size-=i;
+	}
+	ntfs_free(sparse);
+	return 0;
+}
+
 /* process compressed attributes */
 int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
 	ntfs_io *dest)
 {
 	int error=0;
 	int clustersize,l;
-	int s_vcn,rnum,vcn,cluster,len,chunk,got,cl1,l1,offs1,copied;
+	int s_vcn,rnum,vcn,len,chunk,got,l1,offs1,copied;
+	ntfs_cluster_t cluster,cl1;
 	char *comp=0,*comp1;
 	char *decomp=0;
 	ntfs_io io;
@@ -426,18 +470,13 @@
 		chunk=0;
 		if(cluster==-1){
 			/* sparse cluster */
-			char *sparse=ntfs_calloc(512);
 			int l1;
-			if(!sparse)return ENOMEM;
 			if((len-(s_vcn-vcn)) & 15)
 				ntfs_error("unexpected sparse chunk size");
 			l1=chunk = min((vcn+len)*clustersize-offset,l);
-			while(l1){
-				int i=min(l1,512);
-				dest->fn_put(dest,sparse,i);
-				l1-=i;
-			}
-			ntfs_free(sparse);
+			error = ntfs_read_zero(dest,l1);
+			if(error)
+				goto out;
 		}else if(dest->do_read){
 			if(!comp){
 				comp=ntfs_malloc(16*clustersize);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/attr.h linux/fs/ntfs/attr.h
--- v2.2.0-pre3/linux/fs/ntfs/attr.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/attr.h	Sat Jan  2 10:24:46 1999
@@ -15,3 +15,6 @@
   ntfs_io *dest);
 int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
   int dsize, ntfs_attribute **rattr);
+int ntfs_read_zero(ntfs_io *dest,int size);
+int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr);
+int ntfs_attr_allnonresident(ntfs_inode *ino);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/dir.c linux/fs/ntfs/dir.c
--- v2.2.0-pre3/linux/fs/ntfs/dir.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ntfs/dir.c	Sat Jan  2 10:24:46 1999
@@ -4,9 +4,10 @@
  *  Copyright (C) 1995-1997 Martin von Löwis
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "dir.h"
+#include "macros.h"
 
 #include <linux/errno.h>
 #include "super.h"
@@ -26,27 +27,27 @@
 				 ino->u.index.recordsize);
 }
 
-static inline int ntfs_is_top(long long stack)
+static inline int ntfs_is_top(ntfs_u64 stack)
 {
 	return stack==14;
 }
 
-static long long ntfs_pop(long long *stack)
+static int ntfs_pop(ntfs_u64 *stack)
 {
 	static int width[16]={1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
 	int res=-1;
 	switch(width[*stack & 15])
 	{
-	case 1:res=(*stack&15)>>1;
+	case 1:res=(int)((*stack&15)>>1);
 		*stack>>=4;
 		break;
-	case 2:res=((*stack&63)>>2)+7;
+	case 2:res=(int)(((*stack&63)>>2)+7);
 		*stack>>=6;
 		break;
-	case 3:res=((*stack & 255)>>3)+23;
+	case 3:res=(int)(((*stack & 255)>>3)+23);
 		*stack>>=8;
 		break;
-	case 4:res=((*stack & 1023)>>4)+55;
+	case 4:res=(int)(((*stack & 1023)>>4)+55);
 		*stack>>=10;
 		break;
 	default:ntfs_error("Unknown encoding\n");
@@ -59,18 +60,18 @@
 	return 14;
 }
 
-static long long ntfs_push(long long stack,int i)
+static ntfs_u64 ntfs_push(ntfs_u64 stack,int i)
 {
 	if(i<7)return (stack<<4)|(i<<1);
 	if(i<23)return (stack<<6)|((i-7)<<2)|1;
 	if(i<55)return (stack<<8)|((i-23)<<3)|3;
 	if(i<120)return (stack<<10)|((i-55)<<4)|7;
 	ntfs_error("Too many entries\n");
-	return -1;
+	return 0xFFFFFFFFFFFFFFFF;
 }
 
 #if 0
-static void ntfs_display_stack(long long stack)
+static void ntfs_display_stack(ntfs_u64 stack)
 {
 	while(!ntfs_is_top(stack))
 	{
@@ -162,35 +163,30 @@
 	if(walk->newblock * vol->clustersize >= size){
 		/* build index record */
 		int s1=walk->dir->u.index.recordsize;
+		int nr_fix = s1/vol->blocksize+1;
+		int hsize;
 		char *record=ntfs_malloc(s1);
-		int newlen;
 		ntfs_bzero(record,s1);
 		/* magic */
 		ntfs_memcpy(record,"INDX",4);
 		/* offset to fixups */
 		NTFS_PUTU16(record+4,0x28);
 		/* number of fixups */
-		NTFS_PUTU16(record+6,s1/vol->blocksize+1);
+		NTFS_PUTU16(record+6,nr_fix);
 		/* FIXME: log file number */
 		/* VCN of buffer */
 		NTFS_PUTU64(record+0x10,walk->newblock);
-		/* header size. FIXME */
-		NTFS_PUTU16(record+0x18,28);
+		/* header size. */
+		hsize = 0x10+2*nr_fix;
+		hsize = (hsize+7) & ~7; /* Align. */
+		NTFS_PUTU16(record+0x18, hsize);
 		/* total size of record */
 		NTFS_PUTU32(record+0x20,s1-0x18);
+		/* Writing the data will extend the attribute. */
 		io.param=record;
-		newlen=walk->dir->u.index.recordsize;
-		/* allocate contiguous index record */
-		error=ntfs_extend_attr(walk->dir,allocation,&newlen,
-				       ALLOC_REQUIRE_SIZE);
-		if(error){
-			/* FIXME: try smaller allocations */
-			ntfs_free(record);
-			return ENOSPC;
-		}
 		io.size=s1;
-		error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
-				      size,&io);
+		io.do_read=0;
+		error=ntfs_readwrite_attr(walk->dir, allocation, size, &io);
 		if(error || io.size!=s1){
 			ntfs_free(record);
 			return error?error:EIO;
@@ -201,29 +197,38 @@
 	return 0;
 }
 
+/* Write an index block (root or allocation) back to storage.
+   used is the total number of bytes in buf, including all headers. */
+
 static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
-	int used)
+				int used)
 {
 	ntfs_io io;
 	int error;
+	ntfs_attribute *a;
+	ntfs_volume *vol = walk->dir->vol;
+	
 	io.fn_put=0;
 	io.fn_get=ntfs_get;
 	io.param=buf;
-	if(walk->block==-1){
+	if(block==-1){
 		NTFS_PUTU16(buf+0x14,used-0x10);
 		/* 0x18 is a copy thereof */
 		NTFS_PUTU16(buf+0x18,used-0x10);
 		io.size=used;
-		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_root,
+		error=ntfs_write_attr(walk->dir,vol->at_index_root,
 				      I30,0,&io);
 		if(error)return error;
 		if(io.size!=used)return EIO;
+		/* shrink if necessary */
+		a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
+		ntfs_resize_attr(walk->dir, a, used);
 	}else{
-		NTFS_PUTU16(buf+0x1C,used-0x20);
-		ntfs_insert_fixups(buf,walk->dir->vol->blocksize);
+		NTFS_PUTU16(buf+0x1C,used-0x18);
+		ntfs_insert_fixups(buf,vol->blocksize);
 		io.size=walk->dir->u.index.recordsize;
-		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
-				      walk->block*walk->dir->vol->clustersize,
+		error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
+				      block*vol->clustersize,
 				      &io);
 		if(error)return error;
 		if(io.size!=walk->dir->u.index.recordsize)
@@ -240,13 +245,20 @@
 	int error,othersize,mlen;
 	ntfs_io io;
 	ntfs_volume *vol=walk->dir->vol;
+	int oldblock;
+
 	error=ntfs_allocate_index_block(walk);
 	if(error)
 		return error;
-	for(entry=prev=start+NTFS_GETU16(start+0x18)+0x18;
-	    entry-start<usize/2;
-	    entry+=NTFS_GETU16(entry+8))
+	/* This should not happen */
+	if(walk->block == -1){
+		ntfs_error("Trying to split root");
+		return EOPNOTSUPP;
+	}
+	entry = start+NTFS_GETU16(start+0x18)+0x18; 
+	for(prev=entry; entry-start<usize/2; entry += NTFS_GETU16(entry+8))
 		prev=entry;
+
 	newbuf=ntfs_malloc(vol->index_recordsize);
 	if(!newbuf)
 		return ENOMEM;
@@ -266,11 +278,19 @@
 	/* copy everything from entry to new block */
 	othersize=usize-(entry-start);
 	ntfs_memcpy(newbuf+NTFS_GETU16(newbuf+0x18)+0x18,entry,othersize);
-	error=ntfs_index_writeback(walk,newbuf,walk->newblock,othersize);
+	/* Copy flags. */
+	NTFS_PUTU32(newbuf+0x24, NTFS_GETU32(start+0x24));
+	error=ntfs_index_writeback(walk,newbuf,walk->newblock,
+				   othersize+NTFS_GETU16(newbuf+0x18)+0x18);
 	if(error)goto out;
 
 	/* move prev to walk */
 	mlen=NTFS_GETU16(prev+0x8);
+	/* Remember old child node. */
+	if(ntfs_entry_has_subnodes(prev))
+		oldblock = NTFS_GETU32(prev+mlen-8);
+	else
+		oldblock = -1;
 	/* allow for pointer to subnode */
 	middle=ntfs_malloc(ntfs_entry_has_subnodes(prev)?mlen:mlen+8);
 	if(!middle){
@@ -286,8 +306,21 @@
 		ntfs_error("entry not reset");
 	walk->new_entry=middle;
 	walk->u.flags|=ITERATE_SPLIT_DONE;
+	/* Terminate old block. */
+	othersize = usize-(prev-start);
+	NTFS_PUTU64(prev, 0);
+	if(oldblock==-1){
+		NTFS_PUTU32(prev+8, 0x10);
+		NTFS_PUTU32(prev+0xC, 2);
+		othersize += 0x10;
+	}else{
+		NTFS_PUTU32(prev+8, 0x18);
+		NTFS_PUTU32(prev+0xC, 3);
+		NTFS_PUTU64(prev+0x10, oldblock);
+		othersize += 0x18;
+	}
 	/* write back original block */
-	error=ntfs_index_writeback(walk,start,walk->block,usize-(prev-start));
+	error=ntfs_index_writeback(walk,start,walk->block,othersize);
  out:
 	if(newbuf)ntfs_free(newbuf);
 	if(middle)ntfs_free(middle);
@@ -300,12 +333,11 @@
 	int do_split=0;
 	offset=entry-start;
 	if(walk->block==-1){ /*index root */
-		/* FIXME: adjust to maximum allowed index root value */
 		blocksize=walk->dir->vol->mft_recordsize;
 		usedsize=NTFS_GETU16(start+0x14)+0x10;
 	}else{
 		blocksize=walk->dir->u.index.recordsize;
-		usedsize=NTFS_GETU16(start+0x1C)+0x20;
+		usedsize=NTFS_GETU16(start+0x1C)+0x18;
 	}
 	if(usedsize+walk->new_entry_size > blocksize){
 		char* s1=ntfs_malloc(blocksize+walk->new_entry_size);
@@ -321,7 +353,6 @@
 	usedsize+=walk->new_entry_size;
 	ntfs_free(walk->new_entry);
 	walk->new_entry=0;
-	/*FIXME: split root */
 	if(do_split){
 		error=ntfs_split_record(walk,start,blocksize,usedsize);
 		ntfs_free(start);
@@ -330,6 +361,81 @@
 	return 0;
 }
 
+/* Try to split INDEX_ROOT attributes. Return E2BIG if nothing changed. */
+
+int
+ntfs_split_indexroot(ntfs_inode *ino)
+{
+	ntfs_attribute *ra;
+	ntfs_u8 *root=0, *index=0;
+	ntfs_io io;
+	int error, off, i, bsize, isize;
+	ntfs_iterate_s walk;
+
+	ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
+	if(!ra)
+		return E2BIG;
+	bsize = ino->vol->mft_recordsize;
+	root = ntfs_malloc(bsize);
+	if(!root)
+		return E2BIG;
+	io.fn_put = ntfs_put;
+	io.param = root;
+	io.size = bsize;
+	error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
+	if(error)
+		goto out;
+	off = 0x20;
+	/* Count number of entries. */
+	for(i = 0; ntfs_entry_is_used(root+off); i++)
+		off += NTFS_GETU16(root+off+8);
+	if(i<=2){
+		/* We don't split small index roots. */
+		error = E2BIG;
+		goto out;
+	}
+	index = ntfs_malloc(ino->vol->index_recordsize);
+	if(!index)
+		goto out;
+	walk.dir = ino;
+	walk.block = -1;
+	walk.result = walk.new_entry = 0;
+	walk.name = 0;
+	error = ntfs_allocate_index_block(&walk);
+	if(error)
+		goto out;
+
+	/* Write old root to new index block. */
+	io.param = index;
+	io.size = ino->vol->index_recordsize;
+	error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
+			       walk.newblock*ino->vol->clustersize, &io);
+	if(error)
+		goto out;
+	isize = NTFS_GETU16(root+0x18) - 0x10;
+	ntfs_memcpy(index+NTFS_GETU16(index+0x18)+0x18, root+0x20, isize);
+	/* Copy flags. */
+	NTFS_PUTU32(index+0x24, NTFS_GETU32(root+0x1C));
+
+	error = ntfs_index_writeback(&walk, index, walk.newblock, 
+				     isize+NTFS_GETU16(index+0x18)+0x18);
+	if(error)
+		goto out;
+
+	/* Mark root as split. */
+	NTFS_PUTU32(root+0x1C, 1);
+	/* Truncate index root. */
+	NTFS_PUTU64(root+0x20, 0);
+	NTFS_PUTU32(root+0x28, 0x18);
+	NTFS_PUTU32(root+0x2C, 3);
+	NTFS_PUTU64(root+0x30, walk.newblock);
+	error = ntfs_index_writeback(&walk,root,-1,0x38);
+ out:
+	ntfs_free(root);
+	ntfs_free(index);
+	return error;
+}
+
 /* The entry has been found. Copy the result in the caller's buffer */
 static int ntfs_copyresult(char *dest,char *source)
 {
@@ -419,9 +525,9 @@
 	error=ntfs_getdir_record(walk,nextblock);
 	if(!error && walk->type==DIR_INSERT && 
 	   (walk->u.flags & ITERATE_SPLIT_DONE)){
-		/* split has occurred. adjust entry, insert new_entry */
+		/* Split has occurred. Adjust entry, insert new_entry. */
 		NTFS_PUTU32(entry+length-8,walk->newblock);
-		/* reset flags, as the current block might be split again */
+		/* Reset flags, as the current block might be split again. */
 		walk->u.flags &= ~ITERATE_SPLIT_DONE;
 		error=ntfs_dir_insert(walk,start,entry);
 	}
@@ -637,6 +743,7 @@
 		ntfs_error("Inode %d has no volume\n",ino->i_number);
 		return EINVAL;
 	}
+	ntfs_debug(DEBUG_DIR3,"unsorted 1\n");
 	/* are we still in the index root */
 	if(*p_high==0){
 		buf=ntfs_malloc(length=vol->mft_recordsize);
@@ -651,6 +758,7 @@
 		ino->u.index.recordsize = NTFS_GETU32(buf+0x8);
 		ino->u.index.clusters_per_record = NTFS_GETU32(buf+0xC);
 		entry=buf+0x20;
+		ntfs_debug(DEBUG_DIR3,"unsorted 2\n");
 	}else{ /* we are in an index record */
 		length=ino->u.index.recordsize;
 		buf=ntfs_malloc(length);
@@ -673,14 +781,17 @@
 			return ENOTDIR;
 		}
 		entry=buf+NTFS_GETU16(buf+0x18)+0x18;
+		ntfs_debug(DEBUG_DIR3,"unsorted 3\n");
 	}
 
 	/* process the entries */
 	start=*p_low;
 	while(ntfs_entry_is_used(entry)){
+		ntfs_debug(DEBUG_DIR3,"unsorted 4\n");
 		if(start)
 			start--; /* skip entries that were already processed */
 		else{
+			ntfs_debug(DEBUG_DIR3,"unsorted 5\n");
 			if((error=cb(entry,param)))
 				/* the entry could not be processed */
 				break;
@@ -689,9 +800,11 @@
 		entry+=NTFS_GETU16(entry+8);
 	}
 
+	ntfs_debug(DEBUG_DIR3,"unsorted 6\n");
 	/* caller did not process all entries */
 	if(error){
 		ntfs_free(buf);
+		ntfs_debug(DEBUG_DIR3,"unsorted 7\n");
 		return error;
 	}
 
@@ -704,6 +817,7 @@
 		/* directory does not have index allocation */
 		*p_high=0xFFFFFFFF;
 		*p_low=0;
+		ntfs_debug(DEBUG_DIR3,"unsorted 8\n");
 		return 0;
 	}
 	buf=ntfs_malloc(length=attr->size);
@@ -713,6 +827,7 @@
 	if(!error && io.size!=length)error=EIO;
 	if(error){
 		ntfs_free(buf);
+		ntfs_debug(DEBUG_DIR3,"unsorted 9\n");
 		return EIO;
 	}
 	attr=ntfs_find_attr(ino,vol->at_index_allocation,I30);
@@ -721,6 +836,7 @@
 			/* no more index records */
 			*p_high=0xFFFFFFFF;
 			ntfs_free(buf);
+			ntfs_debug(DEBUG_DIR3,"unsorted 10\n");
 			return 0;
 		}
 		*p_high+=ino->u.index.clusters_per_record;
@@ -731,6 +847,7 @@
 		if(buf[byte] & bit)
 			break;
 	}
+	ntfs_debug(DEBUG_DIR3,"unsorted 11\n");
 	ntfs_free(buf);
 	return 0;
 }
@@ -800,6 +917,62 @@
 	return error;
 }
 #endif
+
+/* Fills out and creates an INDEX_ROOT attribute. */
+
+static int
+add_index_root (ntfs_inode *ino, int type)
+{
+	ntfs_attribute *da;
+	ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry */
+	char name[10];
+
+	NTFS_PUTU32(data, type);
+	/* ??? */
+	NTFS_PUTU32(data+4, 1);
+	NTFS_PUTU32(data+8, ino->vol->index_recordsize);
+	NTFS_PUTU32(data+0xC, ino->vol->index_clusters_per_record);
+	/* ??? */
+	NTFS_PUTU32(data+0x10, 0x10);
+	/* Size of entries, including header. */
+	NTFS_PUTU32(data+0x14, 0x20);
+	NTFS_PUTU32(data+0x18, 0x20);
+	/* No index allocation, yet. */
+	NTFS_PUTU32(data+0x1C, 0);
+	/* add last entry. */
+	/* indexed MFT record. */
+	NTFS_PUTU64(data+0x20, 0);
+	/* size of entry */
+	NTFS_PUTU32(data+0x28, 0x10);
+	/* flags: last entry, no child nodes. */
+	NTFS_PUTU32(data+0x2C, 2);
+	/* compute name */
+	ntfs_indexname(name, type);
+	return ntfs_create_attr(ino, ino->vol->at_index_root, name,
+				data, sizeof(data), &da);
+}
+
+int
+ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *result)
+{
+	int error;
+	error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
+	if(error)
+		goto out;
+	error = add_index_root(result, 0x30);
+	if (error)
+		goto out;
+	/* Set directory bit */
+	result->attr[0x16] |= 2;
+	error = ntfs_update_inode (dir);
+	if (error)
+		goto out;
+	error = ntfs_update_inode (result);
+	if (error)
+		goto out;
+ out:
+	return error;
+}
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/dir.h linux/fs/ntfs/dir.h
--- v2.2.0-pre3/linux/fs/ntfs/dir.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/dir.h	Sat Jan  2 10:24:46 1999
@@ -18,7 +18,7 @@
 	enum ntfs_iterate_e type;
 	ntfs_inode *dir;
 	union{
-		long long pos;
+		ntfs_u64 pos;
 		int flags;
 	}u;
 	char *result;      /* pointer to entry if found */
@@ -37,3 +37,5 @@
 int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
 int ntfs_check_index_record(ntfs_inode *ino, char *record);
 int ntfs_getdir_byposition(ntfs_iterate_s *walk);
+int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino);
+int ntfs_split_indexroot(ntfs_inode *ino);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.2.0-pre3/linux/fs/ntfs/fs.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/ntfs/fs.c	Sat Jan  2 10:24:46 1999
@@ -14,7 +14,7 @@
 #include <linux/config.h>
 #endif
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "util.h"
 #include "inode.h"
@@ -29,6 +29,9 @@
 #include <linux/locks.h>
 #include <linux/init.h>
 
+/* Forward declarations */
+static struct inode_operations ntfs_dir_inode_operations;
+
 #define ITEM_SIZE 2040
 
 /* io functions to user space */
@@ -39,10 +42,19 @@
 }
 
 #ifdef CONFIG_NTFS_RW
-static void ntfs_getuser(void *dest,ntfs_io *src,ntfs_size_t len)
+struct ntfs_getuser_update_vm_s{
+	const char *user;
+	struct inode *ino;
+	loff_t off;
+};
+
+static void ntfs_getuser_update_vm (void *dest, ntfs_io *src, ntfs_size_t len)
 {
-	copy_from_user(dest,src->param,len);
-	src->param+=len;
+	struct ntfs_getuser_update_vm_s *p = src->param;
+	copy_from_user (dest, p->user, len);
+	update_vm_cache (p->ino, p->off, dest, len);
+	p->user += len;
+	p->off += len;
 }
 #endif
 
@@ -77,31 +89,42 @@
 static ssize_t
 ntfs_write(struct file *filp,const char* buf,size_t count,loff_t *pos)
 {
-	struct super_block* sb;
 	int ret;
 	ntfs_io io;
-	ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+	struct inode *inode = filp->f_dentry->d_inode;
+	ntfs_inode *ino = NTFS_LINO2NINO(inode);
+	struct ntfs_getuser_update_vm_s param;
 
-	if(!ino)return -EINVAL;
-	ntfs_debug(DEBUG_OTHER, "ntfs_write %x,%x,%x ->",
-	       (unsigned)ino->i_number,(unsigned)*pos,(unsigned)count);
-	sb = filp->f_dentry->d_inode->i_sb;
+	if (!ino)
+		return -EINVAL;
+	ntfs_debug (DEBUG_LINUX, "ntfs_write %x,%x,%x ->\n",
+	       (unsigned)ino->i_number, (unsigned)*pos, (unsigned)count);
 	/* Allows to lock fs ro at any time */
-	if(sb->s_flags & MS_RDONLY)
+	if (inode->i_sb->s_flags & MS_RDONLY)
 		return -ENOSPC;
-	if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+	if (!ntfs_find_attr(ino,ino->vol->at_data,NULL))
 		return -EINVAL;
 
-	io.fn_put=0;
-	io.fn_get=ntfs_getuser;
-	io.param=(void*)buf; /* to get rid of the const */
-	io.size=count;
-	ret = ntfs_write_attr(ino,ino->vol->at_data,NULL,*pos,&io);
-	ntfs_debug(DEBUG_OTHER, "%x\n",ret);
-	if(ret<0)return -EINVAL;
+	/* Evaluating O_APPEND is the file system's job... */
+	if (filp->f_flags & O_APPEND)
+		*pos = inode->i_size;
+	param.user = buf;
+	param.ino = inode;
+	param.off = *pos;
+	io.fn_put = 0;
+	io.fn_get = ntfs_getuser_update_vm;
+	io.param = &param;
+	io.size = count;
+	ret = ntfs_write_attr (ino, ino->vol->at_data, NULL, *pos, &io);
+	ntfs_debug (DEBUG_LINUX, "write -> %x\n", ret);
+	if(ret<0)
+		return -EINVAL;
 
-	*pos+=ret;
-	return ret;
+	*pos += io.size;
+	if (*pos > inode->i_size)
+		inode->i_size = *pos;
+	mark_inode_dirty (filp->f_dentry->d_inode);
+	return io.size;
 }
 #endif
 
@@ -119,10 +142,13 @@
 {
 	struct ntfs_filldir* nf=param;
 	int flags=NTFS_GETU8(entry+0x51);
-	int show_hidden=0,to_lower=0;
+	int show_hidden=0;
 	int length=NTFS_GETU8(entry+0x50);
 	int inum=NTFS_GETU32(entry);
-	int i,error;
+	int error;
+#ifdef NTFS_NGT_NT_DOES_LOWER
+	int i,to_lower=0;
+#endif
 	switch(nf->type){
 	case ngt_dos:
 		/* Don't display long names */
@@ -133,7 +159,9 @@
 		/* Don't display short-only names */
 		switch(flags&3){
 		case 2: return 0;
+#ifdef NTFS_NGT_NT_DOES_LOWER
 		case 3: to_lower=1;
+#endif
 		}
 		break;
 	case ngt_posix:
@@ -156,6 +184,7 @@
 	/* Do not return ".", as this is faked */
 	if(length==1 && *nf->name=='.')
 		return 0;
+#ifdef NTFS_NGT_NT_DOES_LOWER
 	if(to_lower)
 		for(i=0;i<nf->namelen;i++)
 			/* This supports ASCII only. Since only DOS-only
@@ -163,6 +192,7 @@
 			   to ASCII, this should be correct */
 			if(nf->name[i]>='A' && nf->name[i]<='Z')
 				nf->name[i]+='a'-'A';
+#endif
 	nf->name[nf->namelen]=0;
 	ntfs_debug(DEBUG_OTHER, "readdir got %s,len %d\n",nf->name,nf->namelen);
 	/* filldir expects an off_t rather than an loff_t.
@@ -349,7 +379,7 @@
 	char *item=0;
 	ntfs_iterate_s walk;
 	int error;
-	ntfs_debug(DEBUG_OTHER, "Looking up %s in %x\n",d->d_name.name,
+	ntfs_debug(DEBUG_NAME1, "Looking up %s in %x\n",d->d_name.name,
 		   (unsigned)dir->i_ino);
 	/* convert to wide string */
 	error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name,
@@ -369,10 +399,11 @@
 	d_add(d,res);
 	ntfs_free(item);
 	ntfs_free(walk.name);
-	return res?0:-ENOENT;
+	/* Always return success, the dcache will handle negative entries. */
+	return 0;
 }
 
-struct file_operations ntfs_file_operations_nommap = {
+static struct file_operations ntfs_file_operations_nommap = {
 	NULL, /* lseek */
 	ntfs_read,
 #ifdef CONFIG_NTFS_RW
@@ -394,7 +425,7 @@
 	NULL, /* lock */
 };
 
-struct inode_operations ntfs_inode_operations_nobmap = {
+static struct inode_operations ntfs_inode_operations_nobmap = {
 	&ntfs_file_operations_nommap,
 	NULL, /* create */
 	NULL, /* lookup */
@@ -445,7 +476,7 @@
 	}
 	r->u.generic_ip=ino;
 #endif
-	error=ntfs_alloc_inode(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name,
+	error=ntfs_alloc_file(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name,
 			       d->d_name.len);
 	if(error)goto fail;
 	error=ntfs_update_inode(ino);
@@ -483,6 +514,65 @@
 	if(r)iput(r);
 	return -error;
 }
+
+static int
+_linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode)
+{
+	int error;
+	struct inode *r = 0;
+	ntfs_volume *vol;
+	ntfs_inode *ino;
+	ntfs_attribute *si;
+
+	ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n",d->d_name.name, dir->i_ino);
+	error = ENAMETOOLONG;
+	if (d->d_name.len > /* FIXME */255)
+		goto out;
+
+	error = EIO;
+	r = get_empty_inode();
+	if (!r)
+		goto out;
+	
+	vol = NTFS_INO2VOL(dir);
+#ifdef NTFS_IN_LINUX_KERNEL
+	ino = NTFS_LINO2NINO(r);
+#else
+	ino = ntfs_malloc(sizeof(ntfs_inode));
+	error = ENOMEM;
+	if(!ino)
+		goto out;
+	r->u.generic_ip = ino;
+#endif
+	error = ntfs_mkdir(NTFS_LINO2NINO(dir), 
+			   d->d_name.name, d->d_name.len, ino);
+	if(error)
+		goto out;
+	r->i_uid = vol->uid;
+	r->i_gid = vol->gid;
+	r->i_nlink = 1;
+	r->i_sb = dir->i_sb;
+	si = ntfs_find_attr(ino,vol->at_standard_information,NULL);
+	if(si){
+		char *attr = si->d.data;
+		r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18));
+		r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+		r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr+8));
+	}
+	/* It's a directory */
+	r->i_op = &ntfs_dir_inode_operations;
+	r->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+#ifdef CONFIG_NTFS_RW
+	r->i_mode|=S_IWUGO;
+#endif
+	r->i_mode &= ~vol->umask;	
+	
+	d_instantiate(d, r);
+	error = 0;
+ out:
+ 	ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", -error);
+	return -error;
+}
 #endif
 
 static int 
@@ -491,10 +581,12 @@
 	int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block);
 	ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n",
 	       ino->i_ino,block,ret);
+	ntfs_error("bmap of %lx,block %x is %x\n", ino->i_ino,block,ret);
+	ntfs_error("super %x\n", ino->i_sb->s_blocksize); 
 	return (ret==-1) ? 0:ret;
 }
 
-struct file_operations ntfs_file_operations = {
+static struct file_operations ntfs_file_operations = {
 	NULL, /* lseek */
 	ntfs_read,
 #ifdef CONFIG_NTFS_RW
@@ -516,7 +608,7 @@
 	NULL, /* lock */
 };
 
-struct inode_operations ntfs_inode_operations = {
+static struct inode_operations ntfs_inode_operations = {
 	&ntfs_file_operations,
 	NULL, /* create */
 	NULL, /* lookup */
@@ -539,7 +631,7 @@
 	NULL, /* revalidate */
 };
 
-struct file_operations ntfs_dir_operations = {
+static struct file_operations ntfs_dir_operations = {
 	NULL, /* lseek */
 	NULL, /* read */
 	NULL, /* write */
@@ -557,7 +649,7 @@
 	NULL, /* lock */
 };
 
-struct inode_operations ntfs_dir_inode_operations = {
+static struct inode_operations ntfs_dir_inode_operations = {
 	&ntfs_dir_operations,
 #ifdef CONFIG_NTFS_RW
 	ntfs_create, /* create */
@@ -568,7 +660,11 @@
 	NULL, /* link */
 	NULL, /* unlink */
 	NULL, /* symlink */
-	NULL, /* mkdir */
+#ifdef CONFIG_NTFS_RW
+	_linux_ntfs_mkdir, /* mkdir */
+#else
+	NULL,
+#endif
 	NULL, /* rmdir */
 	NULL, /* mknod */
 	NULL, /* rename */
@@ -669,9 +765,14 @@
 	inode->i_mode &= ~vol->umask;
 }
 
-static void ntfs_put_inode(struct inode *ino)
+#ifdef CONFIG_NTFS_RW
+static void 
+ntfs_write_inode (struct inode *ino)
 {
+	ntfs_debug (DEBUG_LINUX, "ntfs:write inode %x\n", ino->i_ino);
+	ntfs_update_inode (NTFS_LINO2NINO (ino));
 }
+#endif
 
 static void _ntfs_clear_inode(struct inode *ino)
 {
@@ -747,10 +848,14 @@
 }
 
 /* Define the super block operation that are implemented */
-struct super_operations ntfs_super_operations = {
+static struct super_operations ntfs_super_operations = {
 	ntfs_read_inode,
-	NULL, /* write_inode */
-	ntfs_put_inode,
+#ifdef CONFIG_NTFS_RW
+	ntfs_write_inode,
+#else
+	NULL,
+#endif
+	NULL, /* put_inode */
 	NULL, /* delete_inode */
 	NULL, /* notify_change */
 	ntfs_put_super,
@@ -898,7 +1003,7 @@
  * Define SECOND if you cannot unload ntfs, and want to avoid rebooting
  * for just one more test
  */
-struct file_system_type ntfs_fs_type = {
+static struct file_system_type ntfs_fs_type = {
 /* Filesystem name, as used after mount -t */
 #ifndef SECOND
 	"ntfs",
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c
--- v2.2.0-pre3/linux/fs/ntfs/inode.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ntfs/inode.c	Sat Jan  2 10:24:46 1999
@@ -1,12 +1,13 @@
 /*
  *  inode.c
  *
- *  Copyright (C) 1995-1997 Martin von Löwis
+ *  Copyright (C) 1995-1999 Martin von Löwis
  *  Copyright (C) 1996 Albert D. Cahalan
  *  Copyright (C) 1996-1997 Régis Duchesne
+ *  Copyright (C) 1998 Joseph Malicki
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "inode.h"
 
@@ -187,7 +188,7 @@
 /* Read and insert all the attributes of an 'attribute list' attribute
    Return the number of remaining bytes in *plen
 */
-static int parse_attributes(ntfs_inode *ino, void *alist, int *plen)
+static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
 {
 	char *mft;
 	int mftno,l,error;
@@ -223,14 +224,14 @@
 	int offset,len,delta;
 	char *buf;
 	ntfs_volume *vol=ino->vol;
-	ntfs_debug(DEBUG_OTHER, "load_attributes %x 1\n",ino->i_number);
+	ntfs_debug(DEBUG_FILE2, "load_attributes %x 1\n",ino->i_number);
 	ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number);
-	ntfs_debug(DEBUG_OTHER, "load_attributes %x 2\n",ino->i_number);
+	ntfs_debug(DEBUG_FILE2, "load_attributes %x 2\n",ino->i_number);
 	alist=ntfs_find_attr(ino,vol->at_attribute_list,0);
-	ntfs_debug(DEBUG_OTHER, "load_attributes %x 3\n",ino->i_number);
+	ntfs_debug(DEBUG_FILE2, "load_attributes %x 3\n",ino->i_number);
 	if(!alist)
 		return;
-	ntfs_debug(DEBUG_OTHER, "load_attributes %x 4\n",ino->i_number);
+	ntfs_debug(DEBUG_FILE2, "load_attributes %x 4\n",ino->i_number);
 	datasize=alist->size;
 	if(alist->resident)
 	{
@@ -255,7 +256,7 @@
 			/* move remaining bytes to buffer start */
 			ntfs_memmove(buf,buf+len-delta,delta);
 	}
-	ntfs_debug(DEBUG_OTHER, "load_attributes %x 5\n",ino->i_number);
+	ntfs_debug(DEBUG_FILE2, "load_attributes %x 5\n",ino->i_number);
 	ntfs_free(buf);
 }
 	
@@ -264,7 +265,7 @@
 	char *buf;
 	int error;
 
-	ntfs_debug(DEBUG_OTHER, "Initializing inode %x\n",inum);
+	ntfs_debug(DEBUG_FILE1, "Initializing inode %x\n",inum);
 	if(!vol)
 		ntfs_error("NO VOLUME!\n");
 	ino->i_number=inum;
@@ -275,14 +276,14 @@
 		ntfs_debug(DEBUG_OTHER, "init inode: %x failed\n",inum);
 		return error;
 	}
-	ntfs_debug(DEBUG_OTHER, "Init: got mft %x\n",inum);
+	ntfs_debug(DEBUG_FILE2, "Init: got mft %x\n",inum);
 	ino->sequence_number=NTFS_GETU16(buf+0x10);
 	ino->attr_count=0;
 	ino->record_count=0;
 	ino->records=0;
 	ino->attrs=0;
 	ntfs_load_attributes(ino);
-	ntfs_debug(DEBUG_OTHER, "Init: done %x\n",inum);
+	ntfs_debug(DEBUG_FILE2, "Init: done %x\n",inum);
 	return 0;
 }
 
@@ -391,40 +392,46 @@
  * This function decodes a run. Length is an output parameter, data and cluster
  * are in/out parameters.
  */
-int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
+int ntfs_decompress_run(unsigned char **data, int *length, ntfs_cluster_t *cluster,
 	int *ctype)
 {
 	unsigned char type=*(*data)++;
 	*ctype=0;
 	switch(type & 0xF)
 	{
-	case 1: *length=NTFS_GETU8(*data);(*data)++;break;
-	case 2: *length=NTFS_GETU16(*data);
-		*data+=2;
-		break;
-	case 3: *length = NTFS_GETU24(*data);
-		*data+=3;
-		break;
-		/* TODO: case 4-8 */
+	case 1: *length=NTFS_GETU8(*data);break;
+	case 2: *length=NTFS_GETU16(*data);break;
+	case 3: *length=NTFS_GETU24(*data);break;
+        case 4: *length=NTFS_GETU32(*data);break;
+        	/* Note: cases 5-8 are probably pointless to code,
+        	   since how many runs > 4GB of length are there?
+        	   at the most, cases 5 and 6 are probably necessary,
+        	   and would also require making length 64-bit
+        	   throughout */
 	default:
 		ntfs_error("Can't decode run type field %x\n",type);
 		return -1;
 	}
+	*data+=(type & 0xF);
+
 	switch(type & 0xF0)
 	{
-	case 0:    *ctype=2;break;
-	case 0x10: *cluster+=NTFS_GETS8(*data);(*data)++;break;
-	case 0x20: *cluster+=NTFS_GETS16(*data);
-		*data+=2;
-		break;
-	case 0x30: 	*cluster+=NTFS_GETS24(*data);
-		*data+=3;
-		break;
-		/* TODO: case 0x40-0x80 */
+	case 0:	   *ctype=2; break;
+	case 0x10: *cluster += NTFS_GETS8(*data);break;
+	case 0x20: *cluster += NTFS_GETS16(*data);break;
+	case 0x30: *cluster += NTFS_GETS24(*data);break;
+	case 0x40: *cluster += NTFS_GETS32(*data);break;
+#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit */
+	case 0x50: *cluster += NTFS_GETS40(*data);break;
+	case 0x60: *cluster += NTFS_GETS48(*data);break;
+	case 0x70: *cluster += NTFS_GETS56(*data);break;
+	case 0x80: *cluster += NTFS_GETS64(*data);break;	
+#endif
 	default:
 		ntfs_error("Can't decode run type field %x\n",type);
 		return -1;
 	}
+	*data+=(type >> 4);
 	return 0;
 }
 
@@ -435,7 +442,8 @@
 	ntfs_io *dest)
 {
 	int datasize,rnum;
-	int cluster,s_cluster,vcn,len,l,chunk,copied;
+	ntfs_cluster_t cluster,s_cluster,vcn,len;
+	int l,chunk,copied;
 	int s_vcn;
 	int clustersize;
 	int error;
@@ -443,6 +451,8 @@
 	clustersize=ino->vol->clustersize;
 	datasize=attr->size;
 	l=dest->size;
+	if(l==0)
+		return 0;
 	if(dest->do_read)
 	{
 		if(offset>=datasize){
@@ -457,20 +467,30 @@
 			if(error)
 				return error;
 		}
+		if (offset+l > attr->initialized)
+			attr->initialized = offset+l;
 	}
 	if(attr->resident)
 	{
 		if(dest->do_read)
-			dest->fn_put(dest,attr->d.data+offset,l);
+			dest->fn_put(dest,(ntfs_u8*)attr->d.data+offset,l);
 		else
-		{
-			dest->fn_get(attr->d.data+offset,dest,l);
-			ntfs_update_inode(ino);
-		}
+			dest->fn_get((ntfs_u8*)attr->d.data+offset,dest,l);
 		dest->size=l;
 		return 0;
 	}
-	if(attr->compressed) {
+	/* read uninitialized data */
+	if(offset>=attr->initialized && dest->do_read)
+		return ntfs_read_zero(dest,l);
+	if(offset+l>attr->initialized && dest->do_read)
+	{
+		dest->size = chunk = offset+l - attr->initialized;
+		error = ntfs_readwrite_attr(ino,attr,offset,dest);
+		if(error)
+			return error;		
+		return ntfs_read_zero(dest,l-chunk);
+	}
+	if(attr->compressed){
 		if(dest->do_read)
 			return ntfs_read_compressed(ino,attr,offset,dest);
 		else
@@ -544,17 +564,26 @@
 int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn)
 {
 	int rnum;
-	ntfs_attribute *data=ntfs_find_attr(ino,ino->vol->at_data,0);
+	ntfs_attribute *data;
+	ntfs_error("bmap %x\n",vcn);
+	data=ntfs_find_attr(ino,ino->vol->at_data,0);
 	/* It's hard to give an error code */
 	if(!data)return -1;
 	if(data->resident)return -1;
 	if(data->compressed)return -1;
-	if(data->size<vcn*ino->vol->clustersize)return -1;
+	if(data->size <= vcn*ino->vol->clustersize)return -1;
+
+
+	/* For Linux, block number 0 represents a hole.
+	   Hopefully, nobody will attempt to bmap $Boot. */
+	if(data->initialized <= vcn*ino->vol->clustersize)
+		return 0;
 
 	for(rnum=0;rnum<data->d.r.len && 
 		    vcn>data->d.r.runlist[rnum].len;rnum++)
 		vcn-=data->d.r.runlist[rnum].len;
 	
+	ntfs_error("result %x\n",data->d.r.runlist[rnum].cluster+vcn);
 	return data->d.r.runlist[rnum].cluster+vcn;
 }
 
@@ -599,7 +628,8 @@
 int 
 layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size)
 {
-	int i,cluster,rclus,len,offset,coffs;
+	int i,len,offset,coffs;
+	ntfs_cluster_t cluster,rclus;
 	ntfs_runlist *rl=attr->d.r.runlist;
 	cluster=0;
 	offset=*offs;
@@ -610,23 +640,21 @@
 		if(offset+8>size)
 			return E2BIG; /* it might still fit, but this simplifies testing */
 		if(len<0x100){
-			*(rec+offset)|=1;
 			NTFS_PUTU8(rec+offset+1,len);
-			coffs=2;
+			coffs=1;
 		}else if(len<0x10000){
-			*(rec+offset)|=2;
 			NTFS_PUTU16(rec+offset+1,len);
-			coffs=3;
+			coffs=2;
 		}else if(len<0x1000000){
-			*(rec+offset)|=3;
 			NTFS_PUTU24(rec+offset+1,len);
-			coffs=4;
+			coffs=3;
 		}else{
-			*(rec+offset)|=4;
 			NTFS_PUTU32(rec+offset+1,len);
-			coffs=5;
+			coffs=4;
 		}
     
+		*(rec+offset)|=coffs++;
+
 		if(rl[i].cluster==0) /*compressed run*/
 			/*nothing*/;
 		else if(rclus>-0x80 && rclus<0x7F){
@@ -641,15 +669,42 @@
 			*(rec+offset)|=0x30;
 			NTFS_PUTS24(rec+offset+coffs,rclus);
 			coffs+=3;
-		}else{
+		}else
+#if 0 /* In case ntfs_cluster_t ever becomes 64bit */
+	       	if (rclus>-0x80000000LL && rclus<0x7FFFFFFF)
+#endif
+		{
 			*(rec+offset)|=0x40;
 			NTFS_PUTS32(rec+offset+coffs,rclus);
 			coffs+=4;
 		}
+#if 0 /* For 64-bit ntfs_cluster_t */
+		else if (rclus>-0x8000000000 && rclus<0x7FFFFFFFFF){
+			*(rec+offset)|=0x50;
+			NTFS_PUTS40(rec+offset+coffs,rclus);
+			coffs+=5;
+		}else if (rclus>-0x800000000000 && rclus<0x7FFFFFFFFFFF){
+			*(rec+offset)|=0x60;
+			NTFS_PUTS48(rec+offset+coffs,rclus);
+			coffs+=6;
+		}else if (rclus>-0x80000000000000 && rclus<0x7FFFFFFFFFFFFF){
+			*(rec+offset)|=0x70;
+			NTFS_PUTS56(rec+offset+coffs,rclus);
+			coffs+=7;
+		}else{
+			*(rec+offset)|=0x80;
+			NTFS_PUTS64(rec+offset+coffs,rclus);
+			coffs+=8;
+		}
+#endif
 		offset+=coffs;
 		if(rl[i].cluster)
 			cluster=rl[i].cluster;
 	}
+	if(offset>=size)
+		return E2BIG;
+	/* terminating null */
+	*(rec+offset++)=0;
 	*offs=offset;
 	return 0;
 }
@@ -657,14 +712,14 @@
 static void 
 count_runs(ntfs_attribute *attr,char *buf)
 {
-	int first,count,last,i;
+	ntfs_u32 first,count,last,i;
 	first=0;
 	for(i=0,count=0;i<attr->d.r.len;i++)
 		count+=attr->d.r.runlist[i].len;
 	last=first+count-1;
 
-	NTFS_PUTU32(buf+0x10,first);
-	NTFS_PUTU32(buf+0x18,last);
+	NTFS_PUTU64(buf+0x10,first);
+	NTFS_PUTU64(buf+0x18,last);
 } 
 
 static int
@@ -814,8 +869,17 @@
 	store.records=0;
 	error=layout_inode(ino,&store);
 	if(error==E2BIG){
+		error = ntfs_split_indexroot(ino);
+		if(!error)
+			error = layout_inode(ino,&store);
+	}
+	if(error == E2BIG){
+		error = ntfs_attr_allnonresident(ino);
+		if(!error)
+			error = layout_inode(ino,&store);
+	}
+	if(error == E2BIG){
 		/* should try:
-		   make attributes non-resident
 		   introduce extension records
 		   */
 		ntfs_error("cannot handle saving inode %x\n",ino->i_number);
@@ -844,7 +908,8 @@
 		}
 	}
 	return 0;
-}
+}	
+
 
 void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
 {
@@ -1030,7 +1095,7 @@
 
 static int 
 add_filename (ntfs_inode* ino, ntfs_inode* dir, 
-			 const unsigned char *filename, int length)
+	      const unsigned char *filename, int length, ntfs_u32 flags)
 {
 	unsigned char   *position;
 	unsigned int    size;
@@ -1057,7 +1122,7 @@
 	NTFS_PUTU64(position + 0x20, now);		/* Last access */
 
 	/* Don't know */
-	NTFS_PUTU8(position+0x38, 0x0);  /*should match standard attributes*/
+	NTFS_PUTU32(position+0x38, flags);
 
 	NTFS_PUTU8(position + 0x40, length);	      /* Filename length */
 	NTFS_PUTU8(position + 0x41, 0x0);	      /* only long name */
@@ -1120,10 +1185,9 @@
 	return error;
 }
 
-
 /* We _could_ use 'dir' to help optimise inode allocation */
-int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
-		int namelen)
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, 
+		      const char *filename, int namelen, ntfs_u32 flags)
 {
 	ntfs_io io;
 	int error;
@@ -1185,15 +1249,23 @@
 	error=add_standard_information(result);
 	if(error)
 		return error;
-	error=add_filename(result,dir,filename,namelen);
+	error=add_filename(result,dir,filename,namelen,flags);
 	if(error)
 		return error;
 	error=add_security(result,dir);
 	/*FIXME: check error */
-	error=add_data(result,0,0);
+	return 0;
+}
+
+int
+ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
+		int namelen)
+{
+	int error = ntfs_alloc_inode(dir,result,filename,namelen,0);
 	if(error)
 		return error;
-	return 0;
+	error = add_data(result,0,0);
+	return error;
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/inode.h linux/fs/ntfs/inode.h
--- v2.2.0-pre3/linux/fs/ntfs/inode.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/inode.h	Sat Jan  2 10:24:46 1999
@@ -3,23 +3,26 @@
  *  Header file for inode.c
  *
  *  Copyright (C) 1997 Régis Duchesne
+ *  Copyright (C) 1998 Martin von Löwis
  */
 
 ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
 int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
-  ntfs_io *buf);
+		   ntfs_io *buf);
 int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
-  ntfs_io *buf);
+		    ntfs_io *buf);
 int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum);
 void ntfs_clear_inode(ntfs_inode *ino);
 int ntfs_check_mft_record(ntfs_volume *vol,char *record);
-int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
-  int namelen);
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, 
+		      const char *filename, int namelen,ntfs_u32);
+int ntfs_alloc_file (ntfs_inode *dir, ntfs_inode *result, 
+		     char *filename, int namelen);
 int ntfs_update_inode(ntfs_inode *ino);
 int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
 int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
-  ntfs_io *dest);
+			ntfs_io *dest);
 int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
-int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
-  int *ctype);
+int ntfs_decompress_run(unsigned char **data, int *length, ntfs_cluster_t *cluster,
+			int *ctype);
 void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/macros.h linux/fs/ntfs/macros.h
--- v2.2.0-pre3/linux/fs/ntfs/macros.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/macros.h	Sat Jan  2 10:24:46 1999
@@ -26,8 +26,10 @@
 #define NTFS_V2INO(ino)		((ntfs_inode*)((ino)->v_data))
 
 /* Classical min and max macros still missing in standard headers... */
+#ifndef min
 #define min(a,b)	((a) <= (b) ? (a) : (b))
 #define max(a,b)	((a) >= (b) ? (a) : (b))
+#endif
 
 #define IS_MAGIC(a,b)		(*(int*)(a)==*(int*)(b))
 #define IS_MFT_RECORD(a)	IS_MAGIC((a),"FILE")
@@ -36,6 +38,13 @@
 
 /* 'NTFS' in little endian */
 #define NTFS_SUPER_MAGIC	0x5346544E
+
+#define NTFS_AFLAG_RO           1
+#define NTFS_AFLAG_HIDDEN       2
+#define NTFS_AFLAG_SYSTEM       4
+#define NTFS_AFLAG_ARCHIVE      20
+#define NTFS_AFLAG_COMPRESSED   0x800
+#define NTFS_AFLAG_DIR          0x10000000
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/ntfsendian.h linux/fs/ntfs/ntfsendian.h
--- v2.2.0-pre3/linux/fs/ntfs/ntfsendian.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/ntfsendian.h	Sat Jan  2 10:24:46 1999
@@ -0,0 +1,86 @@
+/*
+ *  ntfsendian.h
+ *
+ *  Copyright (C) 1998 Martin von Löwis
+ *  Copyright (C) 1998 Joseph Malicki
+ */
+
+#ifdef __linux__
+/* This defines __cpu_to_le16 on Linux 2.1 and higher. */
+#include <asm/byteorder.h>
+#endif
+
+#ifdef __cpu_to_le16
+#define CPU_TO_LE16(a) __cpu_to_le16(a)
+#define CPU_TO_LE32(a) __cpu_to_le32(a)
+#define CPU_TO_LE64(a) __cpu_to_le64(a)
+
+#define LE16_TO_CPU(a) __cpu_to_le16(a)
+#define LE32_TO_CPU(a) __cpu_to_le32(a)
+#define LE64_TO_CPU(a) __cpu_to_le64(a)
+
+#else
+
+#ifdef __LITTLE_ENDIAN
+
+#define CPU_TO_LE16(a) (a)
+#define CPU_TO_LE32(a) (a)
+#define CPU_TO_LE64(a) (a)
+#define LE16_TO_CPU(a) (a)
+#define LE32_TO_CPU(a) (a)
+#define LE64_TO_CPU(a) (a)
+
+#else
+
+/* Routines for big-endian machines */
+#ifdef __BIG_ENDIAN
+
+/* We hope its big-endian, not PDP-endian :) */
+#define CPU_TO_LE16(a) ((((a)&0xF) << 8)|((a) >> 8))
+#define CPU_TO_LE32(a) ((((a) & 0xF) << 24) | (((a) & 0xF0) << 8) | \
+			(((a) & 0xF00) >> 8) | ((a) >> 24))
+#define CPU_TO_LE64(a) ((CPU_TO_LE32(a)<<32)|CPU_TO_LE32((a)>>32)
+
+#define LE16_TO_CPU(a) CPU_TO_LE16(a)
+#define LE32_TO_CPU(a) CPU_TO_LE32(a)
+#define LE64_TO_CPU(a) CPU_TO_LE64(a)
+
+#else
+
+#error Please define Endianness - __BIG_ENDIAN or __LITTLE_ENDIAN or add OS-specific macros
+
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __cpu_to_le16 */
+
+#define NTFS_GETU8(p)      (*(ntfs_u8*)(p))
+#define NTFS_GETU16(p)     ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p)))
+#define NTFS_GETU24(p)     (NTFS_GETU32(p) & 0xFFFFFF)
+#define NTFS_GETU32(p)     ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p)))
+#define NTFS_GETU40(p)     ((ntfs_u64)NTFS_GETU32(p)|(((ntfs_u64)NTFS_GETU8(((char*)(p))+4))<<32))
+#define NTFS_GETU48(p)     ((ntfs_u64)NTFS_GETU32(p)|(((ntfs_u64)NTFS_GETU16(((char*)(p))+4))<<32))
+#define NTFS_GETU56(p)     ((ntfs_u64)NTFS_GETU32(p)|(((ntfs_u64)NTFS_GETU24(((char*)(p))+4))<<32))
+#define NTFS_GETU64(p)     ((ntfs_u64)LE64_TO_CPU(*(ntfs_u64*)(p)))
+ 
+ /* Macros writing unsigned integers */
+#define NTFS_PUTU8(p,v)      ((*(ntfs_u8*)(p))=(v))
+#define NTFS_PUTU16(p,v)     ((*(ntfs_u16*)(p))=CPU_TO_LE16(v))
+#define NTFS_PUTU24(p,v)     NTFS_PUTU16(p,(v) & 0xFFFF);\
+                             NTFS_PUTU8(((char*)(p))+2,(v)>>16)
+#define NTFS_PUTU32(p,v)     ((*(ntfs_u32*)(p))=CPU_TO_LE32(v))
+#define NTFS_PUTU64(p,v)     ((*(ntfs_u64*)(p))=CPU_TO_LE64(v))
+ 
+ /* Macros reading signed integers */
+#define NTFS_GETS8(p)        ((*(ntfs_u8*)(p)))
+#define NTFS_GETS16(p)       ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
+#define NTFS_GETS24(p)       (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) - 0x1000000))
+#define NTFS_GETS32(p)       ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
+#define NTFS_GETS40(p)       (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS8(((char*)(p))+4)) << 32))
+#define NTFS_GETS48(p)       (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS16(((char*)(p))+4)) << 32))
+#define NTFS_GETS56(p)       (((ntfs_s64)NTFS_GETS32(p)) | (((ntfs_s64)NTFS_GETS24(((char*)(p))+4)) << 32))
+#define NTFS_GETS64(p)	     ((ntfs_s64)NTFS_GETU64(p))
+ 
+#define NTFS_PUTS8(p,v)      NTFS_PUTU8(p,v)
+#define NTFS_PUTS16(p,v)     NTFS_PUTU16(p,v)
+#define NTFS_PUTS24(p,v)     NTFS_PUTU24(p,v)
+#define NTFS_PUTS32(p,v)     NTFS_PUTU32(p,v)
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/ntfstypes.h linux/fs/ntfs/ntfstypes.h
--- v2.2.0-pre3/linux/fs/ntfs/ntfstypes.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/ntfstypes.h	Sat Jan  2 10:24:46 1999
@@ -0,0 +1,90 @@
+/*
+ *  types.h
+ *  This file defines four things:
+ *   - generic platform independent fixed-size types (e.g. ntfs_u32)
+ *   - specific fixed-size types (e.g. ntfs_offset_t)
+ *   - macros that read and write those types from and to byte arrays
+ *   - types derived from OS specific ones
+ *
+ *  Copyright (C) 1996,1998 Martin von Löwis
+ */
+
+#ifdef NTFS_IN_LINUX_KERNEL
+/* get installed types if we compile the kernel*/
+#include <linux/fs.h>
+#endif
+
+/* We don't need to define __LITTLE_ENDIAN, as we use
+   <asm/byteorder>. */
+
+#include "ntfsendian.h"
+#include <asm/types.h>
+
+/* integral types */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef u8  ntfs_u8;
+typedef u16 ntfs_u16;
+typedef u32 ntfs_u32;
+typedef u64 ntfs_u64;
+typedef s8  ntfs_s8;
+typedef s16 ntfs_s16;
+typedef s32 ntfs_s32;
+typedef s64 ntfs_s64;
+#endif
+
+/* unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef u16 ntfs_wchar_t;
+#endif
+/* file offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef u64 ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef u64 ntfs_time64_t;
+#endif
+/* This is really unsigned long long. So we support only volumes up to 2 TB */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef u32 ntfs_cluster_t;
+#endif
+
+/* architecture independent macros */
+
+/* PUTU32 would not clear all bytes */
+#define NTFS_PUTINUM(p,i)    NTFS_PUTU64(p,i->i_number);\
+                             NTFS_PUTU16(((char*)p)+6,i->sequence_number)
+
+/* system dependent types */
+#include <asm/posix_types.h>
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef __kernel_uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef __kernel_gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/super.c linux/fs/ntfs/super.c
--- v2.2.0-pre3/linux/fs/ntfs/super.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ntfs/super.c	Sat Jan  2 10:24:46 1999
@@ -5,7 +5,7 @@
  *  Copyright (C) 1996-1997 Régis Duchesne
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "super.h"
 
@@ -32,7 +32,7 @@
 int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size)
 {
 	int start, count, offset;
-	unsigned short fixup;
+	ntfs_u16 fixup;
 
 	if(!IS_MAGIC(record,magic))
 		return 0;
@@ -116,7 +116,7 @@
 	upcase->vol->upcase_length = UPCASE_LENGTH;
 	io.fn_put=ntfs_put;
 	io.fn_get=0;
-	io.param=upcase->vol->upcase;
+	io.param=(char*)upcase->vol->upcase;
 	io.size=2*UPCASE_LENGTH;
 	ntfs_read_attr(upcase,upcase->vol->at_data,0,0,&io);
 }
@@ -152,8 +152,8 @@
 	}else if(ntfs_ua_strncmp(name,"$BITMAP",64)==0){
 		vol->at_bitmap=type;
 		check_type=0xB0;
-	}else if(ntfs_ua_strncmp(name,"$SYMBOLIC_LINK",64) ||
-		 ntfs_ua_strncmp(name,"$REPARSE_POINT",64)){
+	}else if(ntfs_ua_strncmp(name,"$SYMBOLIC_LINK",64)==0 ||
+		 ntfs_ua_strncmp(name,"$REPARSE_POINT",64)==0){
 		vol->at_symlink=type;
 	}
 	if(check_type && check_type!=type){
@@ -170,7 +170,7 @@
 	ntfs_io io;
 	int offset,error,i;
 	ntfs_attribute *data;
-	buf=ntfs_malloc(4096);
+	buf=ntfs_malloc(4050); /* 90*45 */
 	if(!buf)return ENOMEM;
 	io.fn_put=ntfs_put;
 	io.fn_get=ntfs_get;
@@ -183,9 +183,9 @@
 	}
 	do{
 		io.param=buf;
-		io.size=4096;
+		io.size=4050;
 		error=ntfs_readwrite_attr(attrdef,data,offset,&io);
-		for(i=0;!error && i<io.size;i+=0xA0)
+		for(i=0;!error && i<io.size-0xA0;i+=0xA0)
 			error=process_attrdef(attrdef,buf+i);
 		offset+=4096;
 	}while(!error && io.size);
@@ -200,25 +200,30 @@
 
 	vol->mft_ino=(ntfs_inode*)ntfs_calloc(3*sizeof(ntfs_inode));
 	error=ENOMEM;
+	ntfs_debug(DEBUG_BSD,"Going to load MFT\n");
 	if(!vol->mft_ino || (error=ntfs_init_inode(vol->mft_ino,vol,FILE_MFT)))
 	{
 		ntfs_error("Problem loading MFT\n");
 		return error;
 	}
+	ntfs_debug(DEBUG_BSD,"Going to load MIRR\n");
 	vol->mftmirr=vol->mft_ino+1;
 	if((error=ntfs_init_inode(vol->mftmirr,vol,FILE_MFTMIRR))){
 		ntfs_error("Problem %d loading MFTMirr\n",error);
 		return error;
 	}
+	ntfs_debug(DEBUG_BSD,"Going to load BITMAP\n");
 	vol->bitmap=vol->mft_ino+2;
 	if((error=ntfs_init_inode(vol->bitmap,vol,FILE_BITMAP))){
 		ntfs_error("Problem loading Bitmap\n");
 		return error;
 	}
+	ntfs_debug(DEBUG_BSD,"Going to load UPCASE\n");
 	error=ntfs_init_inode(&upcase,vol,FILE_UPCASE);
 	if(error)return error;
 	ntfs_init_upcase(&upcase);
 	ntfs_clear_inode(&upcase);
+	ntfs_debug(DEBUG_BSD,"Going to load ATTRDEF\n");
 	error=ntfs_init_inode(&attrdef,vol,FILE_ATTRDEF);
 	if(error)return error;
 	error=ntfs_init_attrdef(&attrdef);
@@ -245,7 +250,7 @@
 {
 	ntfs_io io;
 	char *cluster0=ntfs_malloc(vol->clustersize);
-	int size;
+	ntfs_u64 size;
 
 	io.fn_put=ntfs_put;
 	io.fn_get=ntfs_get;
@@ -255,8 +260,9 @@
 	ntfs_getput_clusters(vol,0,0,&io);
 	size=NTFS_GETU64(cluster0+0x28);
 	ntfs_free(cluster0);
-	size/=vol->clusterfactor;
-	return size;
+	/* FIXME: more than 2**32 cluster */
+	/* FIXME: gcc will emit udivdi3 if we don't truncate it */
+	return ((unsigned int)size)/vol->clusterfactor;
 }
 
 static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
@@ -323,7 +329,7 @@
    Return the largest block found in *cnt. Return 0 on success, ENOSPC if
    all bits are used */
 static int 
-search_bits(unsigned char* bits,int *loc,int *cnt,int l)
+search_bits(unsigned char* bits,ntfs_cluster_t *loc,int *cnt,int l)
 {
 	unsigned char c=0;
 	int bc=0;
@@ -384,7 +390,7 @@
 }
 
 int 
-ntfs_set_bitrange(ntfs_inode* bitmap,int loc,int cnt,int bit)
+ntfs_set_bitrange(ntfs_inode* bitmap,ntfs_cluster_t loc,int cnt,int bit)
 {
 	int bsize,locit,error;
 	unsigned char *bits,*it;
@@ -392,13 +398,13 @@
 
 	io.fn_put=ntfs_put;
 	io.fn_get=ntfs_get;
-	bsize=(cnt+loc%8+7)/8; /* round up */
+	bsize=(cnt+(loc & 7)+7) & ~7; /* round up to multiple of 8*/
 	bits=ntfs_malloc(bsize);
 	io.param=bits;
 	io.size=bsize;
 	if(!bits)
 		return ENOMEM;
-	error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+	error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,loc>>3,&io);
 	if(error || io.size!=bsize){
 		ntfs_free(bits);
 		return error?error:EIO;
@@ -431,7 +437,7 @@
 	/* reset to start */
 	io.param=bits;
 	io.size=bsize;
-	error=ntfs_write_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+	error=ntfs_write_attr(bitmap,bitmap->vol->at_data,0,loc>>3,&io);
 	ntfs_free(bits);
 	if(error)return error;
 	if(io.size!=bsize)
@@ -445,7 +451,7 @@
    it does not matter where the clusters are. Result is 0 if
    success, in which case location and count says what they really got */
 int 
-ntfs_search_bits(ntfs_inode* bitmap, int *location, int *count, int flags)
+ntfs_search_bits(ntfs_inode* bitmap, ntfs_cluster_t *location, int *count, int flags)
 {
 	unsigned char *bits;
 	ntfs_io io;
@@ -459,7 +465,7 @@
 	io.param=bits;
 
 	/* first search within +/- 8192 clusters */
-	start=*location/8;
+	start=*location>>3;
 	start= start>1024 ? start-1024 : 0;
 	io.size=2048;
 	error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,start,&io);
@@ -483,7 +489,7 @@
 		error=ntfs_read_attr(bitmap,bitmap->vol->at_data,
 				     0,start,&io);
 		if(error)goto fail;
-		if(io.size==0) {
+		if(io.size==0){
 			if(found)
 				goto success;
 			else{
@@ -524,7 +530,7 @@
 	return error;
 }
 
-int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location, int *count,
 	int flags)
 {
 	int error;
@@ -532,7 +538,7 @@
 	return error;
 }
 
-int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count)
+int ntfs_deallocate_clusters(ntfs_volume *vol, ntfs_cluster_t location, int count)
 {
 	int error;
 	error=ntfs_set_bitrange(vol->bitmap,location,count,0);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/super.h linux/fs/ntfs/super.h
--- v2.2.0-pre3/linux/fs/ntfs/super.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/super.h	Sat Jan  2 10:24:46 1999
@@ -16,6 +16,6 @@
 int ntfs_release_volume(ntfs_volume *vol);
 void ntfs_insert_fixups(unsigned char *rec, int secsize);
 int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size);
-int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location, int *count,
   int flags);
-int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count);
+int ntfs_deallocate_clusters(ntfs_volume *vol, ntfs_cluster_t location, int count);
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/support.c linux/fs/ntfs/support.c
--- v2.2.0-pre3/linux/fs/ntfs/support.c	Wed Jun 24 22:54:10 1998
+++ linux/fs/ntfs/support.c	Sat Jan  2 10:24:46 1999
@@ -7,7 +7,10 @@
  *
  */
 
-#include "types.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "ntfstypes.h"
 #include "struct.h"
 #include "support.h"
 
@@ -86,17 +89,20 @@
 	memset(s, 0, n);
 }
 
-void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
+/* These functions deliberately return no value. It is dest, anyway,
+   and not used anywhere in the NTFS code.  */
+
+void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
 {
-	return memcpy(dest, src, n);
+	memcpy(dest, src, n);
 }
 
-void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
+void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
 {
-	return memmove(dest, src, n);
+	memmove(dest, src, n);
 }
 
-/* Warn that an error occurred. */
+/* Warn that an error occured. */
 void ntfs_error(const char *fmt,...)
 {
         va_list ap;
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/support.h linux/fs/ntfs/support.h
--- v2.2.0-pre3/linux/fs/ntfs/support.h	Fri Jan  2 01:42:59 1998
+++ linux/fs/ntfs/support.h	Sat Jan  2 10:24:46 1999
@@ -8,6 +8,16 @@
 /* Debug levels */
 #define DEBUG_OTHER	1
 #define DEBUG_MALLOC	2
+#define DEBUG_BSD       4
+#define DEBUG_LINUX     8
+#define DEBUG_DIR1     16
+#define DEBUG_DIR2     32
+#define DEBUG_DIR3     64
+#define DEBUG_FILE1   128
+#define DEBUG_FILE2   256
+#define DEBUG_FILE3   512
+#define DEBUG_NAME1  1024
+#define DEBUG_NAME2  2048
 
 void ntfs_debug(int mask, const char *fmt, ...);
 #ifdef NTFS_IN_LINUX_KERNEL
@@ -19,8 +29,8 @@
 void ntfs_free(void *block);
 #endif
 void ntfs_bzero(void *s, int n);
-void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
-void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
+void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
+void ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
 void ntfs_error(const char *fmt,...);
 int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
 int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/sysctl.h linux/fs/ntfs/sysctl.h
--- v2.2.0-pre3/linux/fs/ntfs/sysctl.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/sysctl.h	Sat Jan  2 10:24:46 1999
@@ -7,6 +7,10 @@
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #ifdef DEBUG
 extern int ntdebug;
 
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/types.h linux/fs/ntfs/types.h
--- v2.2.0-pre3/linux/fs/ntfs/types.h	Wed Apr  1 20:11:53 1998
+++ linux/fs/ntfs/types.h	Wed Dec 31 16:00:00 1969
@@ -1,128 +0,0 @@
-/*
- *  types.h
- *  This file defines four things:
- *   - generic platform independent fixed-size types (e.g. ntfs_u32)
- *   - specific fixed-size types (e.g. ntfs_offset_t)
- *   - macros that read and write those types from and to byte arrays
- *   - types derived from OS specific ones
- *
- *  Copyright (C) 1996 Martin von Löwis
- */
-
-#ifdef NTFS_IN_LINUX_KERNEL
-/* get installed types if we compile the kernel*/
-#include <linux/fs.h>
-#endif
-
-#if defined(i386) || defined(__i386__) || defined(__alpha__)
-
-/* unsigned integral types */
-#ifndef NTFS_INTEGRAL_TYPES
-#define NTFS_INTEGRAL_TYPES
-typedef unsigned char      ntfs_u8;
-typedef unsigned short     ntfs_u16;
-typedef unsigned int       ntfs_u32;
-typedef unsigned long long ntfs_u64;
-#endif
-
-/* unicode character type */
-#ifndef NTFS_WCHAR_T
-#define NTFS_WCHAR_T
-typedef unsigned short     ntfs_wchar_t;
-#endif
-/* file offset */
-#ifndef NTFS_OFFSET_T
-#define NTFS_OFFSET_T
-typedef unsigned long long ntfs_offset_t;
-#endif
-/* UTC */
-#ifndef NTFS_TIME64_T
-#define NTFS_TIME64_T
-typedef unsigned long long ntfs_time64_t;
-#endif
-/* This is really unsigned long long. So we support only volumes up to 2 TB */
-#ifndef NTFS_CLUSTER_T
-#define NTFS_CLUSTER_T
-typedef unsigned int ntfs_cluster_t;
-#endif
-
-/* Macros reading unsigned integers from a byte pointer */
-/* these should work for all little endian machines */
-#define NTFS_GETU8(p)      (*(ntfs_u8*)(p))
-#define NTFS_GETU16(p)     (*(ntfs_u16*)(p))
-#define NTFS_GETU24(p)     (NTFS_GETU32(p) & 0xFFFFFF)
-#define NTFS_GETU32(p)     (*(ntfs_u32*)(p))
-#define NTFS_GETU64(p)     (*(ntfs_u64*)(p))
-
-/* Macros writing unsigned integers */
-#define NTFS_PUTU8(p,v)      (*(ntfs_u8*)(p))=(v)
-#define NTFS_PUTU16(p,v)     (*(ntfs_u16*)(p))=(v)
-#define NTFS_PUTU24(p,v)     NTFS_PUTU16(p,(v) & 0xFFFF);\
-                             NTFS_PUTU8(((char*)p)+2,(v)>>16)
-#define NTFS_PUTU32(p,v)     (*(ntfs_u32*)(p))=(v)
-#define NTFS_PUTU64(p,v)     (*(ntfs_u64*)(p))=(v)
-
-/* Macros reading signed integers, returning int */
-#define NTFS_GETS8(p)        ((int)(*(char*)(p)))
-#define NTFS_GETS16(p)       ((int)(*(short*)(p)))
-#define NTFS_GETS24(p)       (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) | 0xFF000000))
-
-#define NTFS_PUTS8(p,v)      NTFS_PUTU8(p,v)
-#define NTFS_PUTS16(p,v)     NTFS_PUTU16(p,v)
-#define NTFS_PUTS24(p,v)     NTFS_PUTU24(p,v)
-#define NTFS_PUTS32(p,v)     NTFS_PUTU32(p,v)
-
-#else
-#error Put your machine description here
-#endif
-
-/* architecture independent macros */
-
-/* PUTU32 would not clear all bytes */
-#define NTFS_PUTINUM(p,i)    NTFS_PUTU64(p,i->i_number);\
-                             NTFS_PUTU16(((char*)p)+6,i->sequence_number)
-
-/* system dependent types */
-#ifdef __linux__
-/* We always need kernel types, because glibc makes them of different size */
-#include <asm/posix_types.h>
-/* Avoid a type redefinition with future include of glibc <stdlib.h> */
-#undef __FD_ZERO
-#undef __FD_SET
-#undef __FD_CLR
-#undef __FD_ISSET
-#ifndef NTMODE_T
-#define NTMODE_T
-typedef __kernel_mode_t ntmode_t;
-#endif
-#ifndef NTFS_UID_T
-#define NTFS_UID_T
-typedef __kernel_uid_t ntfs_uid_t;
-#endif
-#ifndef NTFS_GID_T
-#define NTFS_GID_T
-typedef __kernel_gid_t ntfs_gid_t;
-#endif
-#ifndef NTFS_SIZE_T
-#define NTFS_SIZE_T
-typedef __kernel_size_t ntfs_size_t;
-#endif
-#ifndef NTFS_TIME_T
-#define NTFS_TIME_T
-typedef __kernel_time_t ntfs_time_t;
-#endif
-#else
-#include <sys/types.h>
-#include <sys/stat.h>
-typedef mode_t ntmode_t;
-typedef uid_t ntfs_uid_t;
-typedef gid_t ntfs_gid_t;
-typedef size_t ntfs_size_t;
-typedef time_t ntfs_time_t;
-#endif
-
-/*
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/util.c linux/fs/ntfs/util.c
--- v2.2.0-pre3/linux/fs/ntfs/util.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/ntfs/util.c	Sat Jan  2 10:24:46 1999
@@ -2,13 +2,13 @@
  *  util.c
  *  Miscellaneous support
  *
- *  Copyright (C) 1997 Martin von Löwis
+ *  Copyright (C) 1997,1999 Martin von Löwis
  *  Copyright (C) 1997 Régis Duchesne
  *
- *  The utf8 routines are copied from Python wstrop module,
+ *  The utf8 routines are copied from Python wstrop module.
  */
 
-#include "types.h"
+#include "ntfstypes.h"
 #include "struct.h"
 #include "util.h"
 
@@ -20,6 +20,7 @@
 #include "support.h"
 
 /* Converts a single wide character to a sequence of utf8 bytes.
+ * The character is represented in host byte order.
  * Returns the number of bytes, or 0 on error.
  */
 static int
@@ -28,7 +29,7 @@
 	if(c==0)
 		return 0; /* No support for embedded 0 runes */
 	if(c<0x80){
-		if(buf)buf[0]=c;
+		if(buf)buf[0]=(unsigned char)c;
 		return 1;
 	}
 	if(c<0x800){
@@ -51,7 +52,8 @@
 }
 
 /* Decodes a sequence of utf8 bytes into a single wide character.
- * Returns the number of bytes consumed, or 0 on error
+ * The character is returned in host byte order.
+ * Returns the number of bytes consumed, or 0 on error.
  */
 static int
 from_utf8(const unsigned char* str,ntfs_u16 *c)
@@ -98,10 +100,10 @@
 	int len8;
 	unsigned char *result;
 
-	ntfs_debug(DEBUG_OTHER,"converting l=%d\n",in_len);
+	ntfs_debug(DEBUG_NAME1,"converting l=%d\n",in_len);
 	/* count the length of the resulting UTF-8 */
 	for(i=len8=0;i<in_len;i++){
-		tmp=to_utf8(in[i],0);
+		tmp=to_utf8(NTFS_GETU16(in+i),0);
 		if(!tmp)
 			/* invalid character */
 			return EILSEQ;
@@ -114,7 +116,8 @@
 	result[len8]='\0';
 	*out_len=len8;
 	for(i=len8=0;i<in_len;i++)
-		len8+=to_utf8(in[i],result+len8);
+		len8+=to_utf8(NTFS_GETU16(in+i),result+len8);
+	ntfs_debug(DEBUG_NAME1,"result %p:%s\n",result,result);
 	return 0;
 }
 
@@ -139,7 +142,10 @@
 	result[len16]=0;
 	*out_len=len16;
 	for(i=len16=0;i<in_len;i+=tmp,len16++)
-		tmp=from_utf8(in+i,result+len16);
+	{
+		tmp=from_utf8(in+i, &wtmp);
+		NTFS_PUTU16(result+len16, wtmp);
+	}
 	return 0;
 }
 
@@ -151,7 +157,7 @@
 
 	/* check for characters out of range */
 	for(i=0;i<in_len;i++)
-		if(in[i]>=256)
+		if(NTFS_GETU16(in+i)>=256)
 			return EILSEQ;
 	*out=result=ntfs_malloc(in_len+1);
 	if(!result)
@@ -159,7 +165,7 @@
 	result[in_len]='\0';
 	*out_len=in_len;
 	for(i=0;i<in_len;i++)
-		result[i]=in[i];
+		result[i]=(unsigned char)NTFS_GETU16(in+i);
 	return 0;
 }
 
@@ -174,7 +180,7 @@
 		return ENOMEM;
 	*out_len=in_len;
 	for(i=0;i<in_len;i++)
-		result[i]=in[i];
+		NTFS_PUTU16(result+i,in[i]);
 	return 0;
 }
 
@@ -210,19 +216,18 @@
 void ntfs_put(ntfs_io *dest,void *src,ntfs_size_t n)
 {
 	ntfs_memcpy(dest->param,src,n);
-	dest->param+=n;
+	((char*)dest->param)+=n;
 }
 
 void ntfs_get(void* dest,ntfs_io *src,ntfs_size_t n)
 {
 	ntfs_memcpy(dest,src->param,n);
-	src->param+=n;
+	((char*)src->param)+=n;
 }
 
 void *ntfs_calloc(int size)
 {
 	void *result=ntfs_malloc(size);
-
 	if(result)
 		ntfs_bzero(result,size);
 	return result;
@@ -272,9 +277,9 @@
 
 	for(i=0;i<n;i++)
 	{
-		if(a[i]<b[i])
+		if(NTFS_GETU16(a+i)<b[i])
 			return -1;
-		if(b[i]<a[i])
+		if(b[i]<NTFS_GETU16(a+i))
 			return 1;
 	}
 	return 0;
@@ -292,7 +297,7 @@
  * 3: Floating point math in the kernel would corrupt user data
  */
 	const unsigned int D = 10000000;
-	unsigned int H = (ntutc >> 32);
+	unsigned int H = (unsigned int)(ntutc >> 32);
 	unsigned int L = (unsigned int)ntutc;
 	unsigned int numerator2;
 	unsigned int lowseconds;
@@ -334,6 +339,25 @@
 ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
 {
 	return ((t + (ntfs_time64_t)(369*365+89)*24*3600) * 10000000);
+}
+
+/* Fill index name. */
+
+void
+ntfs_indexname(char *buf, int type)
+{
+	char hex[]="0123456789ABCDEF";
+	int index;
+	*buf++='$';
+	*buf++='I';
+	for (index=24; index>0; index-=4)
+		if((0xF << index) & type)
+			break;
+	while(index>=0) {
+		*buf++ = hex[(type >> index) & 0xF];
+		index-=4;
+	}
+	*buf='\0';
 }
 
 /*
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/ntfs/util.h linux/fs/ntfs/util.h
--- v2.2.0-pre3/linux/fs/ntfs/util.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/util.h	Sat Jan  2 10:24:46 1999
@@ -56,6 +56,9 @@
 ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
 ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
 
+/* Attribute names */
+void ntfs_indexname(char *buf, int type);
+
 /*
  * Local variables:
  * c-file-style: "linux"
diff -u --recursive --new-file v2.2.0-pre3/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v2.2.0-pre3/linux/fs/proc/procfs_syms.c	Fri Oct 23 22:01:22 1998
+++ linux/fs/proc/procfs_syms.c	Sat Jan  2 17:55:06 1999
@@ -23,7 +23,6 @@
 EXPORT_SYMBOL(proc_root);
 EXPORT_SYMBOL(proc_root_fs);
 EXPORT_SYMBOL(proc_get_inode);
-EXPORT_SYMBOL(in_group_p);
 EXPORT_SYMBOL(proc_dir_inode_operations);
 EXPORT_SYMBOL(proc_net_inode_operations);
 EXPORT_SYMBOL(proc_net);
diff -u --recursive --new-file v2.2.0-pre3/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.2.0-pre3/linux/include/linux/fs.h	Fri Jan  1 12:58:21 1999
+++ linux/include/linux/fs.h	Sat Jan  2 19:09:50 1999
@@ -254,10 +254,15 @@
 /*
  * Deprecated - we don't keep per-buffer reference flags
  * any more.
+ *
+ * We _could_ try to update the page reference, but that
+ * doesn't seem to really be worth it either. If we did,
+ * it would look something like this:
+ *
+ *	#define buffer_page(bh)		(mem_map + MAP_NR((bh)->b_data))
+ *	#define touch_buffer(bh)	set_bit(PG_referenced, &buffer_page(bh)->flags)
  */
-#define buffer_page(bh)		(mem_map + MAP_NR((bh)->b_data))
-#define buffer_touched(bh)	(PageReferenced(buffer_page(bh)))
-#define touch_buffer(bh)	set_bit(PG_referenced, buffer_page(bh))
+#define touch_buffer(bh)	do { } while (0)
 
 #include <linux/pipe_fs_i.h>
 #include <linux/minix_fs_i.h>
diff -u --recursive --new-file v2.2.0-pre3/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.2.0-pre3/linux/include/linux/pci.h	Thu Dec 31 10:29:03 1998
+++ linux/include/linux/pci.h	Sat Jan  2 18:11:22 1999
@@ -1057,6 +1057,7 @@
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
 #define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
 #define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
 
 #define PCI_VENDOR_ID_ATRONICS		0x907f
diff -u --recursive --new-file v2.2.0-pre3/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.2.0-pre3/linux/include/linux/proc_fs.h	Tue Dec 22 14:16:58 1998
+++ linux/include/linux/proc_fs.h	Sat Jan  2 17:55:06 1999
@@ -297,6 +297,8 @@
 extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
 				off_t offset, int length, int inout);
 
+#ifdef CONFIG_PROC_FS
+
 extern struct proc_dir_entry proc_root;
 extern struct proc_dir_entry proc_root_fs;
 extern struct proc_dir_entry *proc_net;
@@ -436,4 +438,26 @@
  */
 extern void proc_device_tree_init(void);
 
+#else
+
+extern inline int proc_register(struct proc_dir_entry *a, struct proc_dir_entry *b) {};
+extern inline int proc_unregister(struct proc_dir_entry *a, int b) {};
+extern inline int proc_net_register(struct proc_dir_entry *a) {};
+extern inline int proc_net_unregister(int x) {};
+extern inline int proc_scsi_register(struct proc_dir_entry *b, struct proc_dir_entry *c) {};
+extern inline int proc_scsi_unregister(struct proc_dir_entry *a, int x);
+
+extern inline struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+					 struct proc_dir_entry *parent)
+{
+	return NULL;
+}
+
+extern inline void remove_proc_entry(const char *name, struct proc_dir_entry *parent) {};
+
+extern inline void proc_tty_register_driver(struct tty_driver *driver) {};
+extern inline void proc_tty_unregister_driver(struct tty_driver *driver) {};
+
+
+#endif
 #endif /* _LINUX_PROC_FS_H */
diff -u --recursive --new-file v2.2.0-pre3/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h
--- v2.2.0-pre3/linux/include/linux/sunrpc/stats.h	Fri Jul 31 17:09:54 1998
+++ linux/include/linux/sunrpc/stats.h	Sat Jan  2 17:55:06 1999
@@ -44,6 +44,7 @@
 void			rpc_modcount(struct inode *, int);
 #endif
 
+#ifdef CONFIG_PROC_FS
 struct proc_dir_entry *	rpc_proc_register(struct rpc_stat *);
 void			rpc_proc_unregister(const char *);
 int			rpc_proc_read(char *, char **, off_t, int,
@@ -54,5 +55,19 @@
 int			svc_proc_read(char *, char **, off_t, int,
 					int *, void *);
 void			svc_proc_zero(struct svc_program *);
+
+#else
+
+extern inline void svc_proc_unregister(const char *p) {}
+extern inline struct proc_dir_entry*svc_proc_register(struct svc_stat *s)
+{
+	return NULL;
+}
+
+extern inline int svc_proc_read(char *a, char **b, off_t c, int d, int *e, void *f)
+{
+	return 0;
+}
+#endif
 
 #endif /* _LINUX_SUNRPC_STATS_H */
diff -u --recursive --new-file v2.2.0-pre3/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v2.2.0-pre3/linux/include/linux/swapctl.h	Tue Dec 22 14:16:58 1998
+++ linux/include/linux/swapctl.h	Fri Jan  1 22:31:21 1999
@@ -90,18 +90,6 @@
 #define PAGE_DECLINE		(swap_control.sc_page_decline)
 #define PAGE_INITIAL_AGE	(swap_control.sc_page_initial_age)
 
-/* Given a resource of N units (pages or buffers etc), we only try to
- * age and reclaim AGE_CLUSTER_FRACT per 1024 resources each time we
- * scan the resource list. */
-static inline int AGE_CLUSTER_SIZE(int resources)
-{
-	unsigned int n = (resources * AGE_CLUSTER_FRACT) >> 10;
-	if (n < AGE_CLUSTER_MIN)
-		return AGE_CLUSTER_MIN;
-	else
-		return n;
-}
-
 #endif /* __KERNEL */
 
 #endif /* _LINUX_SWAPCTL_H */
diff -u --recursive --new-file v2.2.0-pre3/linux/ipc/util.c linux/ipc/util.c
--- v2.2.0-pre3/linux/ipc/util.c	Fri Nov 27 13:09:29 1998
+++ linux/ipc/util.c	Sat Jan  2 10:22:36 1999
@@ -12,7 +12,8 @@
 #include <linux/mm.h>
 #include <linux/shm.h>
 #include <linux/init.h>
-
+#include <linux/msg.h>
+#include <asm/ipc.h>
 #include <asm/uaccess.h>
 
 #if defined(CONFIG_SYSVIPC)
diff -u --recursive --new-file v2.2.0-pre3/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.2.0-pre3/linux/mm/page_alloc.c	Fri Jan  1 12:58:21 1999
+++ linux/mm/page_alloc.c	Sat Jan  2 19:02:16 1999
@@ -151,6 +151,7 @@
 	if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
 		if (PageSwapCache(page))
 			panic ("Freeing swap cache page");
+		page->flags &= ~(1 << PG_referenced);
 		free_pages_ok(page->map_nr, 0);
 		return;
 	}
@@ -172,6 +173,7 @@
 		if (atomic_dec_and_test(&map->count)) {
 			if (PageSwapCache(map))
 				panic ("Freeing swap cache pages");
+			map->flags &= ~(1 << PG_referenced);
 			free_pages_ok(map_nr, order);
 			return;
 		}
@@ -197,8 +199,9 @@
 	do { struct page *prev = memory_head(area), *ret = prev->next; \
 		while (memory_head(area) != ret) { \
 			if (!dma || CAN_DMA(ret)) { \
-				unsigned long map_nr = ret->map_nr; \
+				unsigned long map_nr; \
 				(prev->next = ret->next)->prev = prev; \
+				map_nr = ret->map_nr; \
 				MARK_USED(map_nr, new_order, area); \
 				nr_free_pages -= 1 << order; \
 				EXPAND(ret, map_nr, order, new_order, area); \
diff -u --recursive --new-file v2.2.0-pre3/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.2.0-pre3/linux/mm/vmscan.c	Fri Jan  1 12:58:21 1999
+++ linux/mm/vmscan.c	Fri Jan  1 22:41:58 1999
@@ -363,13 +363,23 @@
 	/* 
 	 * We make one or two passes through the task list, indexed by 
 	 * assign = {0, 1}:
-	 *   Pass 1: select the swappable task with maximal swap_cnt.
-	 *   Pass 2: assign new swap_cnt values, then select as above.
+	 *   Pass 1: select the swappable task with maximal RSS that has
+	 *         not yet been swapped out. 
+	 *   Pass 2: re-assign rss swap_cnt values, then select as above.
+	 *
 	 * With this approach, there's no need to remember the last task
 	 * swapped out.  If the swap-out fails, we clear swap_cnt so the 
 	 * task won't be selected again until all others have been tried.
+	 *
+	 * Think of swap_cnt as a "shadow rss" - it tells us which process
+	 * we want to page out (always try largest first).
 	 */
-	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
+	counter = nr_tasks / (priority+1);
+	if (counter < 1)
+		counter = 1;
+	if (counter > nr_tasks)
+		counter = nr_tasks;
+
 	for (; counter >= 0; counter--) {
 		assign = 0;
 		max_cnt = 0;
@@ -382,15 +392,9 @@
 				continue;
 	 		if (p->mm->rss <= 0)
 				continue;
-			if (assign) {
-				/* 
-				 * If we didn't select a task on pass 1, 
-				 * assign each task a new swap_cnt.
-				 * Normalise the number of pages swapped
-				 * by multiplying by (RSS / 1MB)
-				 */
-				p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
-			}
+			/* Refresh swap_cnt? */
+			if (assign)
+				p->swap_cnt = p->mm->rss;
 			if (p->swap_cnt > max_cnt) {
 				max_cnt = p->swap_cnt;
 				pbest = p;
@@ -404,14 +408,13 @@
 			}
 			goto out;
 		}
-		pbest->swap_cnt--;
 
 		/*
 		 * Nonzero means we cleared out something, but only "1" means
 		 * that we actually free'd up a page as a result.
 		 */
 		if (swap_out_process(pbest, gfp_mask) == 1)
-				return 1;
+			return 1;
 	}
 out:
 	return 0;
@@ -451,19 +454,17 @@
 	/* max one hundreth of a second */
 	end_time = jiffies + (HZ-1)/100;
 	do {
-		int priority = 5;
+		int priority = 8;
 		int count = pager_daemon.swap_cluster;
 
 		switch (kswapd_state) {
 			do {
 			default:
 				free_memory(shrink_mmap(priority, 0));
+				free_memory(swap_out(priority, 0));
 				kswapd_state++;
 			case 1:
 				free_memory(shm_swap(priority, 0));
-				kswapd_state++;
-			case 2:
-				free_memory(swap_out(priority, 0));
 				shrink_dcache_memory(priority, 0);
 				kswapd_state = 0;
 			} while (--priority >= 0);
@@ -562,7 +563,7 @@
 
 		current->flags |= PF_MEMALLOC;
 	
-		priority = 5;
+		priority = 8;
 		do {
 			free_memory(shrink_mmap(priority, gfp_mask));
 			free_memory(shm_swap(priority, gfp_mask));
diff -u --recursive --new-file v2.2.0-pre3/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.2.0-pre3/linux/net/sunrpc/sched.c	Thu Dec 31 10:29:03 1998
+++ linux/net/sunrpc/sched.c	Sat Jan  2 17:55:06 1999
@@ -6,7 +6,6 @@
  * Copyright (C) 1996 Olaf Kirch, <okir@monad.swb.de>
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 
 #define __KERNEL_SYSCALLS__
diff -u --recursive --new-file v2.2.0-pre3/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c
--- v2.2.0-pre3/linux/net/sunrpc/stats.c	Mon Jan 12 14:39:07 1998
+++ linux/net/sunrpc/stats.c	Sat Jan  2 17:55:06 1999
@@ -12,6 +12,7 @@
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
+#define __NO_VERSION__
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.2.0-pre3/linux/net/sunrpc/sysctl.c linux/net/sunrpc/sysctl.c
--- v2.2.0-pre3/linux/net/sunrpc/sysctl.c	Mon Jan 12 19:43:21 1998
+++ linux/net/sunrpc/sysctl.c	Sat Jan  2 17:55:06 1999
@@ -38,8 +38,10 @@
 	if (!sunrpc_table_header) {
 		sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
 #ifdef MODULE
+#ifdef CONFIG_PROC_FS
 		if (sunrpc_table[0].de)
 			sunrpc_table[0].de->fill_inode = rpc_modcount;
+#endif
 #endif
 	}
 			
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/header.tk linux/scripts/header.tk
--- v2.2.0-pre3/linux/scripts/header.tk	Wed Jun 24 22:54:14 1998
+++ linux/scripts/header.tk	Sat Jan  2 10:29:11 1999
@@ -145,7 +145,7 @@
 			 -relief raised 
 		label $w.bm -bitmap error
 		pack $w.bm $w.m -pady 10 -side top -padx 10
-		wm title $w "Oops" 
+		wm title $w "Xconfig Internal Error" 
 
 		set oldFocus [focus]
 		frame $w.f
@@ -173,7 +173,7 @@
 			 -relief raised 
 		label $w.bm -bitmap error
 		pack $w.bm $w.m -pady 10 -side top -padx 10
-		wm title $w "Oops" 
+		wm title $w "Xconfig Internal Error" 
 
 		set oldFocus [focus]
 		frame $w.f
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/Makefile linux/scripts/ksymoops-0.6/Makefile
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/Makefile	Mon Nov  2 07:46:47 1998
@@ -0,0 +1,72 @@
+# Description file for ksymoops
+
+#	Tue Nov  3 02:31:01 EST 1998
+#	Version 0.6
+#	Read lsmod (/proc/modules).
+#	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+#	Upper case variables.
+#	Convert from a.out to bfd, using same format as ksymoops.
+
+DEFS = Makefile ksymoops.h
+
+# Defaults for vmlinux, ksyms, objects, lsmod, System.map.  Externalised so
+# distributions can tweak to suit their own file system layout.
+
+# To default to not reading a source, set to any empty string.
+# To default to reading a source, supply a quoted and escaped string.
+
+# If the string contains *r (*m, *n, *s) then it is replaced at run time by
+# the current value of `uname -r` (-m, -n, -s).  '*' was chosen as something
+# that rarely appears in filenames and does not cause problems like '%' or '$'.
+
+DEF_VMLINUX =				# default no vmlinux
+DEF_OBJECTS = \"/lib/modules/*r/\"	# default current modules
+DEF_KSYMS = \"/proc/ksyms\"		# default current ksyms
+DEF_LSMOD = \"/proc/modules\"		# default current lsmod
+DEF_MAP = \"/usr/src/linux/System.map\"	# default current map
+
+# RedHat users might want defaults like these
+# DEF_MAP = \"/boot/System.map-*r\"
+# DEF_OBJECTS = \"/boot/module-info-*r\"
+
+PROGS = ksymoops
+
+CC=gcc
+CFLAGS = -Dlinux \
+	 -Wall \
+	 -Wno-conversion \
+	 -Waggregate-return \
+	 -Wstrict-prototypes \
+	 -Wmissing-prototypes \
+	 $(DEBUG)
+
+ifneq ($(strip $(DEF_VMLINUX)),)
+	CFLAGS += -DDEF_VMLINUX=$(strip $(DEF_VMLINUX))
+endif
+ifneq ($(strip $(DEF_OBJECTS)),)
+	CFLAGS += -DDEF_OBJECTS=$(strip $(DEF_OBJECTS))
+endif
+ifneq ($(strip $(DEF_KSYMS)),)
+	CFLAGS += -DDEF_KSYMS=$(strip $(DEF_KSYMS))
+endif
+ifneq ($(strip $(DEF_LSMOD)),)
+	CFLAGS += -DDEF_LSMOD=$(strip $(DEF_LSMOD))
+endif
+ifneq ($(strip $(DEF_MAP)),)
+	CFLAGS += -DDEF_MAP=$(strip $(DEF_MAP))
+endif
+
+OBJECTS = io.o ksyms.o ksymoops.o map.o misc.o object.o oops.o re.o symbol.o
+
+all: 	$(PROGS)
+
+:	$(OBJECTS)
+
+$(OBJECTS): $(DEFS)
+
+$(PROGS): %: %.o $(DEFS) $(OBJECTS)
+	$(CC) $(OBJECTS) $(CFLAGS) -lbfd -liberty -o $@
+	-@size $@
+
+clean:
+	rm -f core *.o $(PROGS)
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/README linux/scripts/ksymoops-0.6/README
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/README	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/README	Mon Nov  2 07:43:43 1998
@@ -0,0 +1,358 @@
+  ksymoops.
+
+  Read a kernel Oops file and make the best stab at converting the code to
+  instructions and mapping stack values to kernel symbols.
+
+  Copyright Keith Owens <kaos@ocs.com.au>.
+  Released under the GNU Public Licence, Version 2.
+
+  To compile, simply type "make" in the ksymoops directory.
+
+  TESTERS WANTED.
+
+  ksymoops handles ix86.  It appears to handle Alpha, Sparc, M68K, PPC,
+  MIPS but I have no machine to test on.  I would appreciate feedback
+  from users of non ix86 machines.  In particular, it would be nice if
+  you could run
+
+   ksymoops -VMO -k /proc/ksyms -dd <oops.file >/tmp/ksymoops.log 2>&1
+
+  and mail /tmp/ksymoops.log to kaos@ocs.com.au
+
+  The patches subdirectory contains some arch specific patches to
+  provide more info on their Oops reports.  At the moment (2.1.126),
+  some archs do not print traces or do not print code lines, makes it
+  impossible to report properly.
+
+  TODO:
+  Performance improvements.  Reading a large log is quite slow, probably
+	  one of the Oops regular expressions is pathological.
+  Clean up these docs.
+  Add "guess", "same" options, distinguish between default and supplied
+  values on report (Andries, I get the message :).
+
+  Tue Nov  3 02:31:01 EST 1998
+  Version 0.6
+  Read lsmod (/proc/modules).
+
+  Wed Oct 28 23:14:55 EST 1998
+  Version 0.5
+  No longer read vmlinux by default, it only duplicates System.map.
+
+  Wed Oct 28 13:46:39 EST 1998
+  Version 0.4
+  Split into separate sources.
+
+  Mon Oct 26 00:01:47 EST 1998
+  Version 0.3c
+  Add alpha (arm) processing.
+
+  Mon Oct 26 00:01:47 EST 1998
+  Version 0.3b
+  Add sparc processing.
+  Handle kernel symbol versions.
+
+  Fri Oct 23 13:11:20 EST 1998
+  Version 0.3
+  Add -follow to find command for people who use symlinks to modules.
+  Add Version_ checking.
+
+  Thu Oct 22 22:28:30 EST 1998
+  Version 0.2.
+  Generalise text prefix handling.
+  Handle messages on Code: line.
+  Format addresses with leading zeroes.
+  Minor bug fixes.
+
+  Wed Oct 21 23:28:48 EST 1998
+  Version 0.1.  Rewrite from scratch in C.
+
+  CREDITS.
+  Oops disassembly based on ksymoops.cc,
+    Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+  m68k code based on ksymoops.cc changes by
+    Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+
+  This code subsumes the Perl script make_System.map.pl which is no longer
+  supported.
+
+  Why another ksymoops I hear you ask?  Various complaints about
+  ksymoops.cc -
+
+  * It requires C++.
+  * It has hard wired limitations on the number of symbols.
+  * It does not handle modules at all.
+  * Very rigid requirements on the format of input, especially the Oops
+    log.
+  * No cross checking between ksyms, modules, System.map etc.
+  * Very little error checking, diagnostics are not suitable for
+    beginners.
+  * It only prints the trace and decoded code, users have to manually
+    extract the other lines from the Oops.
+  * Gives up on the slightest problem.
+  * Only handles i386 and possibly m68k.  The code is difficult to extend
+    to other architectures.
+  * Stops after the first Oops, you have to manually extract each one and
+    run through ksymoops one at a time.
+
+  This version is -
+  * C.
+  * No hard wired limitations (malloc as far as the eye can see).
+  * Handles modules by default.
+  * Uses regular pattern matching so it is a lot more forgiving about
+    input formats.
+  * By default, cross checks ksyms, modules, System.map and vmlinux.
+  * Lots of diagnostics and error checking.
+  * Prints all relevant lines for a complete Oops report.
+  * Tries to provide output no matter how bad the input is.  The level of
+     progress and error reporting is aimed at beginners.
+  * Handles i386, alpha, sparc, m68k.  It is a lot easier to extend to
+    other architectures (patches and/or sample data gratefully accepted).
+  * Handles all Oops in the input file(s).
+
+
+  Usage:	ksymoops
+		  [-v vmlinux]	Where to read vmlinux
+		  [-V]		No vmlinux is available
+		  [-o object_dir]	Directory containing modules
+		  [-O]		No modules is available
+		  [-k ksyms]	Where to read ksyms
+		  [-K]		No ksyms is available
+		  [-l lsmod]	Where to read lsmod
+		  [-L]		No lsmod is available
+		  [-m system.map]	Where to read System.map
+		  [-M]		No System.map is available
+		  [-s save.map]	Save consolidated map
+		  [-d]		Increase debug level by 1
+		  [-h]		Print help text
+		  Oops.file	Oops to decode
+
+	  All flags can occur more than once.  With the exception of -o
+	  and -d which are cumulative, the last occurrence of each flag is
+	  used.  Note that "-v my.vmlinux -V" will be taken as "No vmlinux
+	  available" but "-V -v my.vmlinux" will read my.vmlinux.  You
+	  will be warned about such combinations.
+
+	  Each occurrence of -d increases the debug level.
+
+	  Each -o flag can refer to a directory or to a single object
+	  file.  If a directory is specified then all *.o files in that
+	  directory and its subdirectories are assumed to be modules.
+
+	  If any of the vmlinux, object_dir, ksyms or system.map options
+	  contain the string *r (*m, *n, *s) then it is replaced at run time
+	  by the current value of `uname -r` (-m, -n, -s).
+
+	  The defaults can be changed in the Makefile, typical options are
+
+	  Defaults:	  -V
+			  -o /lib/modules/%r
+			  -k /proc/ksyms
+			  -l /proc/modules
+			  -m /usr/src/linux/System.map
+			  Oops report is read from stdin
+
+  Note:	  Unless you tell ksymoops *NOT* to read a particular file, it
+	  will try to read and reconcile almost all possible sources of kernel
+	  symbol information.  This is intended for beginners, they just
+	  type
+
+	    ksymoops < /var/log/syslog
+
+	  no thinking required.  Experts can point at different files or
+	  suppress the input from selected files.  For example, if you
+	  save /proc/ksyms before doing a test that creates an Oops, you
+	  can point ksymoops at the saved ksyms instead of using
+	  /proc/ksyms.
+
+	  vmlinux is not read by default, it only duplicates the
+	  information in System.map.  If you want to read vmlinux as well
+	  as or instead of System.map, use -v.
+
+	  To get the equivalent of the old ksymoops.cc (no vmlinux, no
+	  modules objects, no ksyms, no System.map) just do ksymoops
+	  -VOKLM.  Or to just read System.map, ksymoops -VOKL -m mapfile.
+
+
+  WARNING:  The user interface will change slightly in 0.7, users will
+            have to give some indication of the state of their
+            environment.  Otherwise it may be too easy to pick the
+            wrong input files.
+
+  Return codes:	0 - normal.
+		  1 - error(s) or warning(s) issued, results may not be
+		      reliable.
+		  2 - fatal error, no useful results.
+
+  Supported architectures
+
+	  i386 tested.
+          m68k code derived from ksymoops.cc and reading traps.c, untested.
+	  MIPS tested.
+	  Sparc tested.
+	  Alpha tested.
+
+	  The term "eip" is generic, for example it includes the i386 EIP
+	  and the m68k PC.  Remember that objdump output always says EIP,
+	  no matter what the architecture, see objfile_head.
+
+	  To support another arch, check the Oops_ procedures between
+	  'Start architecture sensitive code' and 'End architecture
+	  sensitive code'.
+
+	  The pattern matching should take care of different lengths for
+	  the address, i.e. addresses should not be arch sensitive.  I
+	  assume that all addresses are at least 4 characters.
+
+	  If nm output has a different format on your arch, check for uses
+	  of re_nm.
+
+
+
+  Because ksymoops reads kernel information from multiple sources, there
+  could be mismatches.  ksymoops does the following cross checks, but only
+  if the specified files exist -
+
+  * Compare Version_nnn numbers from all sources against each other.  Pity
+    that only vmlinux and System.map have these symbols (as at 2.1.125),
+    however I check ksyms, modules and Oops as well.  If somebody adds
+    symbol Version_nnn to ksyms or modules or adds a Version_nnn line to
+    the Oops log, this code is ready.
+
+  * Compare kernel ksyms against vmlinux.  vmlinux takes precedence.
+
+  * Compare System.map against vmlinux.   vmlinux takes precedence.
+
+  * Compare vmlinux against System.map.   vmlinux takes precedence.
+
+  * Compare kernel ksyms against System.map.  System.map takes precedence.
+
+  * Compare modules against module ksyms.  modules take precedence.  Only
+    if at least one module appears in ksyms.
+
+  * Compare module names in ksyms against lsmod.  Warn if a module
+    appears in lsmod but not in ksyms.  Error if a modules appears in
+    ksyms but is not in lsmod.  Only if both ksyms and lsmod have being
+    read.
+
+  The precedence order is somewhat arbitrary, however it only applies if
+  there is any difference between the various sources.
+
+  Handling modules is awkward.  They can be loaded under different names
+  (insmod -o dummy1 dummy.o) and the text, data and read only data are
+  loaded at different offsets.  Although you can give the -m option to
+  insmod which will output the module map when it is loaded, this has a
+  few problems -
+
+  * No equivalent for removing a module.  If you load and remove a lot of
+    modules, you end up with multiple sets of symbols around the same
+    offsets, which set is correct?
+
+  * "insmod -o dummy1 dummy.o" still reports as dummy.  That is, there is
+     no way of telling which particular version of a multiply loaded
+     module the insmod output refers to.  Therefore there is no way of
+     telling which instantiation failed.
+
+  * Even if the above problems are fixed, how do you tell what the module
+    environment looked like when the Oops occurred?  What if a module is
+    loaded or removed just after Oops, how is the user expected to edit
+    the insmod log?  Rule 1 - make ksymoops easy for beginners.
+
+  Although those problems could be fixed, they require changes to
+  modutils.  Working from ksyms and the module objects can be done without
+  changing modutils and without confusing beginners.
+  
+  Alas the ksyms plus object approach has another problem - matching ksyms
+  to module objects.  Nowhere does the kernel say that module dummy1 came
+  from module /lib/modules/2.1.215/net/dummy.o, ksyms just says dummy1.  I
+  have to match ksyms to the relevant object by finding a globally unique
+  external symbol in each module that can be used to map to the external
+  symbols in ksyms.  This assumes that each module exports at least one
+  text symbol that is unique amongst all modules.
+
+  It may not be possible to correctly map other sections such as data and
+  readonly data for modules because they may not have exported symbols.
+  Since the main aim of ksymoops is to map a code Oops, this should not be
+  a problem.
+
+  Unfortunately some modules export no symbols.  They are marked as
+  EXPORT_NO_SYMBOLS are simply do not export anything.  It is
+  impossible to detect these in ksyms because, by definition, ksyms
+  only contains exported symbols for modules.  Since all modules appear
+  in lsmod (/proc/modules), a cross check of lsmod against the module
+  names will find loaded modules with no symbols, at least I can warn
+  about these.
+
+  After merging the various sources, ksymoops has a (hopefully) accurate
+  map including modules.  The -s option lets you save the merged
+  System.map, but remember that module data and readonly data sections may
+  not be correctly relocated, see above.
+
+  Environment Variables.
+  KSYMOOPS_NM		path for nm, defaults to /usr/bin/nm.
+  KSYMOOPS_FIND		path for find, defaults to /usr/bin/find.
+  KSYMOOPS_OBJDUMP	path for objdump, defaults to /usr/bin/objdump.
+
+
+  Input Oops data.
+
+  The ideal input is to feed the syslog straight into this program.  If
+  you cannot do that, you need to know what the program looks for.
+  Especially if you are typing in the Oops by hand :(.  All input is case
+  insensitive.
+
+  * White space in this context means space or tab.  It does not include
+    newline.
+
+  * Oops in syslog has a syslog prefix.  Leading text up to and including
+    ' kernel: ' is always ignored, there is no need to edit syslog first.
+    This leading text need not exist but if it does, it must end in
+    ' kernel: '.
+
+  * An alternative prefix is <n> where n is the kernel print level.  Also
+    ignored if present.
+
+  * Leading white space is treated as a prefix and ignored, the input is
+    not indentation sensitive.
+
+  * In the following paragraphs, assume that any prefixes have been
+    skipped.  If there is more than one prefix, all are skipped, no matter
+    which order they appear in.
+
+  * A bracketed address is optional '[', required '<', at least 4 hex
+    digits, required '>', optional ']'.  For example [<01234567>] or
+    <1234>.
+
+  * The ix86 EIP line is identified by optional white space followed by
+    'EIP:', followed by a least one white space, followed by a bracketed
+    address.
+
+  * The m68k PC line is identified by optional white space followed by
+    'PC', optionally followed by white space, followed by '=', optionally
+    followed by white space, followed by a bracketed address.
+
+  * The sparc PC line starts with PSR and PC is the second hex value, not
+    bracketed.
+
+  * A call trace line is identified by 'Call Trace:' followed by at least
+    one white space.  Or it is a line starting with a bracketed address,
+    but only if the previous line was a call trace line (I hate multi line
+    output that relies on identation for recognition, especially when
+    lines can have a variable prefix).
+
+  * The Code line is identified by 'Code:' followed by a least one white
+    space character followed by at least one hex value.  The line can
+    contain multiple hex values, each separated by at least one white
+    space.  Each hex value must be 2 to 8 digits and must be a multiple of
+    2 digits.
+
+    Special cases where Code: can be followed by text.
+      'Code: general protection'
+      'Code: <n>'
+    Dump the data anyway, the code was unavailable.
+
+  * Formatted data is only output when the Code: line is seen.  If any
+    data has been stored and more than 5 lines other than Oops text (see
+    Oops_print) or end of file are encountered then ksymoops assumes that
+    the Code: line is missing or garbled and dumps the formatted data
+    anyway.  Fail safe, I hope.
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/io.c linux/scripts/ksymoops-0.6/io.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/io.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/io.c	Mon Nov  2 07:32:55 1998
@@ -0,0 +1,139 @@
+/*
+	io.c.
+
+	Local I/O routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	fwrite_local is redundant, replaced by bfd.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+int regular_file(const char *file, const char *msg)
+{
+	struct stat statbuf;
+	if (stat(file, &statbuf)) {
+		fprintf(stderr, "%s: %s stat %s failed",
+			prefix, msg, file);
+		perror(" ");
+		++errors;
+		return 0;
+	}
+
+	if (!S_ISREG(statbuf.st_mode)) {
+		fprintf(stderr,
+			"%s: %s %s is not a regular file, ignored\n",
+			prefix, msg, file);
+		++errors;
+		return 0;
+	}
+	return 1;
+}
+
+FILE *fopen_local(const char *file, const char *mode, const char *msg)
+{
+	FILE *f;
+	if (!(f = fopen(file, mode))) {
+		fprintf(stderr, "%s: %s fopen '%s' failed",
+			prefix, msg, file);
+		perror(" ");
+		++errors;
+	}
+	return f;
+}
+
+void fclose_local(FILE *f, const char *msg)
+{
+	int i;
+	if ((i = fclose(f))) {
+		fprintf(stderr, "%s: %s fclose failed %d", prefix, msg, i);
+		perror(" ");
+		++errors;
+	}
+}
+
+/* Read a line, increasing the size of the line as necessary until \n is read */
+#define INCREMENT 10	/* arbitrary */
+char *fgets_local(char **line, int *size, FILE *f, const char *msg)
+{
+	char *l, *p, *r;
+	int longline = 1;
+
+	if (!*line) {
+		*size = INCREMENT;
+		*line = malloc(*size);
+		if (!*line)
+			malloc_error("fgets_local alloc line");
+	}
+
+	l = *line;
+	while (longline) {
+		r = fgets(l, *size-(l-*line), f);
+		if (!r) {
+			if (ferror(f)) {
+				fprintf(stderr,
+					"%s: %s fgets failed", prefix, msg);
+				perror(" ");
+				++errors;
+			}
+			if (l != *line)
+				return(*line);
+			else
+				return(r);
+		}
+		if (!(p = strchr(*line, '\n'))) {
+			*size += INCREMENT;
+			*line = realloc(*line, *size);
+			if (!*line)
+				malloc_error("fgets_local realloc line");
+			l = *line+*size-INCREMENT-1;
+		}
+		else {
+			*p = '\0';
+			longline = 0;
+		}
+	}
+
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s line '%s'\n", msg, *line);
+	return(*line);
+}
+
+FILE *popen_local(const char *cmd, const char *msg)
+{
+	FILE *f;
+	if (!(f = popen(cmd, "r"))) {
+		fprintf(stderr, "%s: %s popen '%s' failed",
+			prefix, msg, cmd);
+		perror(" ");
+		++errors;
+	}
+	return f;
+}
+
+void pclose_local(FILE *f, const char *msg)
+{
+	int i;
+	errno = 0;
+	if ((i = pclose(f))) {
+		fprintf(stderr, "%s: %s pclose failed 0x%x", prefix, msg, i);
+		if (errno)
+			perror(" ");
+		else
+			fprintf(stderr, "\n");
+		++errors;
+	}
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksymoops.c linux/scripts/ksymoops-0.6/ksymoops.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksymoops.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/ksymoops.c	Mon Nov  2 07:46:24 1998
@@ -0,0 +1,569 @@
+/*
+	ksymoops.c.
+
+	Read a kernel Oops file and make the best stab at converting the code to
+	instructions and mapping stack values to kernel symbols.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+*/
+
+#define VERSION "0.6"
+
+/*
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Ignore addresses 0-4095 when mapping address to symbol.
+	Discard default objects if -o specified.
+	Oops file must be regular.
+	Add "invalid operand" to Oops_print.
+	Move "Using_Version" copy to map.c.
+	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+	Minor adjustment to re for ppc.
+	Minor adjustment to re for objdump lines with <_EIP+xxx>.
+	Convert from a.out to bfd, using same format as ksymoops.
+	Added MIPS.
+	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+	Wed Oct 28 23:14:55 EST 1998
+	Version 0.5
+	No longer read vmlinux by default, it only duplicates System.map.
+
+	Wed Oct 28 13:47:38 EST 1998
+	Version 0.4
+	Split into separate sources.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3c
+	Add alpha (arm) processing.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3b
+	Add sparc processing.
+	Handle kernel symbol versions.
+
+	Fri Oct 23 13:11:20 EST 1998
+	Version 0.3
+	Add -follow to find command for people who use symlinks to modules.
+	Add Version_ checking.
+
+	Thu Oct 22 22:28:30 EST 1998
+	Version 0.2.
+	Generalise text prefix handling.
+	Handle messages on Code: line.
+	Format addresses with leading zeroes.
+	Minor bug fixes.
+
+	Wed Oct 21 23:28:48 EST 1998
+	Version 0.1.  Rewrite from scratch in C.
+
+	CREDITS.
+	Oops disassembly based on ksymoops.cc,
+	  Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+	m68k code based on ksymoops.cc changes by
+	  Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+ */
+
+#include "ksymoops.h"
+#include <ctype.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+char *prefix;
+char *path_nm = "/usr/bin/nm";			/* env KSYMOOPS_NM */
+char *path_find = "/usr/bin/find";		/* env KSYMOOPS_FIND */
+char *path_objdump = "/usr/bin/objdump";	/* env KSYMOOPS_OBJDUMP */
+int debug = 0;
+int errors = 0;
+int warnings = 0;
+
+SYMBOL_SET  ss_vmlinux;
+SYMBOL_SET  ss_ksyms_base;
+SYMBOL_SET *ss_ksyms_module;
+int         ss_ksyms_modules;
+SYMBOL_SET  ss_lsmod;
+SYMBOL_SET *ss_object;
+int         ss_objects;
+SYMBOL_SET  ss_system_map;
+
+SYMBOL_SET  ss_merged;   /* merged map with info from all sources */
+SYMBOL_SET  ss_Version;  /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+regex_t     re_nm;
+regmatch_t *re_nm_pmatch;
+regex_t     re_bracketed_address;
+regmatch_t *re_bracketed_address_pmatch;
+regex_t     re_unbracketed_address;
+regmatch_t *re_unbracketed_address_pmatch;
+
+static void usage(void)
+{
+	fprintf(stderr, "Version " VERSION "\n");
+	fprintf(stderr, "usage: %s\n", prefix);
+	fprintf(stderr,
+		"\t\t[-v vmlinux]\tWhere to read vmlinux\n"
+		"\t\t[-V]\t\tNo vmlinux is available\n"
+		"\t\t[-o object_dir]\tDirectory containing modules\n"
+		"\t\t[-O]\t\tNo modules is available\n"
+		"\t\t[-k ksyms]\tWhere to read ksyms\n"
+		"\t\t[-K]\t\tNo ksyms is available\n"
+		"\t\t[-l lsmod]\tWhere to read lsmod\n"
+		"\t\t[-L]\t\tNo lsmod is available\n"
+		"\t\t[-m system.map]\tWhere to read System.map\n"
+		"\t\t[-M]\t\tNo System.map is available\n"
+		"\t\t[-s save.map]\tSave consolidated map\n"
+		"\t\t[-d]\t\tIncrease debug level by 1\n"
+		"\t\t[-h]\t\tPrint help text\n"
+		"\t\t<Oops.file\tOops report to decode\n"
+		"\n"
+		"\t\tAll flags can occur more than once.  With the exception "
+			"of -o\n"
+		"\t\tand -d which are cumulative, the last occurrence of each "
+			"flag is\n"
+		"\t\tused.  Note that \"-v my.vmlinux -V\" will be taken as "
+			"\"No vmlinux\n"
+		"\t\tavailable\" but \"-V -v my.vmlinux\" will read "
+			"my.vmlinux.  You\n"
+		"\t\twill be warned about such combinations.\n"
+		"\n"
+		"\t\tEach occurrence of -d increases the debug level.\n"
+		"\n"
+		"\t\tEach -o flag can refer to a directory or to a single "
+			"object\n"
+		"\t\tfile.  If a directory is specified then all *.o files in "
+			"that\n"
+		"\t\tdirectory and its subdirectories are assumed to be "
+			"modules.\n"
+		"\n"
+		"\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
+		"options\n"
+		"\t\tcontain the string *r (*m, *n, *s) then it is replaced "
+		"at run\n"
+		"\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
+		"\n"
+		"\t\tThe defaults can be changed in the Makefile, current "
+		"defaults\n"
+		"\t\tare\n\n"
+		"\t\t\t"
+#ifdef DEF_VMLINUX
+		"-v " DEF_LINUX
+#else
+		"-V"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_OBJECTS
+		"-o " DEF_OBJECTS
+#else
+		"-O"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_KSYMS
+		"-k " DEF_KSYMS
+#else
+		"-K"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_LSMOD
+		"-l " DEF_LSMOD
+#else
+		"-L"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_MAP
+		"-m " DEF_MAP
+#else
+		"-M"
+#endif
+		"\n"
+		"\t\t\tOops report is read from stdin\n"
+		"\n"
+	       );
+}
+
+/* Check if possibly conflicting options were specified */
+static void multi_opt(int specl, int specu, char type, const char *using)
+{
+	if (specl && specu) {
+		fprintf(stderr,
+			"Warning - you specified both -%c and -%c.  Using '",
+			type, toupper(type));
+		++warnings;
+		if (using) {
+			fprintf(stderr, "-%c %s", type, using);
+			if (type == 'o')
+				fprintf(stderr, " ...");
+			fprintf(stderr, "'\n");
+		}
+		else
+			fprintf(stderr, "-%c'\n", toupper(type));
+	}
+	else if (specl > 1 && type != 'o') {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Using '-%c %s'\n",
+			type, type, using);
+		++warnings;
+	}
+	else if (specu > 1) {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Second and subsequent '-%c' ignored\n",
+			toupper(type), toupper(type));
+		++warnings;
+	}
+}
+
+/* If a name contains *r (*m, *n, *s), replace with the current value of
+ * `uname -r` (-m, -n, -s).  Actually uses uname system call rather than the
+ * uname command but the result is the same.
+ */
+static void convert_uname(char **name)
+{
+	char *p, *newname, *oldname, *replacement;
+	unsigned len;
+	int free_oldname = 0;
+	static char procname[] = "convert_uname";
+
+	if (!*name)
+		return;
+
+	while ((p = strchr(*name, '*'))) {
+		struct utsname buf;
+		int i = uname(&buf);
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
+		if (i) {
+			fprintf(stderr,
+				"%s: uname failed, %s will not be processed\n",
+				prefix, *name);
+			perror(prefix);
+			++errors;
+			return;
+		}
+		switch (*(p+1)) {
+		case 'r':
+			replacement = buf.release;
+			break;
+		case 'm':
+			replacement = buf.machine;
+			break;
+		case 'n':
+			replacement = buf.nodename;
+			break;
+		case 's':
+			replacement = buf.sysname;
+			break;
+		default:
+			fprintf(stderr,
+				"%s: invalid replacement character '*%c' "
+				"in %s\n",
+				prefix, *(p+1), *name);
+			++errors;
+			return;
+		}
+		len = strlen(*name)-2+strlen(replacement)+1;
+		if (!(newname = malloc(len)))
+			malloc_error(procname);
+		strncpy(newname, *name, (p-*name));
+		strcpy(newname+(p-*name), replacement);
+		strcpy(newname+(p-*name)+strlen(replacement), p+2);
+		p = newname+(p-*name)+strlen(replacement);	/* no rescan */
+		oldname = *name;
+		*name = newname;
+		if (free_oldname)
+			free(oldname);
+		free_oldname = 1;
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
+	}
+	return;
+}
+
+/* Parse the options.  Verbose but what's new with getopt? */
+static void parse(int argc,
+		  char **argv,
+		  char **vmlinux,
+		  char ***object,
+		  int *objects,
+		  char **ksyms,
+		  char **lsmod,
+		  char **system_map,
+		  char **save_system_map,
+		  char ***filename,
+		  int *filecount,
+		  int *spec_h
+		 )
+{
+	int spec_v = 0, spec_V = 0;
+	int spec_o = 0, spec_O = 0;
+	int spec_k = 0, spec_K = 0;
+	int spec_l = 0, spec_L = 0;
+	int spec_m = 0, spec_M = 0;
+	int spec_s = 0;
+
+	int c, i;
+	char *p;
+
+	while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dh")) != EOF) {
+		if (debug && c != 'd')
+			fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
+		switch(c) {
+		case 'v':
+			*vmlinux = optarg;
+			++spec_v;
+			break;
+		case 'V':
+			*vmlinux = NULL;
+			++spec_V;
+			break;
+		case 'o':
+			if (!spec_o) {
+				/* First -o, discard default value(s) */
+				for (i = 0; i < *objects; ++i)
+					free((*object)[i]);
+				free(*object);
+				*object = NULL;
+				*objects = 0;
+			}
+			*object = realloc(*object,
+				((*objects)+1)*sizeof(**object));
+			if (!*object)
+				malloc_error("object");
+			if (!(p = strdup(optarg)))
+				malloc_error("strdup -o");
+			else {
+				(*object)[(*objects)++] = p;
+				++spec_o;
+			}
+			break;
+		case 'O':
+			++spec_O;
+			for (i = 0; i < *objects; ++i)
+				free((*object)[i]);
+			free(*object);
+			*object = NULL;
+			*objects = 0;
+			break;
+		case 'k':
+			*ksyms = optarg;
+			++spec_k;
+			break;
+		case 'K':
+			*ksyms = NULL;
+			++spec_K;
+			break;
+		case 'l':
+			*lsmod = optarg;
+			++spec_l;
+			break;
+		case 'L':
+			*lsmod = NULL;
+			++spec_L;
+			break;
+		case 'm':
+			*system_map = optarg;
+			++spec_m;
+			break;
+		case 'M':
+			*system_map = NULL;
+			++spec_M;
+			break;
+		case 's':
+			*save_system_map = optarg;
+			++spec_s;
+			break;
+		case 'd':
+			++debug;
+			break;
+		case 'h':
+			usage();
+			++*spec_h;
+			break;
+		case '?':
+			usage();
+			exit(2);
+		}
+	}
+
+	*filecount = argc - optind;
+	*filename = argv + optind;
+
+	/* Expand any requests for the current uname values */
+	convert_uname(vmlinux);
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			convert_uname(*object+i);
+	}
+	convert_uname(ksyms);
+	convert_uname(lsmod);
+	convert_uname(system_map);
+
+	/* Check for multiple options specified */
+	multi_opt(spec_v, spec_V, 'v', *vmlinux);
+	multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
+	multi_opt(spec_k, spec_K, 'k', *ksyms);
+	multi_opt(spec_l, spec_L, 'l', *lsmod);
+	multi_opt(spec_m, spec_M, 'm', *system_map);
+
+	printf("Options used:");
+	if (*vmlinux)
+		printf(" -v %s", *vmlinux);
+	else
+		printf(" -V");
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			printf(" -o %s", (*object)[i]);
+	}
+	else
+		printf(" -O");
+	if (*ksyms)
+		printf(" -k %s", *ksyms);
+	else
+		printf(" -K");
+	if (*lsmod)
+		printf(" -l %s", *lsmod);
+	else
+		printf(" -L");
+	if (*system_map)
+		printf(" -m %s", *system_map);
+	else
+		printf(" -M");
+	printf("\n\n");
+}
+
+/* Read environment variables */
+static void read_env(const char *external, char **internal)
+{
+	char *p;
+	if ((p = getenv(external))) {
+		*internal = p;
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env override %s=%s\n",
+				external, *internal);
+	}
+	else {
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env default %s=%s\n",
+				external, *internal);
+	}
+}
+
+
+int main(int argc, char **argv)
+{
+	char *vmlinux = NULL;
+	char **object = NULL;
+	int objects = 0;
+	char *ksyms = NULL;
+	char *lsmod = NULL;
+	char *system_map = NULL;
+	char *save_system_map = NULL;
+	char **filename;
+	int filecount = 0;
+	int spec_h = 0;		/* -h was specified */
+	int i;
+
+	prefix = *argv;
+	setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifdef DEF_VMLINUX
+	vmlinux = DEF_LINUX;
+#endif
+#ifdef DEF_OBJECTS
+	{
+		char *p;
+		object = realloc(object, (objects+1)*sizeof(*object));
+		if (!object)
+			malloc_error("DEF_OBJECTS");
+		if (!(p = strdup(DEF_OBJECTS)))
+			malloc_error("DEF_OBJECTS");
+		else
+			object[objects++] = p;
+	}
+#endif
+#ifdef DEF_KSYMS
+	ksyms = DEF_KSYMS;
+#endif
+#ifdef DEF_LSMOD
+	lsmod = DEF_LSMOD;
+#endif
+#ifdef DEF_MAP
+	system_map = DEF_MAP;
+#endif
+
+	parse(argc,
+	      argv,
+	      &vmlinux,
+	      &object,
+	      &objects,
+	      &ksyms,
+	      &lsmod,
+	      &system_map,
+	      &save_system_map,
+	      &filename,
+	      &filecount,
+	      &spec_h
+	     );
+
+	if (spec_h && filecount == 0)
+		return(0);	/* just the help text */
+
+	if (debug)
+		fprintf(stderr, "DEBUG: level %d\n", debug);
+
+	read_env("KSYMOOPS_NM", &path_nm);
+	read_env("KSYMOOPS_FIND", &path_find);
+	read_env("KSYMOOPS_OBJDUMP", &path_objdump);
+
+	re_compile_common();
+	ss_init_common();
+
+	read_vmlinux(vmlinux);
+	read_ksyms(ksyms);
+	/* No point in reading modules unless ksyms shows modules loaded */
+	if (ss_ksyms_modules) {
+		expand_objects(object, objects);
+		for (i = 0; i < ss_objects; ++i)
+			read_object(ss_object[i].source, i);
+	}
+	else if (objects)
+		printf("No modules in ksyms, skipping objects\n");
+	/* No point in reading lsmod without ksyms */
+	if (ss_ksyms_modules || ss_ksyms_base.used)
+		read_lsmod(lsmod);
+	else if (lsmod)
+		printf("No ksyms, skipping lsmod\n");
+	read_system_map(system_map);
+	merge_maps(save_system_map);
+
+	/* After all that work, it is finally time to read the Oops report */
+	Oops_read(filecount, filename);
+
+	if (warnings || errors) {
+		printf("\n");
+		if (warnings)
+			printf("%d warning%s ",
+			       warnings, warnings == 1 ? "" : "s");
+		if (warnings && errors)
+			printf("and ");
+		if (errors)
+			printf("%d error%s ", errors, errors == 1 ? "" : "s");
+		printf("issued.  Results may not be reliable.\n");
+		return(1);
+	}
+
+	return(0);
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksymoops.h linux/scripts/ksymoops-0.6/ksymoops.h
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksymoops.h	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/ksymoops.h	Mon Nov  2 07:35:08 1998
@@ -0,0 +1,145 @@
+/*
+	ksymoops.h.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Convert from a.out to bfd, using same format as ksymoops.
+	PPC trace addresses are not bracketed, add new re.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+*/
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+
+
+/* Pity this is not externalised, see binfmt_elf.c */
+#define elf_addr_t unsigned long
+
+extern char *prefix;
+extern char *path_nm;		/* env KSYMOOPS_NM */
+extern char *path_find;		/* env KSYMOOPS_FIND */
+extern char *path_objdump;	/* env KSYMOOPS_OBJDUMP */
+extern int debug;
+extern int errors;
+extern int warnings;
+
+typedef struct symbol SYMBOL;
+
+struct symbol {
+	char *name;		/* name of symbol */
+	char type;		/* type of symbol from nm/System.map */
+	char keep;		/* keep this symbol in merged map? */
+	elf_addr_t address;	/* address in kernel */
+};
+
+/* Header for symbols from one particular source */
+
+typedef struct symbol_set SYMBOL_SET;
+
+struct symbol_set {
+	char *source;			/* where the symbols came from */
+	int used;			/* number of symbols used */
+	int alloc;			/* number of symbols allocated */
+	SYMBOL *symbol;			/* dynamic array of symbols */
+	SYMBOL_SET *related;		/* any related symbol set */
+};
+
+extern SYMBOL_SET  ss_vmlinux;
+extern SYMBOL_SET  ss_ksyms_base;
+extern SYMBOL_SET *ss_ksyms_module;
+extern int         ss_ksyms_modules;
+extern SYMBOL_SET  ss_lsmod;
+extern SYMBOL_SET *ss_object;
+extern int         ss_objects;
+extern SYMBOL_SET  ss_system_map;
+
+extern SYMBOL_SET  ss_merged;	/* merged map with info from all sources */
+extern SYMBOL_SET  ss_Version;	/* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+extern regex_t     re_nm;
+extern regmatch_t *re_nm_pmatch;
+extern regex_t     re_bracketed_address;
+extern regmatch_t *re_bracketed_address_pmatch;
+extern regex_t     re_unbracketed_address;
+extern regmatch_t *re_unbracketed_address_pmatch;
+
+/* Bracketed address: optional '[', required '<', at least 4 hex characters,
+ * required '>', optional ']', optional white space.
+ */
+#define BRACKETED_ADDRESS	"\\[*<([0-9a-fA-F]{4,})>\\]*[ \t]*"
+
+#define UNBRACKETED_ADDRESS	"([0-9a-fA-F]{4,})[ \t]*"
+
+/* io.c */
+extern int regular_file(const char *file, const char *msg);
+extern FILE *fopen_local(const char *file, const char *mode, const char *msg);
+extern void fclose_local(FILE *f, const char *msg);
+extern char *fgets_local(char **line, int *size, FILE *f, const char *msg);
+extern int fwrite_local(void const *ptr, size_t size, size_t nmemb,
+			FILE *stream, const char *msg);
+extern FILE *popen_local(const char *cmd, const char *msg);
+extern void pclose_local(FILE *f, const char *msg);
+
+/* ksyms.c */
+extern void read_ksyms(const char *ksyms);
+extern void map_ksyms_to_modules(void);
+extern void read_lsmod(const char *lsmod);
+extern void compare_ksyms_lsmod(void);
+
+/* misc.c */
+extern void malloc_error(const char *msg);
+extern const char *format_address(elf_addr_t address);
+extern char *find_fullpath(const char *program);
+
+/* map.c */
+extern void read_system_map(const char *system_map);
+extern void merge_maps(const char *save_system_map);
+extern void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+			 int precedence);
+
+
+/* object.c */
+extern SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss);
+extern void read_vmlinux(const char *vmlinux);
+extern void expand_objects(char * const *object, int objects);
+extern void read_object(const char *object, int i);
+
+/* oops.c */
+extern void Oops_read(int filecount, char * const *filename);
+
+/* re.c */
+extern void re_compile(regex_t *preg, const char *regex, int cflags,
+		       regmatch_t **pmatch);
+extern void re_compile_common(void);
+extern void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+		       char ***string);
+extern void re_strings_free(const regex_t *preg, char ***string);
+extern void re_string_check(int need, int available, const char *msg);
+
+/* symbol.c */
+extern void ss_init(SYMBOL_SET *ss, const char *msg);
+extern void ss_free(SYMBOL_SET *ss);
+extern void ss_init_common(void);
+extern SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol,
+				int *start);
+extern void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+			 const char type, const char keep, const char *symbol);
+extern void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+		       const char keep, const char *symbol);
+extern char *map_address(const SYMBOL_SET *ss, const elf_addr_t address);
+extern void ss_sort_atn(SYMBOL_SET *ss);
+extern void ss_sort_na(SYMBOL_SET *ss);
+extern SYMBOL_SET *ss_copy(const SYMBOL_SET *ss);
+extern void add_Version(const char *version, const char *source);
+extern void extract_Version(SYMBOL_SET *ss);
+extern void compare_Version(void);
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksyms.c linux/scripts/ksymoops-0.6/ksyms.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/ksyms.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/ksyms.c	Mon Nov  2 07:35:34 1998
@@ -0,0 +1,287 @@
+/*
+	ksyms.c.
+
+	Process ksyms for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Move "Using_Version" copy to map.c.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Scan one line from ksyms.  Split lines into the base symbols and the module
+ * symbols.  Separate ss for base and each module.
+ */
+static void scan_ksyms_line(const char *line)
+{
+	int i;
+	char **string = NULL;
+	SYMBOL_SET *ssp;
+	static char *prev_module = NULL;
+	static regex_t     re_ksyms;
+	static regmatch_t *re_ksyms_pmatch;
+	static char const procname[] = "scan_ksyms_line";
+
+	/* ksyms: address, symbol, optional module */
+	re_compile(&re_ksyms,
+		"^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_ksyms_pmatch);
+
+	i = regexec(&re_ksyms, line,
+		    re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i)
+		return;
+
+	/* string [1] - address, [2] - symbol, [3] - white space+module,
+	 * [4] - module.
+	 */
+	re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
+	if (string[4]) {
+		if (!prev_module || strcmp(prev_module, string[4])) {
+			/* start of a new module in ksyms */
+			++ss_ksyms_modules;
+			ss_ksyms_module = realloc(ss_ksyms_module,
+				ss_ksyms_modules*sizeof(*ss_ksyms_module));
+			if (!ss_ksyms_module)
+				malloc_error("realloc ss_ksyms_module");
+			ssp = ss_ksyms_module+ss_ksyms_modules-1;
+			ss_init(ssp, string[4]);
+			prev_module = strdup(string[4]);
+			if (!prev_module)
+				malloc_error("strdup prev_module");
+		}
+		ssp = ss_ksyms_module+ss_ksyms_modules-1;
+	}
+	else
+		ssp = &ss_ksyms_base;
+	add_symbol(ssp, string[1], ' ', 1, string[2]);
+	re_strings_free(&re_ksyms, &string);
+}
+
+/* Read the symbols from ksyms.  */
+void read_ksyms(const char *ksyms)
+{
+	FILE *f;
+	char *line = NULL;
+	int i, size;
+	static char const procname[] = "read_ksyms";
+
+	if (!ksyms)
+		return;
+	ss_init(&ss_ksyms_base, "ksyms_base");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
+
+	if (!regular_file(ksyms, procname))
+		return;
+
+	if (!(f = fopen_local(ksyms, "r", procname)))
+		return;
+
+	while (fgets_local(&line, &size, f, procname))
+		scan_ksyms_line(line);
+
+	fclose_local(f, procname);
+	free(line);
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss_sort_na(ss_ksyms_module+i);
+		extract_Version(ss_ksyms_module+i);
+	}
+	if (ss_ksyms_base.used) {
+		ss_sort_na(&ss_ksyms_base);
+		extract_Version(&ss_ksyms_base);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in ksyms, is %s a valid "
+			"ksyms file?\n",
+			ksyms);
+		++warnings;
+	}
+
+	if (debug > 1) {
+		for (i = 0; i < ss_ksyms_modules; ++i) {
+			fprintf(stderr,
+				"DEBUG: %s %s used %d out of %d entries\n",
+				procname,
+				ss_ksyms_module[i].source,
+				ss_ksyms_module[i].used,
+				ss_ksyms_module[i].alloc);
+		}
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss_ksyms_base.source, ss_ksyms_base.used,
+			ss_ksyms_base.alloc);
+	}
+}
+
+/* Map each ksyms module entry to the corresponding object entry.  Tricky,
+ * see the comments in the docs about needing a unique symbol in each
+ * module.
+ */
+static void map_ksym_to_module(SYMBOL_SET *ss)
+{
+	int i, j, matches;
+	char *name = NULL;
+
+	for (i = 0; i < ss->used; ++i) {
+		matches = 0;
+		for (j = 0; j < ss_objects; ++j) {
+			name = (ss->symbol)[i].name;
+			if (find_symbol_name(ss_object+j, name, NULL)) {
+				++matches;
+				ss->related = ss_object+j;
+			}
+		}
+		if (matches == 1)
+			break;		/* unique symbol over all objects */
+		ss->related = NULL;	/* keep looking */
+	}
+	if (!(ss->related)) {
+		fprintf(stderr,
+			"Warning: cannot match loaded module %s to any "
+			"module object.  Trace may not be reliable.\n",
+			ss->source);
+		++warnings;
+	}
+	else if (debug)
+		fprintf(stderr,
+			"DEBUG: ksyms %s matches to %s based on unique "
+			"symbol %s\n",
+			ss->source, ss->related->source, name);
+}
+
+/* Map all ksyms module entries to their corresponding objects */
+void map_ksyms_to_modules(void)
+{
+	int i;
+	SYMBOL_SET *ss, *ssc;
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss = ss_ksyms_module+i;
+		map_ksym_to_module(ss);
+		if (ss->related) {
+			ssc = adjust_object_offsets(ss);
+			compare_maps(ss, ssc, 1);
+		}
+	}
+}
+
+/* Read the modules from lsmod.  */
+void read_lsmod(const char *lsmod)
+{
+	FILE *f;
+	char *line = NULL;
+	int i, size;
+	char **string = NULL;
+	static regex_t     re_lsmod;
+	static regmatch_t *re_lsmod_pmatch;
+	static char const procname[] = "read_lsmod";
+
+	if (!lsmod)
+		return;
+	ss_init(&ss_lsmod, "lsmod");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
+
+	if (!regular_file(lsmod, procname))
+		return;
+
+	if (!(f = fopen_local(lsmod, "r", procname)))
+		return;
+
+	/* lsmod: module, size, use count, optional used by */
+	re_compile(&re_lsmod,
+		"^"
+		"[ \t]*([^ \t]+)"				/* 1 module */
+		"[ \t]*([^ \t]+)"				/* 2 size */
+		"[ \t]*([^ \t]+)"				/* 3 count */
+		"[ \t]*(.*)"					/* 4 used by */
+		"$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_lsmod_pmatch);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_lsmod, line,
+			    re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i)
+			continue;
+		re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
+		add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
+	}
+
+	fclose_local(f, procname);
+	free(line);
+	re_strings_free(&re_lsmod, &string);
+	if (ss_lsmod.used)
+		ss_sort_na(&ss_lsmod);
+	else {
+		fprintf(stderr,
+			"Warning, no symbols in lsmod, is %s a valid "
+			"lsmod file?\n",
+			lsmod);
+		++warnings;
+	}
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss_lsmod.source, ss_lsmod.used,
+			ss_lsmod.alloc);
+}
+
+/* Compare modules from ksyms against module list in lsmod and vice versa.
+ * There is one ss_ for each ksyms module and a single ss_lsmod to cross
+ * check.
+ */
+void compare_ksyms_lsmod(void)
+{
+	int i, j;
+	SYMBOL_SET *ss;
+	SYMBOL *s;
+	static char const procname[] = "compare_ksyms_lsmod";
+
+	s = ss_lsmod.symbol;
+	for (i = 0; i < ss_lsmod.used; ++i, ++s) {
+		for (j = 0; j < ss_ksyms_modules; ++j) {
+			ss = ss_ksyms_module+j;
+			if (strcmp(s->name, ss->source) == 0)
+				break;
+		}
+		if (j >= ss_ksyms_modules) {
+			fprintf(stderr,
+				"Warning in %s, module %s is in lsmod but not "
+				"in ksyms, probably no symbols exported\n",
+				procname, s->name);
+			++warnings;
+		}
+	}
+
+	for (i = 0; i < ss_ksyms_modules; ++i) {
+		ss = ss_ksyms_module+i;
+		if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
+			fprintf(stderr,
+				"Error in %s, module %s is in ksyms but not "
+				"in lsmod\n",
+				procname, ss->source);
+			++errors;
+		}
+	}
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/map.c linux/scripts/ksymoops-0.6/map.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/map.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/map.c	Mon Nov  2 07:35:50 1998
@@ -0,0 +1,251 @@
+/*
+	map.c.
+
+	Read System.map for ksymoops, create merged System.map.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Remove addresses 0-4095 from merged map after writing new map.
+	Move "Using_Version" copy to map.c.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+
+/* Read the symbols from System.map */
+void read_system_map(const char *system_map)
+{
+	FILE *f;
+	char *line = NULL, **string = NULL;
+	int i, size = 0;
+	static char const procname[] = "read_system_map";
+
+	if (!system_map)
+		return;
+	ss_init(&ss_system_map, "System.map");
+	if (debug)
+		fprintf(stderr, "DEBUG: %s %s\n", procname, system_map);
+
+	if (!regular_file(system_map, procname))
+		return;
+
+	if (!(f = fopen_local(system_map, "r", procname)))
+		return;
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i == 0) {
+			re_strings(&re_nm, line, re_nm_pmatch, &string);
+			add_symbol(&ss_system_map, string[1], *string[2],
+				   1, string[3]);
+		}
+	}
+
+	fclose_local(f, procname);
+	re_strings_free(&re_nm, &string);
+	free(line);
+	if (ss_system_map.used) {
+		ss_sort_na(&ss_system_map);
+		extract_Version(&ss_system_map);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in System.map, is %s a "
+			"valid System.map file?\n",
+			system_map);
+		++warnings;
+	}
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname,
+			ss_system_map.source,
+			ss_system_map.used,
+			ss_system_map.alloc);
+}
+
+/* Compare two maps, all symbols in the first should appear in the second. */
+void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+			 int precedence)
+{
+	int i, start = 0;
+	SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1;
+	const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1;
+
+	if (!(ss1->used && ss2->used))
+		return;
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: compare_maps %s vs %s, %s takes precedence\n",
+			ss1->source, ss2->source,
+			precedence == 1 ? ss1->source : ss2->source);
+
+	for (i = 0; i < ss1->used; ++i) {
+		s1 = ss1->symbol+i;
+		if (!(s1->keep))
+			continue;
+		s2 = find_symbol_name(ss2, s1->name, &start);
+		if (!s2) {
+			/* Some types only appear in nm output, not in things
+			 * like System.map.  Silently ignore them.
+			 */
+			if (s1->type == 'a' || s1->type == 't')
+				continue;
+			fprintf(stderr,
+				"Warning: %s symbol %s not found in %s.  "
+				"Ignoring %s entry\n",
+				ss1->source, s1->name,
+				ss2->source, (*ssdrop)->source);
+			++warnings;
+			if (*sdrop)
+				(*sdrop)->keep = 0;
+		}
+		else if (s1->address != s2->address) {
+			/* Type C symbols cannot be resolved from nm to ksyms,
+			 * silently ignore them.
+			 */
+			if (s1->type == 'C' || s2->type == 'C')
+				continue;
+			fprintf(stderr,
+				"Warning: mismatch on symbol %s %c, "
+				"%s says %lx, %s says %lx.  "
+				"Ignoring %s entry\n",
+				s1->name, s1->type, ss1->source, s1->address,
+				ss2->source, s2->address, (*ssdrop)->source);
+			++warnings;
+			if (*sdrop)
+				(*sdrop)->keep = 0;
+		}
+		else
+			++start;	/* step to next entry in ss2 */
+	}
+}
+
+/* Append the second symbol set onto the first */
+static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2)
+{
+	int i;
+	SYMBOL *s;
+
+	if (!ss2 || !ss2->used)
+		return;
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: append_map %s to %s\n",
+			ss2->source, ss1->source);
+
+	for (i = 0; i < ss2->used; ++i) {
+		s = ss2->symbol+i;
+		if (s->keep)
+			add_symbol_n(ss1, s->address, s->type, 1,
+				s->name);
+	}
+}
+
+/* Compare the various sources and build a merged system map */
+void merge_maps(const char *save_system_map)
+{
+	int i;
+	SYMBOL *s;
+	FILE *f;
+	static char const procname[] = "merge_maps";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+
+	/* Using_Versions only appears in ksyms, copy to other tables */
+	if ((s = find_symbol_name(&ss_ksyms_base,
+			"Using_Versions", 0))) {
+		if (ss_system_map.used) {
+			add_symbol_n(&ss_system_map, s->address,
+				s->type, s->keep, s->name);
+			ss_sort_na(&ss_system_map);
+		}
+		if (ss_vmlinux.used) {
+			add_symbol_n(&ss_vmlinux, s->address, s->type,
+				s->keep, s->name);
+			ss_sort_na(&ss_vmlinux);
+		}
+	}
+
+	compare_Version();	/* highlight any version problems first */
+	compare_ksyms_lsmod();	/* highlight any missing modules next */
+	compare_maps(&ss_ksyms_base, &ss_vmlinux, 2);
+	compare_maps(&ss_system_map, &ss_vmlinux, 2);
+	compare_maps(&ss_vmlinux, &ss_system_map, 1);
+	compare_maps(&ss_ksyms_base, &ss_system_map, 2);
+
+	if (ss_objects) {
+		map_ksyms_to_modules();
+	}
+
+	ss_init(&ss_merged, "merged");
+	append_map(&ss_merged, &ss_vmlinux);
+	append_map(&ss_merged, &ss_ksyms_base);
+	append_map(&ss_merged, &ss_system_map);
+	for (i = 0; i < ss_ksyms_modules; ++i)
+		append_map(&ss_merged, (ss_ksyms_module+i)->related);
+	if (!ss_merged.used) {
+		fprintf(stderr, "Warning, no symbols in merged map\n");
+		++warnings;
+	}
+
+	/* drop duplicates, type a (registers) and gcc2_compiled. */
+	ss_sort_atn(&ss_merged);
+	s = ss_merged.symbol;
+	for (i = 0; i < ss_merged.used-1; ++i) {
+		if (s->type == 'a' ||
+		    (s->type == 't' && !strcmp(s->name, "gcc2_compiled.")))
+			s->keep = 0;
+		else if (strcmp(s->name, (s+1)->name) == 0 &&
+		    s->address == (s+1)->address) {
+			if (s->type != ' ')
+				(s+1)->keep = 0;
+			else
+				s->keep = 0;
+		}
+		++s;
+	}
+	ss_sort_atn(&ss_merged);	/* will remove dropped variables */
+
+	if (save_system_map) {
+		if (debug)
+			fprintf(stderr, "DEBUG: writing merged map to %s\n",
+				save_system_map);
+		if (!(f = fopen_local(save_system_map, "w", procname)))
+			return;
+		s = ss_merged.symbol;
+		for (i = 0; i < ss_merged.used; ++i) {
+			if (s->keep)
+				fprintf(f, "%s %c %s\n",
+					format_address(s->address),
+					s->type, s->name);
+			++s;
+		}
+	}
+
+	/* The merged map may contain symbols with an address of 0, e.g.
+	 * Using_Versions.  These give incorrect results for low addresses in
+	 * map_address, such addresses map to "Using_Versions+xxx".  Remove
+	 * any addresses below (arbitrary) 4096 from the merged map.  AFAIK,
+	 * Linux does not use the first page on any arch.
+	 */
+	for (i = 0; i < ss_merged.used; ++i) {
+		if ((ss_merged.symbol+i)->address < 4096)
+			(ss_merged.symbol+i)->keep = 0;
+		else
+			break;
+	}
+	if (i)
+		ss_sort_atn(&ss_merged);	/* remove dropped variables */
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/misc.c linux/scripts/ksymoops-0.6/misc.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/misc.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/misc.c	Mon Nov  2 07:36:00 1998
@@ -0,0 +1,108 @@
+/*
+	misc.c.
+
+	Miscellaneous routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Convert from a.out to bfd, using same format as ksymoops.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void malloc_error(const char *msg)
+{
+	fprintf(stderr, "%s: fatal malloc error for %s\n", prefix, msg);
+	exit(2);
+}
+
+/* Format an address with the correct number of leading zeroes */
+const char *format_address(elf_addr_t address)
+{
+	/* Well oversized */
+	static char format[10], text[200];
+	if (!*format)
+		snprintf(format, sizeof(format), "%%0%dlx",
+			2*sizeof(address));
+	snprintf(text, sizeof(text), format, address);
+	return(text);
+}
+
+/* Find the full pathname of a program.  Code heavily based on
+ * glibc-2.0.5/posix/execvp.c.
+ */
+char *find_fullpath(const char *program)
+{
+	char *fullpath = NULL;
+	char *path, *p;
+	size_t len;
+	static const char procname[] = "find_fullpath";
+
+	/* Don't search when it contains a slash.  */
+	if (strchr(program, '/')) {
+		if (!(fullpath = strdup(program)))
+			malloc_error(procname);
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s %s\n", procname, fullpath);
+		return(fullpath);
+	}
+
+	path = getenv ("PATH");
+	if (!path) {
+		/* There is no `PATH' in the environment.  The default search
+		   path is the current directory followed by the path `confstr'
+		   returns for `_CS_PATH'.
+		 */
+		len = confstr(_CS_PATH, (char *) NULL, 0);
+		if (!(path = malloc(1 + len)))
+			malloc_error(procname);
+		path[0] = ':';
+		confstr(_CS_PATH, path+1, len);
+	}
+
+	len = strlen(program) + 1;
+	if (!(fullpath = malloc(strlen(path) + len)))
+		malloc_error(procname);
+	p = path;
+	do {
+		path = p;
+		p = strchr(path, ':');
+		if (p == NULL)
+			p = strchr(path, '\0');
+
+		/* Two adjacent colons, or a colon at the beginning or the end
+		 * of `PATH' means to search the current directory.
+		 */
+		if (p == path)
+			memcpy(fullpath, program, len);
+		else {
+			/* Construct the pathname to try.  */
+			memcpy(fullpath, path, p - path);
+			fullpath[p - path] = '/';
+			memcpy(&fullpath[(p - path) + 1], program, len);
+		}
+
+		/* If we have execute access, assume this is the program. */
+		if (access(fullpath, X_OK) == 0) {
+			if (debug > 1)
+				fprintf(stderr, "DEBUG: %s %s\n",
+					procname, fullpath);
+			return(fullpath);
+		}
+	} while (*p++ != '\0');
+
+	fprintf(stderr, "Error: %s %s could not find executable %s\n",
+		prefix, procname, program);
+	++errors;
+	return(NULL);
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/object.c linux/scripts/ksymoops-0.6/object.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/object.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/object.c	Sat Oct 31 18:18:26 1998
@@ -0,0 +1,230 @@
+/*
+	object.c.
+
+	object handling routines for ksymoops.  Read modules, vmlinux, etc. 
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/* Extract all symbols definitions from an object using nm */
+static void read_nm_symbols(SYMBOL_SET *ss, const char *file)
+{
+	FILE *f;
+	char *cmd, *line = NULL, **string = NULL;
+	int i, size = 0;
+	static char const procname[] = "read_nm_symbols";
+
+	if (!regular_file(file, procname))
+		return;
+
+	cmd = malloc(strlen(path_nm)+strlen(file)+2);
+	if (!cmd)
+		malloc_error("nm command");
+	strcpy(cmd, path_nm);
+	strcat(cmd, " ");
+	strcat(cmd, file);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	if (!(f = popen_local(cmd, procname)))
+		return;
+	free(cmd);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+		if (debug > 3)
+			fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+		if (i == 0) {
+			re_strings(&re_nm, line, re_nm_pmatch, &string);
+			add_symbol(ss, string[1], *string[2], 1, string[3]);
+		}
+	}
+
+	pclose_local(f, procname);
+	re_strings_free(&re_nm, &string);
+	free(line);
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: %s %s used %d out of %d entries\n",
+			procname, ss->source, ss->used, ss->alloc);
+}
+
+/* Read the symbols from vmlinux */
+void read_vmlinux(const char *vmlinux)
+{
+	if (!vmlinux)
+		return;
+	ss_init(&ss_vmlinux, "vmlinux");
+	read_nm_symbols(&ss_vmlinux, vmlinux);
+	if (ss_vmlinux.used) {
+		ss_sort_na(&ss_vmlinux);
+		extract_Version(&ss_vmlinux);
+	}
+	else {
+		fprintf(stderr,
+			"Warning, no kernel symbols in vmlinux, is %s a valid "
+			"vmlinux file?\n",
+			vmlinux);
+		++warnings;
+	}
+}
+
+
+/* Read the symbols from one object (module) */
+void read_object(const char *object, int i)
+{
+	ss_init(ss_object+i, object);
+	read_nm_symbols(ss_object+i, object);
+	if ((ss_object+i)->used) {
+		ss_sort_na(ss_object+i);
+		extract_Version(ss_object+i);
+	}
+	else {
+		fprintf(stderr, "Warning, no symbols in %s\n", object);
+		++warnings;
+	}
+}
+
+/* Add a new entry to the list of objects */
+static void add_ss_object(const char *file)
+{
+	++ss_objects;
+	ss_object = realloc(ss_object, ss_objects*sizeof(*ss_object));
+	if (!ss_object)
+		malloc_error("realloc ss_object");
+	ss_init(ss_object+ss_objects-1, file);
+}
+
+/* Run a directory and its subdirectories, looking for *.o files */
+static void find_objects(const char *dir)
+{
+	FILE *f;
+	char *cmd, *line = NULL;
+	int size = 0, files = 0;
+	static char const procname[] = "find_objects";
+	static char const options[] = " -follow -name '*.o' -print";
+
+	cmd = malloc(strlen(path_find)+1+strlen(dir)+strlen(options)+1);
+	if (!cmd)
+		malloc_error("find command");
+	strcpy(cmd, path_find);
+	strcat(cmd, " ");
+	strcat(cmd, dir);
+	strcat(cmd, options);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	if (!(f = popen_local(cmd, procname)))
+		return;
+	free(cmd);
+
+	while (fgets_local(&line, &size, f, procname)) {
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+		add_ss_object(line);
+		++files;
+	}
+
+	pclose_local(f, procname);
+	if (!files) {
+		fprintf(stderr,
+			"Warning: no *.o files in %s.  "
+			"Is %s a valid module directory?\n",
+			dir, dir);
+		++warnings;
+	}
+}
+
+/* Take the user supplied list of objects which can include directories.
+ * Expand directories into any *.o files.  The results are stored in
+ * ss_object, leaving the user supplied options untouched.
+ */
+void expand_objects(char * const *object, int objects)
+{
+	struct stat statbuf;
+	int i;
+	const char *file;
+	static char const procname[] = "expand_objects";
+
+	for (i = 0; i < objects; ++i) {
+		file = object[i];
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s checking '%s' - ",
+				procname, file);
+		if (!stat(file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
+			if (debug > 1)
+				fprintf(stderr, "directory, expanding\n");
+			find_objects(file);
+		}
+		else {
+			if (debug > 1)
+				fprintf(stderr, "not directory\n");
+			add_ss_object(file);
+		}
+	}
+}
+
+/* Map a symbol type to a section code. 0 - text, 1 - data, 2 - read only data,
+ * 3 - C (cannot relocate), 4 - the rest.
+ */
+static int section(char type)
+{
+	switch (type) {
+	case 'T':
+	case 't':
+		return 0;
+	case 'D':
+	case 'd':
+		return 1;
+	case 'R':
+	case 'r':
+		return 2;
+	case 'C':
+		return 3;
+	default:
+		return 4;
+	}
+}
+
+/* Given ksyms module data which has a related object, create a copy of the
+ * object data, adjusting the offsets to match where the module was loaded.
+ */
+SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss)
+{
+	int i;
+	elf_addr_t adjust[] = {0, 0, 0, 0, 0};
+	SYMBOL *sk, *so;
+	SYMBOL_SET *ssc;
+
+	if (debug > 1)
+		fprintf(stderr,
+			"DEBUG: adjust_object_offsets %s\n", ss->source);
+
+	ssc = ss_copy(ss->related);
+
+	/* For common symbols, calculate the adjustment */
+	for (i = 0; i < ss->used; ++i) {
+		sk = ss->symbol+i;
+		if ((so = find_symbol_name(ssc, sk->name, NULL)))
+			adjust[section(so->type)] = sk->address - so->address;
+	}
+	for (i = 0; i < ssc->used; ++i) {
+		so = ssc->symbol+i;
+		/* Type C does not relocate well, silently ignore */
+		if (so->type != 'C' && adjust[section(so->type)])
+			so->address += adjust[section(so->type)];
+		else
+			so->keep = 0;  /* do not merge into final map */
+	}
+
+	ss->related = ssc;	/* map using adjusted copy */
+	return(ssc);
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/oops.c linux/scripts/ksymoops-0.6/oops.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/oops.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/oops.c	Mon Nov  2 07:38:07 1998
@@ -0,0 +1,1061 @@
+/*
+	oops.c.
+
+	Oops processing for ksymoop.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Oops file must be regular.
+	Add "invalid operand" to Oops_print.
+	Minor adjustment to re for ppc.
+	Minor adjustment to re for objdump lines with <_EIP+xxx>.
+	Convert from a.out to bfd, using same format as ksymoops.
+	Added MIPS.
+	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into seperate sources.
+ */
+
+#include "ksymoops.h"
+#include <bfd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Error detected by bfd */
+static void Oops_bfd_perror(const char *msg)
+{
+	fprintf(stderr, "Error ");
+	bfd_perror(msg);
+	++errors;
+}
+
+/* Safest way to get correct output bfd format is to copy ksymoops' format. */
+static int Oops_copy_bfd_format(bfd **ibfd, bfd **obfd, asection **isec,
+				const char *file)
+{
+	char *me, **matches, **match;
+
+	if (!(*obfd = bfd_openw(file, NULL))) {
+		Oops_bfd_perror(file);
+		return(0);
+	}
+
+	me = find_fullpath(prefix);
+	if (!me)
+		return(0);
+
+	if (!(*ibfd = bfd_openr(me, NULL))) {
+		Oops_bfd_perror(me);
+		return(0);
+	}
+	free(me);	/* Who is Tommy? */
+
+	if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) {
+		Oops_bfd_perror(me);
+		if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
+			fprintf(stderr, "Matching formats:");
+			match = matches;
+			while (*match)
+				fprintf(stderr, " %s", *match++);
+			fprintf(stderr, "\n");
+			free(matches);
+		}
+		return(0);
+	}
+
+	if (!(*isec = bfd_get_section_by_name(*ibfd, ".text"))) {
+		Oops_bfd_perror("get_section");
+		return(0);
+	}
+
+	bfd_set_format(*obfd, bfd_object);
+	bfd_set_arch_mach(*obfd, bfd_get_arch(*ibfd), bfd_get_mach(*ibfd));
+
+	if (!bfd_set_file_flags(*obfd, bfd_get_file_flags(*ibfd))) {
+		Oops_bfd_perror("set_file_flags");
+		return(0);
+	}
+
+	return(1);
+}
+
+/* Write the code values to a file using bfd. */
+static int Oops_write_bfd_data(bfd *ibfd, bfd *obfd, asection *isec,
+			       const char *code, int size)
+{
+	asection *osec;
+	asymbol *osym;
+
+	if (!bfd_set_start_address(obfd, 0)) {
+		Oops_bfd_perror("set_start_address");
+		return(0);
+	}
+	if (!(osec = bfd_make_section(obfd, ".text"))) {
+		Oops_bfd_perror("make_section");
+		return(0);
+	}
+	if (!bfd_set_section_flags(obfd, osec, 
+		bfd_get_section_flags(ibfd, isec))) {
+		Oops_bfd_perror("set_section_flags");
+		return(0);
+	}
+	if (!bfd_set_section_alignment(obfd, osec, 
+		bfd_get_section_alignment(ibfd, isec))) {
+		Oops_bfd_perror("set_section_alignment");
+		return(0);
+	}
+	osec->output_section = osec;
+	if (!(osym = bfd_make_empty_symbol(obfd))) {
+		Oops_bfd_perror("make_empty_symbol");
+		return(0);
+	}
+	osym->name = "_EIP";
+	osym->section = osec;
+	osym->flags = BSF_GLOBAL;
+	osym->value = 0;
+	if (!bfd_set_symtab(obfd, &osym, 1)) {
+		Oops_bfd_perror("set_symtab");
+		return(0);
+	}
+	if (!bfd_set_section_size(obfd, osec, size)) {
+		Oops_bfd_perror("set_section_size");
+		return(0);
+	}
+	if (!bfd_set_section_vma(obfd, osec, 0)) {
+		Oops_bfd_perror("set_section_vma");
+		return(0);
+	}
+	if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) {
+		Oops_bfd_perror("set_section_contents");
+		return(0);
+	}
+	if (!bfd_close(obfd)) {
+		Oops_bfd_perror("close(obfd)");
+		return(0);
+	}
+	if (!bfd_close(ibfd)) {
+		Oops_bfd_perror("close(ibfd)");
+		return(0);
+	}
+	return 1;
+}
+
+/* Write the Oops code to a temporary file with suitable header and trailer. */
+static char *Oops_code_to_file(const char *code, int size)
+{
+	char *file;
+	bfd *ibfd, *obfd;
+	asection *isec;
+
+	bfd_init();
+	file = tmpnam(NULL);
+	if (!Oops_copy_bfd_format(&ibfd, &obfd, &isec, file))
+		return(NULL);
+	if (!Oops_write_bfd_data(ibfd, obfd, isec, code, size))
+		return(NULL);
+	return(file);
+}
+
+/* Run objdump against the binary Oops code */
+static FILE *Oops_objdump(const char *file)
+{
+	char *cmd;
+	FILE *f;
+	static char const options[] = "-dhf ";
+	static char const procname[] = "Oops_objdump";
+
+	cmd = malloc(strlen(path_objdump)+1+strlen(options)+strlen(file)+1);
+	if (!cmd)
+		malloc_error(procname);
+	strcpy(cmd, path_objdump);
+	strcat(cmd, " ");
+	strcat(cmd, options);
+	strcat(cmd, file);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+	f = popen_local(cmd, procname);
+	free(cmd);
+	return(f);
+}
+
+/* Process one code line from objdump, ignore everything else */
+static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
+			    int adjust)
+{
+	int i;
+	elf_addr_t address, eip_relative;
+	char *line2, *map, **string = NULL;
+	static regex_t     re_Oops_objdump;
+	static regmatch_t *re_Oops_objdump_pmatch;
+	static char const procname[] = "Oops_decode_one";
+
+	/* objdump output.  Optional whitespace, hex digits, optional
+	 * ' <_EIP+offset>', ':'.  The '+offset' after _EIP is also optional.
+	 * Older binutils output 'xxxxxxxx <_EIP+offset>:', newer versions do
+	 * '00000000 <_EIP>:' first followed by '      xx:' lines.
+	 *
+	 * Just to complicate things even more, objdump recognises jmp, call,
+	 * etc., converts the code to something like this :-
+	 * "   f: e8 32 34 00 00  call   3446 <_EIP+0x3446>"
+	 * Recognise this and append the eip adjusted address, followed by the
+	 * map_address text for that address.
+	 *
+	 * With any luck, objdump will take care of all such references which
+	 * makes this routine architecture insensitive.  No need to test for
+	 * i386 jmp, call or m68k swl etc.
+	 */
+	re_compile(&re_Oops_objdump,
+			"^[ \t]*"
+			"([0-9a-fA-F]+)"				/* 1 */
+			"( <_EIP[^>]*>)?"				/* 2 */
+			":"
+			"("						/* 3 */
+			".* +<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$"	/* 4 */
+			")?"
+			".*"
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_objdump_pmatch);
+
+	i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1,
+		re_Oops_objdump_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i != 0)
+		return;
+
+	re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string);
+	errno = 0;
+	address = strtoul(string[1], NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in objdump line, "
+			"treated as zero - '%s'\n"
+			"  objdump line '%s'\n",
+			procname, string[1], line);
+		perror(" ");
+		++errors;
+		address = 0;
+	}
+	address += eip + adjust;
+	if (string[4]) {
+		/* EIP relative data to be adjusted */
+		errno = 0;
+		eip_relative = strtoul(string[4], NULL, 16);
+		if (errno) {
+			fprintf(stderr,
+				"%s Invalid hex value in objdump line, "
+				"treated as zero - '%s'\n"
+				"  objdump line '%s'\n",
+				procname, string[4], line);
+			perror(" ");
+			++errors;
+			eip_relative = 0;
+		}
+		eip_relative += eip + adjust;
+		map = map_address(&ss_merged, eip_relative);
+		/* new text is original line, eip_relative in hex, map text */
+		i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1;
+		line2 = malloc(i);
+		if (!line2)
+			malloc_error(procname);
+		snprintf(line2, i, "%s %s %s",
+			line, format_address(eip_relative), map);
+		add_symbol_n(ss, address, 'C', 1, line2);
+		free(line2);
+	}
+	else
+		add_symbol_n(ss, address, 'C', 1, line);	/* as is */
+	re_strings_free(&re_Oops_objdump, &string);
+}
+
+/* Maximum number of code bytes to process */
+#define CODE_SIZE 36	/* sparc and alpha dump 36 bytes */
+
+/******************************************************************************/
+/*                     Start architecture sensitive code                      */
+/******************************************************************************/
+
+/* Extract the hex values from the Code: line and convert to binary */
+static int Oops_code_values(const unsigned char* code_text, char *code,
+			    int *adjust, char ***string, int string_max)
+{
+	int byte = 0, l;
+	unsigned long c;
+	char *value;
+	const char *p;
+	static regex_t     re_Oops_code_value;
+	static regmatch_t *re_Oops_code_value_pmatch;
+	static const char procname[] = "Oops_code_values";
+
+	/* Given by re_Oops_code: code_text is a message (e.g. "general
+	 * protection") or one or more hex fields separated by space or tab.
+	 * Some architectures bracket the current instruction with '<' and '>'.
+	 * The first character is nonblank.
+	 */
+	if (!isxdigit(*code_text)) {
+		fprintf(stderr,
+			"Warning, Code looks like message, not hex digits.  "
+			"No disassembly attempted.\n");
+		++warnings;
+		return(0);
+	}
+	memset(code, '\0', CODE_SIZE);
+	p = code_text;
+	*adjust = 0;	/* EIP points to code byte 0 */
+
+	/* Code values.  Hex values separated by white space.  On sparc, the
+	 * current instruction is bracketed in '<' and '>'.
+	 */
+	re_compile(&re_Oops_code_value,
+			"^"
+			"(<?)"						/* 1 */
+			"([0-9a-fA-F]+)"				/* 2 */
+			">?"
+			"[ \t]*"
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_code_value_pmatch);
+
+	re_string_check(re_Oops_code_value.re_nsub+1, string_max, procname);
+	while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1,
+			re_Oops_code_value_pmatch, 0) == 0) {
+		re_strings(&re_Oops_code_value, p,
+			re_Oops_code_value_pmatch, string);
+		if (byte >= CODE_SIZE)
+			break;
+		errno = 0;
+		value = (*string)[2];
+		c = strtoul(value, NULL, 16);
+		if (errno) {
+			fprintf(stderr,
+				"%s Invalid hex value in code_value line, "
+				"treated as zero - '%s'\n"
+				"  code_value line '%s'\n",
+				procname, value, code_text);
+			perror(" ");
+			++errors;
+			c = 0;
+		}
+		if ((*string[1]) && *((*string)[1]))
+			*adjust = -byte;	/* this byte is EIP */
+		/* i386 - 2 byte code, m68k - 4 byte, sparc - 8 byte.
+		 * Consistent we're not!
+		 */
+		l = strlen(value);
+		if (l%2) {
+			fprintf(stderr,
+				"%s invalid value 0x%s in Code line, not a "
+				"multiple of 2 digits, value ignored\n",
+				procname, value);
+			++errors;
+		}
+		else while (l) {
+			if (byte >= CODE_SIZE) {
+				fprintf(stderr,
+					"%s Warning: extra values in Code "
+					"line, ignored - '%s'\n",
+					procname, value);
+				++warnings;
+				break;
+			}
+			l -= 2;
+			code[byte++] = (c >> l*4) & 0xff;
+			value += 2;
+		}
+		p += re_Oops_code_value_pmatch[0].rm_eo;
+	}
+
+	if (*p) {
+		fprintf(stderr,
+			"Warning garbage '%s' at end of code line ignored "
+			"by %s\n",
+			p, procname);
+		++warnings;
+	}
+	return(1);
+}
+
+/* Look for the EIP: line, returns start of the relevant hex value */
+static char *Oops_eip(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_eip_sparc;
+	static regmatch_t *re_Oops_eip_sparc_pmatch;
+	static regex_t     re_Oops_eip_ppc;
+	static regmatch_t *re_Oops_eip_ppc_pmatch;
+	static regex_t     re_Oops_eip_mips;
+	static regmatch_t *re_Oops_eip_mips_pmatch;
+	static regex_t     re_Oops_eip_other;
+	static regmatch_t *re_Oops_eip_other_pmatch;
+	static const char procname[] = "Oops_eip";
+
+	/* Oops 'EIP:' line for sparc, actually PSR followed by PC */
+	re_compile(&re_Oops_eip_sparc,
+			"^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_sparc_pmatch);
+
+	re_string_check(re_Oops_eip_sparc.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_eip_sparc, line, re_Oops_eip_sparc.re_nsub+1,
+		re_Oops_eip_sparc_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec sparc %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
+			string);
+		return((*string)[re_Oops_eip_sparc.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for PPC, all over the place */
+	re_compile(&re_Oops_eip_ppc,
+			"("
+			  "(kernel pc )"
+			  "|(trap at PC: )"
+			  "|(bad area pc )"
+			  "|(NIP: )"
+			")"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_ppc_pmatch);
+
+	re_string_check(re_Oops_eip_ppc.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_eip_ppc, line, re_Oops_eip_ppc.re_nsub+1,
+		re_Oops_eip_ppc_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec ppc %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
+			string);
+		return((*string)[re_Oops_eip_ppc.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for MIPS, epc, optional white space, ':',
+	 * optional white space, unbracketed address.
+	 */
+	re_compile(&re_Oops_eip_mips,
+			"^(epc[ \t]*:+[ \t]*)"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_mips_pmatch);
+
+	re_string_check(re_Oops_eip_mips.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_eip_mips, line, re_Oops_eip_mips.re_nsub+1,
+		re_Oops_eip_mips_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec mips %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
+			string);
+		return((*string)[re_Oops_eip_mips.re_nsub]);
+	}
+
+	/* Oops 'EIP:' line for other architectures */
+	re_compile(&re_Oops_eip_other,
+			"^("
+	/* i386 */	"(EIP:[ \t]+.*)"
+	/* m68k */	"|(PC[ \t]*=[ \t]*)"
+			")"
+			BRACKETED_ADDRESS
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_eip_other_pmatch);
+
+	re_string_check(re_Oops_eip_other.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_eip_other, line, re_Oops_eip_other.re_nsub+1,
+		re_Oops_eip_other_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec other %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
+			string);
+		return((*string)[re_Oops_eip_other.re_nsub]);
+	}
+	return(NULL);
+}
+
+/* Set the eip from the EIP line */
+static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
+{
+	static const char procname[] = "Oops_set_eip";
+	errno = 0;
+	*eip = strtoul(value, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in EIP line, ignored - '%s'\n",
+			procname, value);
+		perror(" ");
+		++errors;
+		*eip = 0;
+	}
+	add_symbol_n(ss, *eip, 'E', 1, ">>EIP:");
+}
+
+/* Look for the MIPS ra line, returns start of the relevant hex value */
+static char *Oops_ra(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_ra;
+	static regmatch_t *re_Oops_ra_pmatch;
+	static const char procname[] = "Oops_ra";
+
+	/* Oops 'ra:' line for MIPS, ra, optional white space, one or
+	 * more '=', optional white space, unbracketed address.
+	 */
+	re_compile(&re_Oops_ra,
+			"(ra[ \t]*=+[ \t]*)"
+			UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_ra_pmatch);
+
+	re_string_check(re_Oops_ra.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_ra, line, re_Oops_ra.re_nsub+1,
+		re_Oops_ra_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_ra, line, re_Oops_ra_pmatch,
+			string);
+		return((*string)[re_Oops_ra.re_nsub]);
+	}
+	return(NULL);
+}
+
+/* Set the MIPS ra from the ra line */
+static void Oops_set_ra(const char *value, SYMBOL_SET *ss)
+{
+	static const char procname[] = "Oops_set_ra";
+	elf_addr_t ra;
+	errno = 0;
+	ra = strtoul(value, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s Invalid hex value in ra line, ignored - '%s'\n",
+			procname, value);
+		perror(" ");
+		++errors;
+		ra = 0;
+	}
+	add_symbol_n(ss, ra, 'R', 1, ">>RA :");
+}
+
+/* Look for the Trace multilines :(.  Returns start of addresses. */
+static const char *Oops_trace(const char *line, char ***string, int string_max)
+{
+	int i;
+	const char *start = NULL;
+	static int trace_line = 0;
+	static regex_t     re_Oops_trace;
+	static regmatch_t *re_Oops_trace_pmatch;
+	static const char procname[] = "Oops_trace";
+
+	/* ppc is different, not a bracketed address, just an address */
+
+	/* Oops 'Trace' lines */
+	re_compile(&re_Oops_trace,
+			"^(Call Trace: )"			/*  1 */
+	/* alpha */	"|^(Trace: )"				/*  2 */
+	/* various */	"|(" BRACKETED_ADDRESS ")"	   	/* 3,4*/
+	/* ppc */	"|^(Call backtrace:)"			/*  5 */
+	/* ppc */	"|^(" UNBRACKETED_ADDRESS ")"		/* 6,7*/
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_trace_pmatch);
+
+	re_string_check(re_Oops_trace.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_trace, line, re_Oops_trace.re_nsub+1,
+		re_Oops_trace_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_trace, line, re_Oops_trace_pmatch,
+			string);
+		if ((*string)[1] || (*string)[2]) {
+			trace_line = 1;
+			start = line + re_Oops_trace_pmatch[0].rm_eo;
+		}
+		else if ((*string)[5]) {
+			trace_line = 2;		/* ppc */
+			start = line + re_Oops_trace_pmatch[0].rm_eo;
+		}
+		else if (trace_line == 1 && (*string)[3])
+			start = line + re_Oops_trace_pmatch[3].rm_so;
+		else if (trace_line == 2 && (*string)[6])	/* ppc */
+			start = line + re_Oops_trace_pmatch[6].rm_so;
+		else
+			trace_line = 0;
+	}
+	else
+		trace_line = 0;
+	if (trace_line)
+		return(start);
+	return(NULL);
+}
+
+/* Process a trace call line, extract addresses */
+static void Oops_trace_line(const char *line, const char *p, SYMBOL_SET *ss)
+{
+	char **string = NULL;
+	regex_t *pregex;
+	regmatch_t *pregmatch;
+	static const char procname[] = "Oops_trace_line";
+
+	/* ppc does not bracket its addresses */
+	if (isxdigit(*p)) {
+		pregex = &re_unbracketed_address;
+		pregmatch = re_unbracketed_address_pmatch;
+	}
+	else {
+		pregex = &re_bracketed_address;
+		pregmatch = re_bracketed_address_pmatch;
+	}
+
+	/* Loop over [un]?bracketed addresses */
+	while (regexec(pregex, p, pregex->re_nsub+1, pregmatch, 0) == 0) {
+		re_strings(pregex, p, pregmatch, &string);
+		add_symbol(ss, string[1], 'T', 1, "Trace:");
+		p += pregmatch[0].rm_eo;
+	}
+
+	if (*p && !strcmp(p, "...")) {
+		fprintf(stderr,
+			"Warning garbage '%s' at end of trace line ignored "
+			"by %s\n",
+			p, procname);
+		++warnings;
+	}
+	re_strings_free(pregex, &string);
+}
+
+/* Do pattern matching to decide if the line should be printed.  When reading a
+ * syslog containing multiple Oops, you need the intermediate data (registers,
+ * tss etc.) to go with the decoded text.  Sets text to the start of the useful
+ * text, after any prefix.  Note that any leading white space is treated as part
+ * of the prefix, later routines do not see any indentation.
+ */
+static int Oops_print(const char *line, const char **text, char ***string,
+		      int string_max)
+{
+	int i, print;
+	static int stack_line = 0, trace_line = 0;
+	static regex_t     re_Oops_prefix;
+	static regmatch_t *re_Oops_prefix_pmatch;
+	static regex_t     re_Oops_print;
+	static regmatch_t *re_Oops_print_pmatch;
+	static const char procname[] = "Oops_print";
+
+	*text = line;
+
+	/* Lines to be ignored.  For some reason the "amuse the user" print in
+	 * some die_if_kernel routines causes regexec to run very slowly.
+	 */
+
+	if (strstr(*text, "\\|/ ____ \\|/")  ||
+	    strstr(*text, "\"@'/ ,. \\`@\"") ||
+	    strstr(*text, "/_| \\__/ |_\\")  ||
+	    strstr(*text, "   \\__U_/"))
+		return(1);	/* print but avoid regexec */
+
+	/* Prefixes to be ignored */
+	re_compile(&re_Oops_prefix,
+			"^("			/* start of line */
+			"([^ ]{3} [ 0-9][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} "
+			  "[^ ]+ kernel: +)"	/* syslogd */
+			"|(<[0-9]+>)"		/* kmsg */
+			"|([ \t]+)"		/* leading white space */
+			")+"			/* any prefixes, in any order */
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_prefix_pmatch);
+
+	re_string_check(re_Oops_prefix.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_prefix, *text, re_Oops_prefix.re_nsub+1,
+		re_Oops_prefix_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec prefix %d\n", procname, i);
+	if (i == 0)
+		*text += re_Oops_prefix_pmatch[0].rm_eo;  /* step over prefix */
+
+
+	/* Lots of possibilities.  Expand as required for all architectures.
+	 *
+	 * The order below is required to handle multiline outupt.
+	 * string[2] is defined if the text is 'Stack from '.
+	 * string[3] is defined if the text is 'Stack: '.
+	 * string[4] is defined if the text might be a stack continuation.
+	 * string[5] is defined if the text is 'Call Trace: '.
+	 * string[6] is defined if the text might be a trace continuation.
+	 * string[7] is the address part of the BRACKETED_ADDRESS.
+	 *
+	 * string[8] is defined if the text contains a version number.  No Oops
+	 * report contains this as of 2.1.125 but IMHO it should be added.  If
+	 * anybody wants to print a VERSION_nnnn line in their Oops, this code
+	 * is ready.
+	 *
+	 * string[9] is defined if the text is 'Trace: ' (alpha).
+	 * string[10] is defined if the text is 'Call backtrace:' (ppc).
+	 */
+	re_compile(&re_Oops_print,
+       /* arch type */					    /* Required order */
+			"("						/*  1 */
+	/* i386 */	"^(Stack: )"					/*  2 */
+	/* m68k */	"|^(Stack from )"				/*  3 */
+	/* various */	"|^([0-9a-fA-F]{4,})"				/*  4 */
+	/* various */	"|^(Call Trace: )"				/*  5 */
+	/* various */	"|^(" BRACKETED_ADDRESS ")"			/* 6,7*/
+	/* various */	"|^(Version_[0-9]+)"				/*  8 */
+	/* alpha */	"|^(Trace: )"					/*  9 */
+	/* ppc */	"|^(Call backtrace:)"				/* 10 */
+
+			/* order does not matter from here on */
+	
+	/* various */	"|(Unable to handle kernel)"
+	/* various */	"|^(Process .*stackpage=)"
+	/* various */	"|^(Call Trace:[ \t])"
+	/* various */	"|^(Code *:[ \t])"
+	/* various */	"|^(Kernel panic)"
+	/* various */	"|^(In swapper task)"
+	/* various */	"|(Aiee)"      /* anywhere in text is a bad sign (TM) */
+	/* various */	"|(die_if_kernel)"	/* ditto */
+
+	/* i386 2.0 */	"|^(Corrupted stack page)"
+	/* i386 */	"|^(invalid operand: )"
+	/* i386 */	"|^(Oops: )"
+	/* i386 */	"|^(Cpu: +[0-9])"
+	/* i386 */	"|^(current->tss)"
+	/* i386 */	"|^(\\*pde +=)"
+	/* i386 */	"|^(EIP: )"
+	/* i386 */	"|^(EFLAGS: )"
+	/* i386 */	"|^(eax: )"
+	/* i386 */	"|^(esi: )"
+	/* i386 */	"|^(ds: )"
+
+	/* m68k */	"|^(pc=)"
+	/* m68k */	"|^(68060 access)"
+	/* m68k */	"|^(Exception at )"
+	/* m68k */	"|^(PC: )"
+	/* m68k */	"|^(d[04]: )"
+	/* m68k */	"|^(Frame format=)"
+	/* m68k */	"|^(wb [0-9] stat)"
+	/* m68k */	"|^(push data: )"
+	/* m68k */	"|^(baddr=)"
+	/* any other m68K lines to print? */
+
+	/* sparc */	"|(\\([0-9]\\): Oops )"
+	/* sparc */	"|(: memory violation)"
+	/* sparc */	"|(: Exception at)"
+	/* sparc */	"|(: Arithmetic fault)"
+	/* sparc */	"|(: Instruction fault)"
+	/* sparc */	"|(: arithmetic trap)"
+	/* sparc */	"|^(Bad unaligned kernel)"
+	/* sparc */	"|^(Forwarding unaligned exception)"
+	/* sparc */	"|^(: unhandled unaligned exception)"
+	/* sparc */	"|(: unaligned trap)"
+	/* sparc */	"|^(<sc)"
+	/* sparc */	"|^(pc *=)"
+	/* sparc */	"|^(r[0-9]+ *=)"
+	/* sparc */	"|^(gp *=)"
+	/* any other sparc lines to print? */
+
+	/* alpha */	"|^(tsk->)"
+	/* alpha   die_if_kernel has no fixed text, identify by (pid): text. */
+	/* alpha */	"|^(.*\\([0-9]+\\): "
+			   /* Somebody has been playful with the texts.  */
+			   "((Whee)|(Oops)|(Kernel)|(.*Penguin)|(BOGUS))"
+			  ")"
+	/* alpha */	"|^(PSR: )"
+	/* alpha */	"|^(g0: )"
+	/* alpha */	"|^(o0: )"
+	/* alpha */	"|^(l0: )"
+	/* alpha */	"|^(i0: )"
+	/* alpha */	"|^(Instruction DUMP: )"
+	/* any other alpha lines to print? */
+
+	/* ppc */	"|^(MSR: )"
+	/* ppc */	"|^(TASK = )"
+	/* ppc */	"|^(last math )"
+	/* ppc */	"|^(GPR[0-9]+: )"
+	/* ppc */	"|(kernel pc )"
+	/* ppc */	"|(trap at PC: )"
+	/* ppc */	"|(bad area pc )"
+	/* ppc */	"|(NIP: )"
+	/* any other ppc lines to print? */
+
+	/* MIPS */	"|^(\\$[0-9 ]+:)"
+	/* MIPS */	"|^(epc )"
+	/* MIPS */	"|^(Status:)"
+	/* MIPS */	"|^(Cause :)"
+	/* MIPS */	"|( ra *=)"
+	/* any other MIPS lines to print? */
+
+			")",
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_print_pmatch);
+
+	re_string_check(re_Oops_print.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_print, *text, re_Oops_print.re_nsub+1,
+		re_Oops_print_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	print = 0;
+	if (i == 0) {
+		re_strings(&re_Oops_print, *text, re_Oops_print_pmatch,
+			string);
+		print = 1;
+		/* Handle multiline messages, messy */
+		if (!(*string)[2] && !(*string)[3] && !(*string)[4])
+			stack_line = 0;
+		else if ((*string)[2] || (*string)[3])
+			stack_line = 1;
+		else if (stack_line && !(*string)[4]) {
+			print = 0;
+			stack_line = 0;
+		}
+		if (!(*string)[5] && !(*string)[6] && !(*string)[9])
+			trace_line = 0;
+		else if ((*string)[5] || (*string)[9] || (*string)[10])
+			trace_line = 1;
+		else if (stack_line && !(*string)[6]) {
+			print = 0;
+			trace_line = 0;
+		}
+		if ((*string)[8])
+			add_Version((*string)[8]+8, "Oops");
+	}
+	return(print);
+}
+
+/* Look for the Code: line.  Returns start of the code bytes. */
+static const char *Oops_code(const char *line, char ***string, int string_max)
+{
+	int i;
+	static regex_t     re_Oops_code;
+	static regmatch_t *re_Oops_code_pmatch;
+	static const char procname[] = "Oops_code";
+
+	/* Oops 'Code: ' hopefully followed by at least one hex code.  sparc
+	 * brackets the PC in '<' and '>'.
+	 */
+	re_compile(&re_Oops_code,
+			"^("						/*  1 */
+	/* sparc */	  "(Instruction DUMP)"				/*  2 */
+	/* various */	  "|(Code *)"					/*  3 */
+			")"
+			":[ \t]+"
+			"("						/*  4 */
+			  "(general protection.*)"
+			  "|(<[0-9]+>)"
+			  "|((<?[0-9a-fA-F]+>?[ \t]*)+)"
+			")"
+			"(.*)$"				/* trailing garbage */
+			,
+		REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+		&re_Oops_code_pmatch);
+
+	re_string_check(re_Oops_code.re_nsub+1, string_max, procname);
+	i = regexec(&re_Oops_code, line, re_Oops_code.re_nsub+1,
+		re_Oops_code_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0) {
+		re_strings(&re_Oops_code, line, re_Oops_code_pmatch,
+			string);
+		if ((*string)[re_Oops_code.re_nsub] &&
+		    *((*string)[re_Oops_code.re_nsub])) {
+			fprintf(stderr,
+				"Warning: trailing garbage ignored on Code: "
+				"line\n"
+				"  Text: '%s'\n"
+				"  Garbage: '%s'\n",
+				line, (*string)[re_Oops_code.re_nsub]);
+			++warnings;
+		}
+		return((*string)[4]);
+	}
+	return(NULL);
+}
+
+/******************************************************************************/
+/*                      End architecture sensitive code                       */
+/******************************************************************************/
+
+/* Decode the Oops Code: via objdump*/
+static void Oops_decode(const unsigned char* code_text, elf_addr_t eip,
+			SYMBOL_SET *ss, char ***string, int string_max)
+{
+	FILE *f;
+	char *file, *line = NULL, code[CODE_SIZE];
+	int size = 0, adjust;
+	static char const procname[] = "Oops_decode";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+	/* text to binary */
+	if (!Oops_code_values(code_text, code, &adjust, string, string_max))
+		return;
+	/* binary to same format as ksymoops */
+	if (!(file = Oops_code_to_file(code, CODE_SIZE)))
+		return;
+	/* objdump the pseudo object */
+	if (!(f = Oops_objdump(file)))	
+		return;
+	while (fgets_local(&line, &size, f, procname)) {
+		if (debug > 1)
+			fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+		Oops_decode_one(ss, line, eip, adjust);
+	}
+	pclose_local(f, procname);	/* opened in Oops_objdump */
+	free(line);
+	if (unlink(file)) {
+		fprintf(stderr, "%s could not unlink %s", prefix, file);
+		perror(" ");
+	}
+}
+
+/* Reached the end of an Oops report, format the extracted data. */
+static void Oops_format(const SYMBOL_SET *ss_format)
+{
+	int i;
+	SYMBOL *s;
+	static const char procname[] = "Oops_format";
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s\n", procname);
+
+	compare_Version();	/* Oops might have a version one day */
+	printf("\n");
+	for (s = ss_format->symbol, i = 0; i < ss_format->used; ++i, ++s) {
+		/* For type C data, print Code:, address, map, "name" (actually
+		 * the text of an objdump line).  For other types print name,
+		 * address, map.
+		 */
+		if (s->type == 'C')
+			printf("Code:  %s %-30s %s\n",
+				format_address(s->address),
+				map_address(&ss_merged, s->address),
+				s->name);
+		else
+			printf("%s %s %s\n",
+				s->name,
+				format_address(s->address),
+				map_address(&ss_merged, s->address));
+	}
+	printf("\n");
+}
+
+/* Select next Oops input file */
+static FILE *Oops_next_file(int *filecount, char * const **filename)
+{
+	static FILE *f = NULL;
+	static const char procname[] = "Oops_next_file";
+	static int first_file = 1;
+
+	if (first_file) {
+		f = stdin;
+		first_file = 0;
+	}
+	while (*filecount) {
+		if (f)
+			fclose_local(f, procname);
+		f = NULL;
+		if (regular_file(**filename, procname))
+			f = fopen_local(**filename, "r", procname);
+		if (f) {
+			if (debug)
+				fprintf(stderr,
+					"DEBUG: reading Oops report "
+					"from %s\n", **filename);
+		}
+		++*filename;
+		--*filecount;
+		if (f)
+			return(f);
+	}
+	return(f);
+}
+
+/* Read the Oops report */
+#define MAX_STRINGS 300	/* Maximum strings in any Oops re */
+void Oops_read(int filecount, char * const *filename)
+{
+	char *line = NULL, **string = NULL;
+	const char *start, *text;
+	int i, size = 0, lineno = 0, lastprint = 0;
+	elf_addr_t eip = 0;
+	FILE *f;
+	SYMBOL_SET ss_format;
+	static const char procname[] = "Oops_read";
+
+	ss_init(&ss_format, "Oops log data");
+
+	if (!filecount && isatty(0))
+		printf("Reading Oops report from the terminal\n");
+
+	string = malloc(MAX_STRINGS*sizeof(*string));
+	if (!string)
+		malloc_error(procname);
+	memset(string, '\0', MAX_STRINGS*sizeof(*string));
+
+	do {
+		if (!(f = Oops_next_file(&filecount, &filename)))
+			continue;
+		while (fgets_local(&line, &size, f, procname)) {
+			if (debug > 2)
+				fprintf(stderr,
+					"DEBUG: %s - %s\n", procname, line);
+			++lineno;
+			if (Oops_print(line, &text, &string, MAX_STRINGS)) {
+				puts(line);
+				lastprint = lineno;
+			}
+			if ((start = Oops_eip(text, &string, MAX_STRINGS)))
+				Oops_set_eip(start, &eip, &ss_format);
+			if ((start = Oops_ra(text, &string, MAX_STRINGS)))
+				Oops_set_ra(start, &ss_format);
+			if ((start = Oops_trace(text, &string, MAX_STRINGS)))
+				Oops_trace_line(text, start, &ss_format);
+			if ((start = Oops_code(text, &string, MAX_STRINGS))) {
+				Oops_decode(start, eip, &ss_format,
+					&string, MAX_STRINGS);
+				Oops_format(&ss_format);
+				ss_free(&ss_format);
+			}
+			/* More than 5 (arbitrary) lines which were not printed
+			 * and there is some saved data, assume we missed the
+			 * Code: line.
+			 */
+			if (ss_format.used && lineno > lastprint+5) {
+				fprintf(stderr,
+					"Warning, Code line not seen, dumping "
+					"what data is available\n");
+				++warnings;
+				Oops_format(&ss_format);
+				ss_free(&ss_format);
+			}
+		}
+		if (ss_format.used) {
+			fprintf(stderr,
+				"Warning, Code line not seen, dumping "
+				"what data is available\n");
+			++warnings;
+			Oops_format(&ss_format);
+			ss_free(&ss_format);
+		}
+	} while (filecount != 0);
+
+	for (i = 0; i < sizeof(string); ++i) {
+		free(string[i]);
+		string[i] = NULL;
+	}
+	free(line);
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/README linux/scripts/ksymoops-0.6/patches/README
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/README	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/patches/README	Mon Nov  2 07:30:23 1998
@@ -0,0 +1,15 @@
+An ad-hoc collection of patches to the Linux kernel and related
+utilities, to make the best use of ksymoops.
+
+Most of the kernel patches are to extend the information logged on an
+Oops, some architectures currently (2.1.126) do not provide full
+diagnostics.  Hopefully these patches will be integrated into the
+kernel tree in future.  ksymoops will still run without these patches
+but will provide degraded output.
+
+mips    - Kernel patch by Ulf Carlsson <ulfc@bun.falkenberg.se> to
+          provide full diagnostics on a mips Oops.
+
+ppc     - Kernel patch by "Ryan Nielsen" <ran@krazynet.com> to provide
+          full diagnostics on a ppc Oops.
+
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/mips linux/scripts/ksymoops-0.6/patches/mips
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/mips	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/patches/mips	Mon Nov  2 07:30:23 1998
@@ -0,0 +1,167 @@
+--- linux/arch/mips/kernel/traps.c-orig	Thu Oct 29 17:23:19 1998
++++ linux/arch/mips/kernel/traps.c	Fri Oct 30 13:25:21 1998
+@@ -6,6 +6,7 @@
+  *
+  * Copyright 1994, 1995, 1996, 1997, 1998 by Ralf Baechle
+  * Modified for R3000 by Paul M. Antoine, 1995, 1996
++ * Complete output from die() by Ulf Carlsson, 1998
+  */
+ #include <linux/config.h>
+ #include <linux/init.h>
+@@ -80,50 +81,61 @@
+  * This routine abuses get_user()/put_user() to reference pointers
+  * with at least a bit of error checking ...
+  */
+-void show_registers(char * str, struct pt_regs * regs, long err)
++void show_stack(unsigned int *sp)
+ {
+-	int	i;
+-	int	*stack;
+-	u32	*sp, *pc, addr, module_start, module_end;
+-	extern	char start_kernel, _etext;
++	int i;
++	unsigned int *stack;
+ 
+-	sp = (u32 *)regs->regs[29];
+-	pc = (u32 *)regs->cp0_epc;
++	stack = sp;
++	i = 0;
+ 
+-	show_regs(regs);
++	printk("Stack:");
++	while ((unsigned long) stack & (PAGE_SIZE - 1)) {
++		unsigned long stackdata;
+ 
+-	/*
+-	 * Dump the stack
+-	 */
+-	printk("Process %s (pid: %ld, stackpage=%08lx)\nStack: ",
+-		current->comm, current->pid, (unsigned long)current);
+-	for(i=0;i<5;i++)
+-		printk("%08x ", *sp++);
+-	stack = (int *) sp;
++		if (__get_user(stackdata, stack++)) {
++			printk(" (Bad stack address)");
++			break;
++		}
+ 
+-	for(i=0; i < kstack_depth_to_print; i++) {
+-		unsigned int stackdata;
++		printk(" %08lx", stackdata);
+ 
+-		if (((u32) stack & (PAGE_SIZE -1)) == 0)
+-			break;
+-		if (i && ((i % 8) == 0))
+-			printk("\n       ");
+-		if (get_user(stackdata, stack++) < 0) {
+-			printk("(Bad stack address)");
++		if (++i > 40) {
++			printk(" ...");
+ 			break;
+ 		}
+-		printk("%08x ", stackdata);
++
++		if (i % 8 == 0)
++			printk("\n      ");
+ 	}
+-	printk("\nCall Trace: ");
+-	stack = (int *)sp;
+-	i = 1;
++}
++
++void show_trace(unsigned int *sp)
++{
++	int i;
++	unsigned int *stack;
++	unsigned long kernel_start, kernel_end;
++	unsigned long module_start, module_end;
++	extern char _stext, _etext;
++
++	stack = sp;
++	i = 0;
++
++	kernel_start = (unsigned long) &_stext;
++	kernel_end = (unsigned long) &_etext;
+ 	module_start = VMALLOC_START;
+ 	module_end = module_start + MODULE_RANGE;
+-	while (((unsigned long)stack & (PAGE_SIZE -1)) != 0) {
+-		if (get_user(addr, stack++) < 0) {
+-			printk("(Bad address)\n");
++
++	printk("\nCall Trace:");
++
++	while ((unsigned long) stack & (PAGE_SIZE -1)) {
++		unsigned long addr;
++
++		if (__get_user(addr, stack++)) {
++			printk(" (Bad stack address)\n");
+ 			break;
+ 		}
++
+ 		/*
+ 		 * If the address is either in the text segment of the
+ 		 * kernel, or in the region which contains vmalloc'ed
+@@ -132,26 +144,33 @@
+ 		 * down the cause of the crash will be able to figure
+ 		 * out the call path that was taken.
+ 		 */
+-		if (((addr >= (u32) &start_kernel) &&
+-		     (addr <= (u32) &_etext)) ||
+-		    ((addr >= module_start) && (addr <= module_end))) {
+-			if (i && ((i % 8) == 0))
+-				printk("\n       ");
+-			printk("%08x ", addr);
+-			i++;
++
++		if ((addr >= kernel_start && addr < kernel_end) ||
++		    (addr >= module_start && addr < module_end)) { 
++
++			printk(" [<%08lx>]", addr);
++			if (++i > 40) {
++				printk(" ...");
++				break;
++			}
+ 		}
+ 	}
++}
+ 
+-	printk("\nCode : ");
+-	if ((KSEGX(pc) == KSEG0 || KSEGX(pc) == KSEG1) &&
+-	    (((unsigned long) pc & 3) == 0))
+-	{
+-		for(i=0;i<5;i++)
+-			printk("%08x ", *pc++);
+-		printk("\n");
++void show_code(unsigned int *pc)
++{
++	long i;
++
++	printk("\nCode:");
++
++	for(i = -3 ; i < 6 ; i++) {
++		unsigned long insn;
++		if (__get_user(insn, pc + i)) {
++			printk(" (Bad address in epc)\n");
++			break;
++		}
++		printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
+ 	}
+-	else
+-		printk("(Bad address in epc)\n");
+ }
+ 
+ void die(const char * str, struct pt_regs * regs, unsigned long err)
+@@ -162,6 +181,12 @@
+ 	console_verbose();
+ 	printk("%s: %04lx\n", str, err & 0xffff);
+ 	show_regs(regs);
++	printk("Process %s (pid: %ld, stackpage=%08lx)\n",
++		current->comm, current->pid, (unsigned long) current);
++	show_stack((unsigned int *) regs->regs[29]);
++	show_trace((unsigned int *) regs->regs[29]);
++	show_code((unsigned int *) regs->cp0_epc);
++	printk("\n");
+ 	do_exit(SIGSEGV);
+ }
+ 
+
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/ppc linux/scripts/ksymoops-0.6/patches/ppc
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/patches/ppc	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/patches/ppc	Mon Nov  2 07:30:23 1998
@@ -0,0 +1,67 @@
+--- linux/arch/ppc/kernel/process.c	1998/10/11 17:47:23	1.67
++++ linux/arch/ppc/kernel/process.c	1998/11/02 03:11:28
+@@ -196,6 +198,19 @@
+ 	_enable_interrupts(s);
+ }
+ 
++void instruction_dump (unsigned long *pc)
++{
++	int i;
++
++	if((((unsigned long) pc) & 3))
++                return;
++
++	printk("Instruction DUMP:");
++	for(i = -3; i < 6; i++)
++		printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
++	printk("\n");
++}
++
+ void show_regs(struct pt_regs * regs)
+ {
+ 	int i;
+--- linux/arch/ppc/kernel/traps.c	1998/05/05 19:18:53	1.21
++++ linux/arch/ppc/kernel/traps.c	1998/11/02 03:11:36
+@@ -79,6 +79,7 @@
+ 		debugger(regs);
+ #endif
+ 		print_backtrace((unsigned long *)regs->gpr[1]);
++		instruction_dump((unsigned long *)regs->nip);
+ 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
+ 	}
+ 	force_sig(signr, current);
+@@ -126,6 +127,7 @@
+ 		debugger(regs);
+ #endif
+ 		print_backtrace((unsigned long *)regs->gpr[1]);
++		instruction_dump((unsigned long *)regs->nip);
+ 		panic("machine check");
+ 	}
+ 	_exception(SIGSEGV, regs);	
+@@ -219,6 +221,7 @@
+ #endif
+ 	show_regs(regs);
+ 	print_backtrace((unsigned long *)regs->gpr[1]);
++	instruction_dump((unsigned long *)regs->nip);
+ 	panic("kernel stack overflow");
+ }
+ 
+--- linux/arch/ppc/mm/fault.c	1998/10/06 03:13:19	1.28
++++ linux/arch/ppc/mm/fault.c	1998/11/02 03:11:36
+@@ -89,6 +89,7 @@
+ 			printk("page fault in interrupt handler, addr=%lx\n",
+ 			       address);
+ 			show_regs(regs);
++			instruction_dump((unsigned long *)regs->nip);
+ #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ 			if (debugger_kernel_faults)
+ 				debugger(regs);
+@@ -174,6 +175,7 @@
+ 	/* kernel has accessed a bad area */
+ 	show_regs(regs);
+ 	print_backtrace( (unsigned long *)regs->gpr[1] );
++	instruction_dump((unsigned long *)regs->nip);
+ #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ 	if (debugger_kernel_faults)
+ 		debugger(regs);
+
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/re.c linux/scripts/ksymoops-0.6/re.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/re.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/re.c	Mon Nov  2 07:37:03 1998
@@ -0,0 +1,145 @@
+/*
+	re.c.
+
+	Regular expression processing for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	PPC trace addresses are not bracketed, add new re.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Compile a regular expression */
+void re_compile(regex_t *preg, const char *regex, int cflags,
+		regmatch_t **pmatch)
+{
+	int i, l;
+	char *p;
+	static char const procname[] = "re_compile";
+
+	if (preg->re_nsub)
+		return;		/* already compiled */
+
+	if (debug)
+		fprintf(stderr, "DEBUG: %s '%s'", procname, regex);
+	if ((i = regcomp(preg, regex, cflags))) {
+		l = regerror(i, preg, NULL, 0);
+		++l;	/* doc is ambiguous, be safe */
+		p = malloc(l);
+		if (!p)
+			malloc_error("regerror text");
+		regerror(i, preg, p, l);
+		fprintf(stderr,
+			"%s: fatal %s error on '%s' - %s\n",
+			prefix, procname, regex, p);
+		exit(2);
+	}
+	if (debug)
+		fprintf(stderr, " %d sub expression(s)\n", preg->re_nsub);
+	/* [0] is entire match, [1] is first substring */
+	*pmatch = malloc((preg->re_nsub+1)*sizeof(**pmatch));
+	if (!*pmatch)
+		malloc_error("pmatch");
+
+}
+
+/* Compile common regular expressions */
+void re_compile_common(void)
+{
+
+	/* nm: address, type, symbol */
+	re_compile(&re_nm,
+		"^([0-9a-fA-F]{4,}) +([^ ]) +([^ ]+)$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_nm_pmatch);
+
+	/* bracketed address preceded by optional white space */
+	re_compile(&re_bracketed_address,
+		"^[ \t]*" BRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED,
+		&re_bracketed_address_pmatch);
+
+	/* unbracketed address preceded by optional white space */
+	re_compile(&re_unbracketed_address,
+		"^[ \t*]*" UNBRACKETED_ADDRESS,
+		REG_NEWLINE|REG_EXTENDED,
+		&re_unbracketed_address_pmatch);
+
+}
+
+/* Split text into the matching re substrings - Perl is so much easier :).
+ * Each element of *string is set to a malloced copy of the substring or
+ * NULL if the substring did not match (undef).  A zero length substring match
+ * is represented by a zero length **string.
+ */
+void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+		char ***string)
+{
+	int i;
+	if (!*string) {
+		*string = malloc((preg->re_nsub+1)*sizeof(**string));
+		if (!*string)
+			malloc_error("re_strings base");
+		for (i = 0; i < preg->re_nsub+1; ++i)
+			(*string)[i] = NULL;
+	}
+	for (i = 0; i < preg->re_nsub+1; ++i) {
+		if (debug > 4)
+			fprintf(stderr,
+				"DEBUG: re_string %d offsets %d %d",
+				i, pmatch[i].rm_so, pmatch[i].rm_eo);
+		if (pmatch[i].rm_so == -1) {
+			/* no match for this sub expression */
+			free((*string)[i]);
+			(*string)[i] = NULL;
+			if (debug > 4)
+				fprintf(stderr, " (undef)\n");
+		}
+		else {
+			int l = pmatch[i].rm_eo - pmatch[i].rm_so + 1;
+			char *p;
+			p = malloc(l);
+			if (!p)
+				malloc_error("re_strings");
+			strncpy(p, text+pmatch[i].rm_so, l-1);
+			*(p+l-1) = '\0';
+			(*string)[i] = p;
+			if (debug > 4)
+				fprintf(stderr, " '%s'\n", p);
+		}
+	}
+}
+
+/* Free the matching re substrings */
+void re_strings_free(const regex_t *preg, char ***string)
+{
+	if (*string) {
+		int i;
+		for (i = 0; i < preg->re_nsub+1; ++i)
+			free((*string)[i]);
+		free(*string);
+		*string = NULL;
+	}
+}
+
+/* Check that there are enough strings for an re */
+void re_string_check(int need, int available, const char *msg)
+{
+	if (need > available) {
+		fprintf(stderr,
+			"%s: fatal not enough re_strings in %s.  "
+			"Need %d, available %d\n",
+			prefix, msg, need, available);
+		exit(2);
+	}
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops-0.6/symbol.c linux/scripts/ksymoops-0.6/symbol.c
--- v2.2.0-pre3/linux/scripts/ksymoops-0.6/symbol.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/ksymoops-0.6/symbol.c	Mon Nov  2 07:37:13 1998
@@ -0,0 +1,440 @@
+/*
+	symbol.c.
+
+	Symbol handling routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Fix end of code calculation.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Initialise a symbol source */
+void ss_init(SYMBOL_SET *ss, const char *msg)
+{
+	memset(ss, '\0', sizeof(*ss));
+	ss->source = strdup(msg);
+	if (!ss->source)
+		malloc_error(msg);
+}
+
+/* Free dynamic data from a symbol source */
+void ss_free(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
+		free(s->name);
+	free(ss->symbol);
+	free(ss->source);
+	memset(ss, '\0', sizeof(*ss));
+}
+
+/* Initialise common symbol sets */
+void ss_init_common(void)
+{
+	ss_init(&ss_Version, "Version_");
+}
+
+/* Find a symbol name in a symbol source.  Brute force ascending order search,
+ * no hashing.  If start is not NULL, it contains the starting point for the
+ * scan and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by name.
+ */
+SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
+{
+	int i, l;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if ((l = strcmp(symbol, s->name)) == 0) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		if (l < 0)
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Find an address in a symbol source.  Brute force ascending order search, no
+ * hashing.  If start is not NULL, it contains the starting point for the scan
+ * and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by address.
+ */
+static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
+			    const elf_addr_t address, int *start)
+{
+	int i;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (address > s->address)
+			continue;
+		else if (address == s->address) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		else
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Add a symbol to a symbol set, address in binary */
+void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+		  const char type, const char keep, const char *symbol)
+{
+	int i;
+	char **string = NULL;
+	SYMBOL *s;
+	static regex_t     re_symbol_ver;
+	static regmatch_t *re_symbol_ver_pmatch;
+	static const char procname[] = "add_symbol_n";
+
+	/* Strip out any trailing symbol version _Rxxxxxxxx. */
+	re_compile(&re_symbol_ver,
+		"^(.*)_R[0-9a-fA-F]{8,}$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_symbol_ver_pmatch);
+
+	i = regexec(&re_symbol_ver, symbol,
+		    re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0)
+		re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
+			&string);
+
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
+			procname, ss->source, format_address(address),
+			type, keep, i == 0 ? string[1] : symbol);
+	if (ss->used > ss->alloc) {
+		fprintf(stderr,
+			"%s: fatal %s ss %s used (%d) > alloc (%d)\n",
+			procname, prefix, ss->source, ss->used, ss->alloc);
+		exit(2);
+	}
+	if (ss->used == ss->alloc) {
+		/* increase by 20% or 10, whichever is larger, arbitrary */
+		int newsize = ss->alloc*120/100;
+		if (newsize < ss->alloc+10)
+			newsize = ss->alloc+10;
+		if (debug > 3)
+			fprintf(stderr,
+				"DEBUG: %s increasing %s from %d to %d "
+				"entries\n",
+				procname, ss->source, ss->alloc, newsize);
+		ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
+		if (!ss->symbol)
+			malloc_error("realloc ss");
+		ss->alloc = newsize;
+	}
+	s = ss->symbol+ss->used;
+	if (i == 0) {
+		s->name = string[1];
+		string[1] = NULL;	/* don't free this one */
+	}
+	else {
+		s->name = strdup(symbol);
+		if (!s->name)
+			malloc_error("strdup symbol");
+	}
+	s->type = type;
+	s->keep = keep;
+	s->address = address;
+	++ss->used;
+	re_strings_free(&re_symbol_ver, &string);
+}
+
+/* Add a symbol to a symbol set, address in character */
+void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+		const char keep, const char *symbol)
+{
+	elf_addr_t a;
+	static char const procname[] = "add_symbol";
+	errno = 0;
+	a = strtoul(address, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s: %s address '%s' is in error",
+			prefix, procname, address);
+		perror(" ");
+		++errors;
+	}
+	add_symbol_n(ss, a, type, 1, symbol);
+}
+
+/* Map an address to symbol, offset and length, address in binary */
+char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
+{
+	int i = 0, l;
+	SYMBOL *s;
+	static char *map = NULL;
+	static int size = 0;
+	static const char procname[] = "map_address_n";
+
+	if (debug > 2)
+		fprintf(stderr, "DEBUG: %s %s %s\n",
+			procname, ss->source, format_address(address));
+	s = find_symbol_address(ss, address, &i);
+	if (!s && --i >= 0)
+		s = ss->symbol+i;	/* address is between s and s+1 */
+			
+	/* Extra map text is always < 100 bytes */
+	if (s)
+		l = strlen(s->name) + 100;
+	else
+		l = 100;
+	if (l > size) {
+		map = realloc(map, l);
+		if (!map)
+			malloc_error(procname);
+		size = l;
+	}
+	if (!s) {
+		if (ss->used == 0)
+			snprintf(map, size, "No symbols available");
+		else
+			snprintf(map, size, "Before first symbol");
+	}
+	else if ((i+1) >= ss->used) {
+		/* Somewhere past last symbol.  Length of last section of code
+		 * is unknown, arbitrary cutoff at 32K.
+		 */
+		elf_addr_t offset = address - s->address;
+		if (offset > 32768)
+			snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
+		else
+			snprintf(map, size, "<%s+%lx/????>", s->name, offset);
+	}
+	else
+		snprintf(map, size,
+			"<%s+%lx/%lx>",
+			s->name, address - s->address,
+			(s+1)->address - s->address);
+	return(map);
+}
+
+/* After sorting, obsolete symbols are at the top.  Delete them. */
+static void ss_compress(SYMBOL_SET *ss)
+{
+	int i, j;
+	SYMBOL *s;
+	static const char procname[] = "ss_compress";
+
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s on table %s, before %d ",
+			procname, ss->source, ss->used);
+	for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (!s->keep) {
+			for (j = i; j < ss->used; ++j, ++s) {
+				if (s->keep) {
+					fprintf(stderr,
+						"%s: fatal %s table %s is not "
+						"sorted\n",
+						prefix, procname, ss->source);
+					exit(2);
+				}
+			}
+			break;
+		}
+	}
+	for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
+		free(s->name);
+	ss->used = i;
+	if (debug > 1)
+		fprintf(stderr, "after %d\n", ss->used);
+}
+
+static int ss_compare_atn(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	if (c->type > d->type)
+		return(1);
+	if (c->type < d->type)
+		return(-1);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	return(0);
+}
+
+/* Sort a symbol set by address, type and name */
+void ss_sort_atn(SYMBOL_SET *ss)
+{
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_atn);
+	ss_compress(ss);
+}
+
+static int ss_compare_na(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	return(0);
+}
+
+/* Sort a symbol set by name and address, drop duplicates.  There should be
+ * no duplicates but I have seen duplicates in ksyms on 2.0.35.
+ */
+void ss_sort_na(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+	s = ss->symbol;
+	for (i = 0; i < ss->used-1; ++i) {
+		if (strcmp(s->name, (s+1)->name) == 0 &&
+		    s->address == (s+1)->address) {
+			if (s->type != ' ')
+				(s+1)->keep = 0;
+			else
+				s->keep = 0;
+		}
+		++s;
+	}
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+}
+
+/* Copy a symbol set, including all its strings */
+SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
+{
+	SYMBOL_SET *ssc;
+	if (debug > 3)
+		fprintf(stderr,
+			"DEBUG: ss_copy %s\n", ss->source);
+	ssc = malloc(sizeof(*ssc));
+	if (!ssc)
+		malloc_error("copy ssc");
+	ss_init(ssc, ss->source);
+	ssc->used = ss->used;
+	ssc->alloc = ss->used;	/* shrink the copy */
+	ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
+	if (!(ssc->symbol))
+		malloc_error("copy ssc symbols");
+	memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
+	return(ssc);
+}
+
+/* Convert version number to major, minor string.  */
+static const char *format_Version(elf_addr_t Version)
+{
+	static char string[12];	/* 255.255.255\0 worst case */
+	snprintf(string, sizeof(string), "%d.%d.%d",
+		(Version >> 16) & 0xff,
+		(Version >> 8) & 0xff,
+		(Version) & 0xff);
+	return(string);
+}
+
+/* Save version number.  The "address" is the version number, the "symbol" is
+ * the source of the version.
+ */
+void add_Version(const char *version, const char *source)
+{
+	static char const procname[] = "add_Version";
+	int i = atoi(version);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s %s %s %s\n",
+			procname, source, version, format_Version(i));
+	add_symbol_n(&ss_Version, i, 'V', 1, source);
+}
+
+/* Extract Version_ number from a symbol set and save it.  */
+void extract_Version(SYMBOL_SET *ss)
+{
+	int i = 0;
+	SYMBOL *s;
+
+	s = find_symbol_name(ss, "Version_", &i);
+	if (!s && i < ss->used)
+		s = ss->symbol+i;	/* first symbol after "Version_" */
+	if (!s || strncmp(s->name, "Version_", 8))
+		return;
+	add_Version(s->name+8, ss->source);
+}
+
+/* Compare all extracted Version numbers.  Silent unless there is a problem. */
+void compare_Version(void)
+{
+	int i = 0;
+	SYMBOL *s, *s0;
+	static int prev_used = 0;
+
+	if (!ss_Version.used)
+		return;
+	/* Only check if the Version table has changed in size */
+	if (prev_used == ss_Version.used)
+		return;
+
+	ss_sort_na(&ss_Version);
+	s0 = s = ss_Version.symbol;
+	if (debug)
+		fprintf(stderr, "DEBUG: Version %s\n",
+			format_Version(s0->address));
+	for (i = 0; i < ss_Version.used; ++i, ++s) {
+		if (s->address != s0->address) {
+			fprintf(stderr,
+				"Version mismatch error.  %s says %s, ",
+				s0->name,
+				format_Version(s0->address));
+			fprintf(stderr,
+				"%s says %s.  Expect lots of address "
+				"mismatches.\n",
+				s->name,
+				format_Version(s->address));
+			++errors;
+		}
+	}
+	prev_used = ss_Version.used;
+}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
--- v2.2.0-pre3/linux/scripts/ksymoops.cc	Sun Jun  7 11:16:40 1998
+++ linux/scripts/ksymoops.cc	Wed Dec 31 16:00:00 1969
@@ -1,411 +0,0 @@
-// ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs
-// Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
-// compile like so: g++ -o ksymoops ksymoops.cc -liostream
-
-// Update to binutils 2.8 and handling of header text on oops lines by
-// Keith Owens <kaos@ocs.com.au>
-
-//////////////////////////////////////////////////////////////////////////////
-
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2, or (at your option)
-// any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program; see the file COPYING.  If not, write to the
-// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-// This is a simple filter to resolve EIP and call-trace symbols from
-// a Linux kernel "Oops" log.  Supply the symbol-map file name as a
-// command-line argument, and redirect the oops-log into stdin.  Out
-// will come the EIP and call-trace in symbolic form.
-
-// Changed by Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
-// adapted to Linux/m68k
-
-//////////////////////////////////////////////////////////////////////////////
-
-// BUGS:
-// * Only resolves operands of jump and call instructions.
-
-#include <fstream.h>
-#include <strstream.h>
-#include <iomanip.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <a.out.h>
-
-inline int strnequ(char const* x, char const* y, size_t n) { return (strncmp(x, y, n) == 0); }
-
-const int code_size = 20;
-
-//////////////////////////////////////////////////////////////////////////////
-
-class KSym
-{
-    friend class NameList;
-
-  private:
-    unsigned long address_;
-    char* name_;
-    long offset_;
-    long extent_;
-
-  private:
-    istream& scan(istream&);
-    ostream& print(ostream&) const;
-    void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; }
-
-  public:
-    friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); }
-    friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); }
-};
-
-istream&
-KSym::scan(istream& is)
-{
-    is >> ::hex >> address_;
-    char type;
-    is >> type;
-    char name[128];
-    is >> name;
-    name_ = new char [strlen(name)+1];
-    strcpy(name_, name);
-    offset_ = 0;
-    return is;
-}
-
-ostream&
-KSym::print(ostream& os) const
-{
-    os << ::hex << address_ + offset_ << ' ' << '<' << name_;
-    if (offset_)
-	os << '+' << ::hex << offset_ << '/' << ::hex << extent_;
-    return os << '>';
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class NameList
-{
-  private:
-    // Caution: Fixed Allocation!
-    // This should suffice for awhile since 1.1.86 has only 2482 symbols.
-    KSym ksyms_0_[8000];
-    int cardinality_;
-
-  public:
-    NameList() : cardinality_(0) { }
-    
-  private:
-    istream& scan(istream&);
-
-  public:
-    int valid() { return (cardinality_ > 0); }
-	
-    KSym* find(unsigned long address);
-    void decode(unsigned char* code, long eip_addr);
-    
-  public:
-    friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); }
-};
-
-KSym*
-NameList::find(unsigned long address)
-{
-    if (!valid())
-	return 0;
-    KSym* start = ksyms_0_;
-    KSym* end = &ksyms_0_[cardinality_ - 1];
-    if (address < start->address_ || address >= end->address_)
-	return 0;
-
-    KSym* mid;
-    while (start <= end) {
-	mid = &start[(end - start) / 2];
-	if (mid->address_ < address)
-	    start = mid + 1;
-	else if (mid->address_ > address)
-	    end = mid - 1;
-	else
-	    return mid;
-    }
-    while (mid->address_ > address)
-	--mid;
-    mid->offset_ = address - mid->address_;
-    if (mid->offset_ > mid->extent_)
-	clog << "Oops! " << *mid << endl;
-    return mid;
-}
-
-void
-NameList::decode(unsigned char* code, long eip_addr)
-{
-    /* This is a hack to avoid using gcc.  We create an object file by
-       concatenating objfile_head, the twenty bytes of code, and
-       objfile_tail.  */
-    static struct exec objfile_head = {
-	OMAGIC, code_size + 4, 0, 0, sizeof (struct nlist) * 3, 0, 0, 0
-    };
-    static struct {
-	unsigned char tail[4];
-	struct nlist syms[3];
-	unsigned long strsize;
-	char strings[42];
-    } objfile_tail = {
-#ifdef i386
-	{ 0x00, 0x90, 0x90, 0x90 },
-#endif
-#ifdef mc68000
-	{ 0x00, 0x00, 0x00, 0x00 },
-#endif
-	{ { (char *) 4, N_TEXT, 0, 0, 0 },
-	  { (char *) 19, N_TEXT, 0, 0, 0 },
-	  { (char *) 37, N_TEXT | N_EXT, 0, 0, 0 } },
-	42,
-	"gcc2_compiled.\0___gnu_compiled_c\0_EIP\0"
-    };
-    char const* objdump_command = "objdump -d oops_decode.o";
-    char const* objfile_name = &objdump_command[11];
-    ofstream objfile_stream(objfile_name);
-
-    objfile_stream.write((char *) &objfile_head, sizeof(objfile_head));
-    objfile_stream.write(code, code_size);
-    objfile_stream.write((char *) &objfile_tail, sizeof(objfile_tail));
-    objfile_stream.close();
-    
-    FILE* objdump_FILE = popen(objdump_command, "r");
-    if (objdump_FILE == 0) {
-	clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl;
-	return;
-    }
-    
-    char buf[1024];
-    int lines = 0;
-    int eip_seen = 0;
-    long offset;
-    while (fgets(buf, sizeof(buf), objdump_FILE)) {
-	if (strlen(buf) < 14)
-	    continue;
-	if (eip_seen && buf[4] == ':') {
-	    // assume objdump from binutils 2.8..., reformat to old style
-	    offset = strtol(buf, 0, 16);
-	    char newbuf[sizeof(buf)];
-	    memset(newbuf, '\0', sizeof(newbuf));
-	    ostrstream ost(newbuf, sizeof(newbuf));
-	    ost.width(8);
-	    ost << hex << offset;
-	    ost << " <_EIP+" << hex << offset << ">: " << &buf[6] << ends;
-	    strcpy(buf, newbuf);
-	}
-	if (!strnequ(&buf[9], "<_EIP", 5))
-	    continue;
-	eip_seen = 1;
-	if (strstr(buf, " is out of bounds"))
-	    break;
-	lines++;
-	cout << "Code: ";
-	if (!valid()) {
-	    cout << buf;
-	    continue;
-	}
-	offset = strtol(buf, 0, 16);
-	char* bp_0 = strchr(buf, '>');
-	KSym* ksym = find(eip_addr + offset);
-	if (bp_0)
-	    bp_0 += 2;
-	else
-	    bp_0 = strchr(buf, ':');
-	if (ksym)
-	    cout << *ksym << ' ';
-	char *bp_1 = strstr(bp_0, "\t");        // objdump from binutils 2.8...
-	if (bp_1)
-	    ++bp_1;
-	else
-	    bp_1 = bp_0;
-	char *bp = bp_1;
-	while (!isspace(*bp))
-	    bp++;
-	while (isspace(*bp))
-	    bp++;
-	if (!isxdigit(*bp)) {
-	    cout << bp_0;
-#ifdef i386
-	} else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
-	    long rel_addr = strtol(bp, 0, 16);
-	    ksym = find(eip_addr + rel_addr);
-	    if (ksym) {
-		*bp++ = '\0';
-		cout << bp_0 << *ksym << endl;
-	    } else
-		cout << bp_0;
-#endif
-#ifdef mc68000
-	} else if ((bp_1[0] == 'b' && bp_1[4] == ' ' && strchr("swl", bp_1[3]))
-		   || (bp_1[0] == 'd' && bp_1[1] == 'b')) {
-	    // a branch or decr-and-branch insn
-	    if (bp_1[0] == 'd') // skip register
-		while (*bp && *bp++ != ',');
-	    long rel_addr = strtoul(bp, 0, 16);
-	    ksym = find(eip_addr + rel_addr);
-	    if (ksym) {
-		*bp++ = '\0';
-		cout << bp_0 << *ksym << endl;
-	    } else
-	      cout << bp_0;
-#endif
-	} else {
-	    cout << bp_0;
-	}
-    }
-    if (!lines)
-	clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl;
-    pclose(objdump_FILE);
-    unlink(objfile_name);
-}
-
-istream&
-NameList::scan(istream& is)
-{
-    KSym* ksyms = ksyms_0_;
-    int cardinality = 0;
-    while (!is.eof()) {
-	is >> *ksyms;
-	if (cardinality++ > 0)
-	    ksyms[-1].set_extent(*ksyms);
-	ksyms++;
-    }
-    cardinality_ = --cardinality;
-    return is;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-char const* program_name;
-
-void
-usage()
-{
-    clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl;
-    exit(1);
-}
-
-int
-main(int argc, char** argv)
-{
-    char c;
-    char *oops_column = NULL;
-    program_name = (argc--, *argv++);
-
-    NameList names;
-    if (argc > 1)
-	usage();
-    else if (argc == 1) {
-	char const* map_file_name = (argc--, *argv++);
-	ifstream map(map_file_name);
-	if (map.bad())
-	    clog << program_name << ": Can't open `" << map_file_name << "'" << endl;
-	else {
-	    map >> names;
-	    cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl;
-	}
-    }
-    if (!names.valid())
-	cout << "No symbol map.  I'll only show you disassembled code." << endl;
-    cout << endl;
-
-    char buffer[1024];
-    while (1) {
-	long eip_addr;
-	cin.get(buffer, sizeof(buffer));
-	if (cin.eof())
-	    break;
-	cin.get(c);	/* swallow newline */
-#ifdef i386
-	if (strstr(buffer, "EIP:") && names.valid()) {
-	    oops_column =  strstr(buffer, "EIP:");
-	    if (sscanf(oops_column+13, "[<%x>]", &eip_addr) != 1) {
-	    	cout << "Cannot read eip address from EIP: line.  Is this a valid oops file?" << endl;
-		exit(1);
-	    }
-	    cout << ">>EIP: ";
-	    KSym* ksym = names.find(eip_addr);
-	    if (ksym)
-		cout << *ksym << endl;
-	    else
-		cout << ::hex << eip_addr << " cannot be resolved" << endl;
-	}
-#endif
-#ifdef mc68000
-	if (strstr(buffer, "PC:") && names.valid()) {
-	    oops_column =  strstr(buffer, "PC:");
-	    if (sscanf(oops_column+4, "[<%x>]", &eip_addr) != 1) {
-	    	cout << "Cannot read pc address from PC: line.  Is this a valid oops file?" << endl;
-		exit(1);
-	    }
-	    cout << ">>PC: ";
-	    KSym* ksym = names.find(eip_addr);
-	    if (ksym)
-		cout << *ksym << endl;
-	    else
-		cout << ::hex << eip_addr << " cannot be resolved" << endl;
-	}
-#endif
-	else if (oops_column && strstr(oops_column, "[<") && names.valid()) {
-	    unsigned long address;
-	    while (strstr(oops_column, "[<")) {
-		char *p = oops_column;
-		while (1) {
-		    while (*p && *p++ != '[')
-			;
-		    if (sscanf(p, "<%x>]", &address) != 1)
-			break;
-		    cout << "Trace: ";
-		    KSym* ksym = names.find(address);
-		    if (ksym)
-			cout << *ksym;
-		    else
-			cout << ::hex << address;
-		    cout << endl;
-		}
-		cin.get(buffer, sizeof(buffer));
-		if (cin.eof())
-		    break;
-		cin.get(c);	/* swallow newline */
-	    }
-	}
-	if (oops_column && strnequ(oops_column, "Code:", 5)) {
-	    unsigned char code[code_size];
-	    unsigned char* cp = code;
-	    unsigned char* end = &code[code_size];
-	    char *p = oops_column + 5;
-	    int c;
-	    memset(code, '\0', sizeof(code));
-	    while (*p && cp < end) {
-	    	while (*p == ' ')
-		    ++p;
-		if (sscanf(p, "%x", &c) != 1)
-		    break;
-#ifdef mc68000
-		*cp++ = c >> 8;
-#endif
-		*cp++ = c;
-	    	while (*p && *p++ != ' ')
-		    ;
-	    }
-	    names.decode(code, eip_addr);
-	}
-    }
-    cout << flush;
-
-    return 0;
-}
diff -u --recursive --new-file v2.2.0-pre3/linux/scripts/tkcond.c linux/scripts/tkcond.c
--- v2.2.0-pre3/linux/scripts/tkcond.c	Wed May 20 19:10:42 1998
+++ linux/scripts/tkcond.c	Sat Jan  2 10:30:48 1999
@@ -132,8 +132,8 @@
 	    }
 	  else
 	    {
-	      fprintf(stderr,"Ooops\n");
-	      exit(0);
+	      fprintf(stderr,"tkparse can't handle this conditional\n");
+	      exit(1);
 	    }
 	}
 
