Initial checkin of JTE files from package version 1.4 v1.4
authorSteve McIntyre <steve@einval.com>
Sun, 13 Mar 2005 21:46:46 +0000 (21:46 +0000)
committerSteve McIntyre <steve@einval.com>
Sun, 13 Mar 2005 21:46:46 +0000 (21:46 +0000)
Makefile [new file with mode: 0644]
dump-jte.c [new file with mode: 0644]
dump.c [new file with mode: 0644]
endian.c [new file with mode: 0644]
endian.h [new file with mode: 0644]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
mkimage.c [new file with mode: 0644]
mkisofs-JTE-v1.4.gz [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..404ea9d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+BINS = dump dump-jte mkimage 
+CFLAGS = -g -Wall -Werror -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
+CC = gcc
+
+all: $(BINS)
+
+mkimage: mkimage.o endian.o md5.o
+       $(CC) -o $@ $+ -lz
+
+clean:
+       rm -f *.o $(BINS) *~
diff --git a/dump-jte.c b/dump-jte.c
new file mode 100644 (file)
index 0000000..1a186a3
--- /dev/null
@@ -0,0 +1,418 @@
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/sendfile.h>
+
+/* Possible commands to run. Or them together... */
+#define CMD_LIST        0x0001
+#define CMD_BUILD_IMAGE 0x0002
+
+typedef unsigned long long UINT64;
+typedef unsigned long      UINT32;
+
+#define BUF_SIZE 65536
+
+/* Different types used in the extent_type below */
+#define JTET_HEADER     0
+#define JTET_FOOTER     1
+#define JTET_FILE_MATCH 2
+#define JTET_NOMATCH    3
+
+#define JTE_ID_STRING     "JTE"
+#define JTE_HEADER_STRING "MKJ IMAGE START"
+#define JTE_FOOTER_STRING "*MKJ IMAGE END*"
+#define JTE_VER_MAJOR     0x0001
+#define JTE_VER_MINOR     0x0000
+
+struct jt_extent_data
+{
+    unsigned char id[4];                       /* "JTE" plus NULL terminator */
+    unsigned char extent_type;                 /* The type of this extent in the jigdo template file */
+    unsigned char extent_length[8];            /* The length in bytes of this extent, including all
+                                                  the metadata. 64-bit, big endian */
+    unsigned char start_sector[4];             /* The start sector of this extent within the output image;
+                                                  32-bit BE. Header and footer use 0xFFFFFFFF */
+    union
+    {
+        struct
+        {
+            unsigned char header_string[16];   /* Recognition string. Should contain "MKJ IMAGE START",
+                                                  including NULL terminator */
+            unsigned char version[4];          /* Version number, encoded MMmm */
+            unsigned char sector_size[4];      /* Sector size used in this image.
+                                                  _Always_ expected to be 2KB. Stored as 32-bit BE */
+            unsigned char pad[16];
+        } header;
+        struct
+        {
+            unsigned char footer_string[16];   /* Recognition string. Should contain "*MKJ IMAGE END*",
+                                                  including NULL terminator */
+            unsigned char image_size[8];       /* Size of image, in bytes. 64-bit BE. */
+            unsigned char md5[16];             /* MD5SUM of the entire image */
+        } footer;
+        struct
+        {
+            unsigned char file_length[8];      /* The actual length of the file stored in this extent.
+                                                  Will be <= extent_length; also 64-bit BE */
+            unsigned char filename_length[4];  /* The length of the following filename entry */
+            unsigned char md5[16];             /* MD5SUM of the _file_ data in this lump, without padding */
+            unsigned char pad[12];
+        } file_match;
+        struct
+        {
+            unsigned char unmatched_length[8]; /* The length of the data in this extent. Will be == 
+                                                  extent_length - sizeof(struct jt_extent_data) ; also 64-bit BE */
+            unsigned char md5[16];             /* MD5SUM of this lump of unmatched data */
+            unsigned char pad[16];
+        } nomatch;
+    } data;
+};
+
+int verbose = 0;
+int cmd = 0;
+UINT32 sector_size = 0;
+int jte_fd = -1;
+int out_fd = -1;
+
+static char *print_md5(unsigned char *buf)
+{
+    static char outbuf[33];
+
+    bzero(outbuf, sizeof(outbuf));
+    sprintf(outbuf, "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
+            buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+            buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+    return outbuf;
+}
+
+static int my_sendfile(int src_fd, int dst_fd, off_t length)
+{
+    char buf[BUF_SIZE];
+    off_t bytes = 0;
+    off_t bytes_read = 0;
+    off_t bytes_written = 0;
+    off_t size = 0;
+
+    while (bytes < length)
+    {
+        size = length - bytes;
+        if (size > BUF_SIZE)
+            size = BUF_SIZE;
+        
+        bytes_read = read(src_fd, buf, size);
+        if (size != bytes_read)
+        {
+            printf("my_sendfile: FAILED to read %llu bytes from input file; error %d\n", size, errno);
+            return errno;
+        }
+        bytes_written = write(dst_fd, buf, bytes_read);
+        if (bytes_read != bytes_written)
+        {
+            printf("my_sendfile: FAILED to write %llu bytes to output file; error %d\n", bytes_read, errno);
+            return errno;
+        }
+        bytes += bytes_read;
+    }
+    
+    /* Now pad if necessary */
+    if (length % sector_size)
+    {
+        bzero (buf, BUF_SIZE);
+        off_t pad_size = sector_size - (length % sector_size);
+        
+        bytes = write(dst_fd, buf, pad_size);
+        if (-1 == bytes)
+        {
+            printf("my_sendfile: FAILED to pad with %llu bytes; error %d\n", pad_size, errno);
+            return errno;
+        }
+    }
+    
+    return 0;
+}
+
+UINT64 handle_jtet_header(UINT64 extent_length,
+                          UINT32 start_sector, unsigned char *buf)
+{
+    struct jt_extent_data *extent = (struct jt_extent_data *)buf;
+    
+    sector_size = extent->data.header.sector_size[0] << 24;
+    sector_size |= extent->data.header.sector_size[1] << 16;
+    sector_size |= extent->data.header.sector_size[2] << 8;
+    sector_size |= extent->data.header.sector_size[3];
+    
+    if (cmd & CMD_LIST)
+    {
+        printf("  Header string: %s\n", extent->data.header.header_string);
+        printf("  JTE version: %d.%d\n",
+               (extent->data.header.version[0] << 8) | extent->data.header.version[1],
+               (extent->data.header.version[2] << 8) | extent->data.header.version[3]);
+        printf("  Sector size: %ld bytes\n", sector_size);
+    }
+    return extent_length;
+}
+
+UINT64 handle_jtet_footer(UINT64 extent_length,
+                          UINT32 start_sector, unsigned char *buf)
+{
+    struct jt_extent_data *extent = (struct jt_extent_data *)buf;
+    UINT64 image_size = (UINT64)extent->data.footer.image_size[0] << 56;
+    image_size |= (UINT64)extent->data.footer.image_size[1] << 48;
+    image_size |= (UINT64)extent->data.footer.image_size[2] << 40;
+    image_size |= (UINT64)extent->data.footer.image_size[3] << 32;
+    image_size |= (UINT64)extent->data.footer.image_size[4] << 24;
+    image_size |= (UINT64)extent->data.footer.image_size[5] << 16;
+    image_size |= (UINT64)extent->data.footer.image_size[6] << 8;
+    image_size |= (UINT64)extent->data.footer.image_size[7];
+
+    if (cmd & CMD_LIST)
+    {
+        printf("  Footer string: %s\n", extent->data.footer.footer_string);
+        printf("  ISO image size: %llu\n", image_size);
+        printf("  ISO image MD5sum: %s\n", print_md5(extent->data.footer.md5));
+    }
+    return 0;
+}
+
+UINT64 handle_jtet_file_match(UINT64 extent_length,
+                              UINT32 start_sector, unsigned char *buf)
+{
+    int error = 0;
+    int chunk_fd = -1;
+    struct jt_extent_data *extent = (struct jt_extent_data *)buf;
+    char *filename = &buf[57];
+    UINT64 length = 0;
+    int filename_length = 0;
+
+    length |= (UINT64)extent->data.file_match.file_length[0] << 56;
+    length |= (UINT64)extent->data.file_match.file_length[1] << 48;
+    length |= (UINT64)extent->data.file_match.file_length[2] << 40;
+    length |= (UINT64)extent->data.file_match.file_length[3] << 32;
+    length |= (UINT64)extent->data.file_match.file_length[4] << 24;
+    length |= (UINT64)extent->data.file_match.file_length[5] << 16;
+    length |= (UINT64)extent->data.file_match.file_length[6] << 8;
+    length |= (UINT64)extent->data.file_match.file_length[7];
+
+    filename_length |= extent->data.file_match.filename_length[0] << 24;
+    filename_length |= extent->data.file_match.filename_length[1] << 16;
+    filename_length |= extent->data.file_match.filename_length[2] << 8;
+    filename_length |= extent->data.file_match.filename_length[3];
+
+    if (cmd & CMD_LIST)
+    {
+        printf("  File length: %llu\n", length);
+        printf("  Filename len: %d\n", filename_length);
+        printf("  Filename: %s\n", filename);
+        printf("  File MD5sum: %s\n", print_md5(extent->data.file_match.md5));
+    }
+    
+    if (cmd & CMD_BUILD_IMAGE)
+    {
+        chunk_fd = open(filename, O_RDONLY|O_LARGEFILE);
+        if (-1 == chunk_fd)
+        {
+            printf("FAILED to open filename %s, error %d. Aborting\n", filename, errno);
+            return 0;
+        }
+        else
+        {
+            if (verbose)
+                printf("Writing %7llu bytes of %s to output file\n", length, filename);
+            error = my_sendfile(chunk_fd, out_fd, length);
+            close(chunk_fd);
+            if (error)
+            {
+                printf("FAILED to copy contents of %s into output file; error %d. Aborting\n", filename, error);
+                return 0;
+            }
+        }
+    }
+    
+    return extent_length;
+}
+
+UINT64 handle_jtet_no_match(UINT64 extent_length,
+                            UINT32 start_sector, unsigned char *buf)
+{
+    int error = 0;
+    UINT64 length = 0;
+    struct jt_extent_data *extent = (struct jt_extent_data *)buf;
+
+    length |= (UINT64)extent->data.nomatch.unmatched_length[0] << 56;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[1] << 48;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[2] << 40;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[3] << 32;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[4] << 24;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[5] << 16;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[6] << 8;
+    length |= (UINT64)extent->data.nomatch.unmatched_length[7];
+
+    if (cmd & CMD_LIST)
+    {
+        printf("  Unmatched data, length %llu\n", length);
+        printf("  Chunk MD5sum: %s\n", print_md5(extent->data.nomatch.md5));
+    }
+    
+    if (cmd & CMD_BUILD_IMAGE)
+    {
+        /* Seek past the header of this block */
+        lseek(jte_fd, sizeof(struct jt_extent_data), SEEK_CUR);
+        if (verbose)
+            printf("Writing %7llu bytes of unmatched image data to output file\n", length);
+        error = my_sendfile(jte_fd, out_fd, length);
+        if (error)
+        {
+            printf("FAILED to copy unmatched data chunk into output file; error %d. Aborting\n", error);
+            return 0;
+        }
+    }
+    
+    return extent_length;
+}
+
+UINT64 parse_jte_block(UINT64 offset, unsigned char *buf, size_t buf_size)
+{
+    int    extent_type = 0;
+    UINT64 extent_length = 0;
+    UINT32 start_sector = 0;
+    struct jt_extent_data *extent = (struct jt_extent_data *)buf;
+    
+    if (strncmp(extent->id, JTE_ID_STRING, 4))
+    {
+        printf("Error! Didn't find expected JTE block at offset %lld\n", offset);
+        return 0;
+    }
+    if (cmd & CMD_LIST)
+        printf("\nJTE block found at offset %lld\n", offset);
+
+    extent_type = extent->extent_type;
+
+    extent_length |= (UINT64)extent->extent_length[0] << 56;
+    extent_length |= (UINT64)extent->extent_length[1] << 48;
+    extent_length |= (UINT64)extent->extent_length[2] << 40;
+    extent_length |= (UINT64)extent->extent_length[3] << 32;
+    extent_length |= (UINT64)extent->extent_length[4] << 24;
+    extent_length |= (UINT64)extent->extent_length[5] << 16;
+    extent_length |= (UINT64)extent->extent_length[6] << 8;
+    extent_length |= (UINT64)extent->extent_length[7];
+
+    start_sector |= extent->start_sector[0] << 24;
+    start_sector |= extent->start_sector[1] << 16;
+    start_sector |= extent->start_sector[2] << 8;
+    start_sector |= extent->start_sector[3];
+    
+    if (cmd & CMD_LIST)
+        printf("  extent type %d, length %llu, start_sector %ld (out offset %llu)\n",
+               extent_type, extent_length, start_sector, (off_t)start_sector * sector_size);
+
+    switch(extent_type)
+    {
+        case JTET_HEADER:
+            return handle_jtet_header(extent_length, start_sector, buf);
+        case JTET_FOOTER:
+            return handle_jtet_footer(extent_length, start_sector, buf);
+        case JTET_FILE_MATCH:
+            return handle_jtet_file_match(extent_length, start_sector, buf);
+        case JTET_NOMATCH:
+            return handle_jtet_no_match(extent_length, start_sector, buf);
+        default:
+            printf("Awooga! Invalid extent type!\n");
+            return 0;
+    }
+}
+
+int main(int argc, char **argv)
+{
+    char *filename = NULL;
+    char *outfile = NULL;
+    unsigned char *buf = NULL;
+    UINT64 offset = 0;
+    UINT64 bytes = 0;
+    int done = 0;
+    int i = 0;
+    
+    for (i = 1; i < argc; i++)
+    {
+        if (!strcmp(argv[i], "-l"))
+            cmd |= CMD_LIST;
+        else if (!strcmp(argv[i], "-b"))
+            cmd |= CMD_BUILD_IMAGE;
+        else if (!strcmp(argv[i], "-v"))
+            verbose = 1;
+        else if (!strcmp(argv[i], "-o") && i < argc - 1)
+            outfile = argv[++i];
+        else if (!strcmp(argv[i], "-f") && i < argc - 1)
+            filename = argv[++i];
+    }
+
+    if (0 == cmd)
+        cmd = CMD_LIST;
+
+    if (NULL == filename)
+    {
+        printf("No filename specified! Try again...\n");
+        return EINVAL;
+    }
+
+    if ( (cmd & CMD_BUILD_IMAGE) && !outfile)
+    {
+        printf("No output filename given; aborting...\n");
+        return EINVAL;
+    }
+    
+    jte_fd = open(filename, O_RDONLY|O_LARGEFILE);
+    if (-1 == jte_fd)
+    {
+        printf("Failed to open input file %s, error %d!. Try again...\n", filename, errno);
+        return errno;
+    }
+
+    if (outfile)
+    {
+        out_fd = open(outfile, O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, 0644);
+        if (-1 == out_fd)
+        {
+            printf("Failed to open output file %s, error %d!\n", outfile, errno);
+            return errno;
+        }
+    }
+
+    buf = malloc(BUF_SIZE);
+    if (!buf)
+    {
+        printf("Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
+        return ENOMEM;
+    }
+
+    while (!done)
+    {
+        UINT64 start_offset = -1;
+        lseek(jte_fd, offset, SEEK_SET);
+        bytes = read(jte_fd, buf, BUF_SIZE);
+        if (0 >= bytes)
+        {
+            printf("Failed to read! error %d\n", errno);
+            done = 1;
+            break;
+        }
+        lseek(jte_fd, offset, SEEK_SET);
+        start_offset = parse_jte_block(offset, buf, bytes);
+        if (!start_offset)
+            break; /* We're finished! */
+        offset += start_offset;
+    }        
+    
+    close(jte_fd);
+    if (-1 != out_fd)
+        close(out_fd);
+
+    return 0;
+}
diff --git a/dump.c b/dump.c
new file mode 100644 (file)
index 0000000..7d7b610
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,281 @@
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+typedef unsigned long long UINT64;
+typedef unsigned long      UINT32;
+
+#define BUF_SIZE 65536
+
+typedef enum state_
+{
+    STARTING,
+    IN_DATA,
+    IN_DESC,
+    DUMP_DESC,
+    DONE,
+    ERROR
+} e_state;
+
+off_t find_string(unsigned char *buf, size_t buf_size, char *search)
+{
+    size_t length = strlen(search);
+    off_t result;
+    
+    for (result = 0; result < (buf_size - length); result++)
+    {
+        if (!memcmp(&buf[result], search, length))
+            return result;
+    }
+    return -1;
+}
+
+off_t parse_data_block(off_t offset, unsigned char *buf, size_t buf_size)
+{
+    /* Parse the contents of this data block... */
+    UINT64 dataLen = 0;
+    UINT64 dataUnc = 0;
+    
+    printf("\nDATA block found at offset %lld\n", offset);
+    dataLen = (UINT64)buf[4];
+    dataLen |= (UINT64)buf[5] << 8;
+    dataLen |= (UINT64)buf[6] << 16;
+    dataLen |= (UINT64)buf[7] << 24;
+    dataLen |= (UINT64)buf[8] << 32;
+    dataLen |= (UINT64)buf[9] << 40;
+    printf("  compressed block size %llu bytes\n", dataLen);
+
+    dataUnc = (UINT64)buf[10];
+    dataUnc |= (UINT64)buf[11] << 8;
+    dataUnc |= (UINT64)buf[12] << 16;
+    dataUnc |= (UINT64)buf[13] << 24;
+    dataUnc |= (UINT64)buf[14] << 32;
+    dataUnc |= (UINT64)buf[15] << 40;
+    printf("  uncompressed block size %llu bytes\n", dataUnc);
+
+    return dataLen;
+}
+
+void base64_dump(unsigned char *buf, size_t buf_size)
+{
+    const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+    int value = 0;
+    unsigned int i;
+    int bits = 0;
+
+    for (i = 0; i < buf_size ; i++)
+    {
+        value = (value << 8) | buf[i];
+        bits += 2;
+        printf("%c", b64_enc[(value >> bits) & 63U]);
+        if (bits >= 8) {
+            bits -= 6;
+            printf("%c", b64_enc[(value >> bits) & 63U]);
+        }
+    }
+    if (bits > 0)
+    {
+        value <<= 8 - bits;
+        printf("%c", b64_enc[(value >> bits) & 63U]);
+    }
+}
+
+off_t parse_desc_block(off_t offset, unsigned char *buf, size_t buf_size)
+{
+    /* Parse the contents of this data block... */
+    UINT64 descLen = 0;
+    
+    printf("\nDESC block found at offset %lld\n", offset);
+    descLen = (UINT64)buf[4];
+    descLen |= (UINT64)buf[5] << 8;
+    descLen |= (UINT64)buf[6] << 16;
+    descLen |= (UINT64)buf[7] << 24;
+    descLen |= (UINT64)buf[8] << 32;
+    descLen |= (UINT64)buf[9] << 40;
+    printf("  DESC block size is %llu bytes\n", descLen);
+    
+    return 10;
+}
+
+off_t parse_desc_data(off_t offset, unsigned char *buf, size_t buf_size)
+{
+    int type = buf[0];
+    printf("  DESC entry: block type %d\n", type);
+    
+    switch (type)
+    {
+        case 2:
+        {
+            UINT64 skipLen = 0;
+            skipLen = (UINT64)buf[1];
+            skipLen |= (UINT64)buf[2] << 8;
+            skipLen |= (UINT64)buf[3] << 16;
+            skipLen |= (UINT64)buf[4] << 24;
+            skipLen |= (UINT64)buf[5] << 32;
+            skipLen |= (UINT64)buf[6] << 40;
+            printf("    Unmatched data, %llu bytes\n", skipLen);
+            return 7;
+        }
+        case 5:
+        {
+            UINT64 imglen = 0;
+            UINT32 blocklen = 0;
+            int i = 0;
+
+            imglen = (UINT64)buf[1];
+            imglen |= (UINT64)buf[2] << 8;
+            imglen |= (UINT64)buf[3] << 16;
+            imglen |= (UINT64)buf[4] << 24;
+            imglen |= (UINT64)buf[5] << 32;
+            imglen |= (UINT64)buf[6] << 40;
+
+            blocklen = (UINT32)buf[23];
+            blocklen |= (UINT32)buf[24] << 8;
+            blocklen |= (UINT32)buf[25] << 16;
+            blocklen |= (UINT32)buf[26] << 24;
+
+            printf("    Original image length %llu bytes\n", imglen);
+            printf("    Image MD5: ");
+            for (i = 7; i < 23; i++)
+                printf("%2.2x", buf[i]);
+            printf(" (");
+            base64_dump(&buf[7], 16);
+            printf(")");
+            printf("\n    MD5 block length %lu bytes\n", blocklen);
+            return 0; /* i.e. we're finished! */
+        }
+        case 6:
+        {
+            UINT64 fileLen = 0;
+            int i = 0;
+
+            fileLen = (UINT64)buf[1];
+            fileLen |= (UINT64)buf[2] << 8;
+            fileLen |= (UINT64)buf[3] << 16;
+            fileLen |= (UINT64)buf[4] << 24;
+            fileLen |= (UINT64)buf[5] << 32;
+            fileLen |= (UINT64)buf[6] << 40;
+            
+            printf("    File, length %llu bytes\n", fileLen);
+            printf("    file rsyncsum: ");
+            for (i = 7; i < 15; i++)
+                printf("%2.2x", buf[i]);
+            printf("\n    file md5: ");
+            for (i = 15; i < 31; i++)
+                printf("%2.2x", buf[i]);
+            printf(" (");
+            base64_dump(&buf[15], 16);
+            printf(")");
+            printf("\n");
+            return 31;
+        }
+        default:
+            break;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    char *filename = NULL;
+    int fd = -1;
+    unsigned char *buf = NULL;
+    off_t offset = 0;
+    off_t bytes = 0;
+    e_state state = STARTING;
+    
+    if (argc != 2)
+    {
+        printf("No filename specified! Try again...\n");
+        return EINVAL;
+    }
+    
+    filename = argv[1];
+    
+    fd = open(filename, O_RDONLY);
+    if (-1 == fd)
+    {
+        printf("Failed to open file %s, error %d!. Try again...\n", filename, errno);
+        return errno;
+    }
+
+    buf = malloc(BUF_SIZE);
+    if (!buf)
+    {
+        printf("Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
+        return ENOMEM;
+    }
+
+    /* Find the beginning of the data - read the first chunk, including the header */
+    while (STARTING == state)
+    {
+        off_t start_offset = -1;
+
+        bytes = read(fd, buf, BUF_SIZE);
+        if (0 >= bytes)
+        {
+            state = DONE;
+            break;
+        }
+        start_offset = find_string(buf, bytes, "DATA");
+        if (start_offset >= 0)
+        {
+            offset += start_offset;
+            state = IN_DATA;
+            break;
+        }
+        offset += bytes;
+    }
+
+    while (DONE != state && ERROR != state)
+    {
+        off_t start_offset = -1;
+        lseek(fd, offset, SEEK_SET);
+        bytes = read(fd, buf, BUF_SIZE);
+        if (0 >= bytes)
+        {
+            state = ERROR;
+            break;
+        }
+        if (IN_DATA == state)
+        {
+            if (!find_string(buf, bytes, "DATA"))
+                state = IN_DATA;
+            if (!find_string(buf, bytes, "DESC"))
+                state = IN_DESC;
+        }
+        switch (state)
+        {
+            case IN_DATA:
+                start_offset = parse_data_block(offset, buf, bytes);
+                offset += start_offset;
+                break;
+            case IN_DESC:
+                start_offset = parse_desc_block(offset, buf, bytes);
+                offset += start_offset;
+                state = DUMP_DESC;
+                break;
+            case DUMP_DESC:
+                start_offset = parse_desc_data(offset, buf, bytes);
+                offset += start_offset;
+                if (0 == start_offset)
+                    state = DONE;
+                break;
+            default:
+                break;
+        }
+    }        
+    
+    close(fd);
+
+    return 0;
+}
diff --git a/endian.c b/endian.c
new file mode 100644 (file)
index 0000000..eb61a66
--- /dev/null
+++ b/endian.c
@@ -0,0 +1,187 @@
+#include "endian.h"
+
+/* Write a 64-bit quantity out into memory in BIG ENDIAN order */
+void write_be64(in, out)
+    unsigned long long in;
+    unsigned char *out;
+{
+    out[0] = (in >> 56) & 0xFF;
+    out[1] = (in >> 48) & 0xFF;
+    out[2] = (in >> 40) & 0xFF;
+    out[3] = (in >> 32) & 0xFF;
+    out[4] = (in >> 24) & 0xFF;
+    out[5] = (in >> 16) & 0xFF;
+    out[6] = (in >> 8) & 0xFF;
+    out[7] = in & 0xFF;
+}
+
+/* Read in a 64-bit BIG ENDIAN quantity */
+unsigned long long read_be64(in)
+    unsigned char *in;
+{
+    unsigned long long result = 0;
+
+    result |= (unsigned long long)in[0] << 56;
+    result |= (unsigned long long)in[1] << 48;
+    result |= (unsigned long long)in[2] << 40;
+    result |= (unsigned long long)in[3] << 32;
+    result |= (unsigned long long)in[4] << 24;
+    result |= (unsigned long long)in[5] << 16;
+    result |= (unsigned long long)in[6] << 8;
+    result |= (unsigned long long)in[7];
+    
+    return result;
+}
+
+/* Write a 64-bit quantity out into memory in LITTLE ENDIAN order */
+void write_le64(in, out)
+    unsigned long long in;
+    unsigned char *out;
+{
+    out[0] = in & 0xFF;
+    out[1] = (in >> 8) & 0xFF;
+    out[2] = (in >> 16) & 0xFF;
+    out[3] = (in >> 24) & 0xFF;
+    out[4] = (in >> 32) & 0xFF;
+    out[5] = (in >> 40) & 0xFF;
+    out[6] = (in >> 48) & 0xFF;
+    out[7] = (in >> 56) & 0xFF;
+}
+
+/* Read in a 64-bit LITTLE ENDIAN quantity */
+unsigned long long read_le64(in)
+    unsigned char *in;
+{
+    unsigned long long result = 0;
+
+    result |= (unsigned long long)in[0];
+    result |= (unsigned long long)in[1] << 8;
+    result |= (unsigned long long)in[2] << 16;
+    result |= (unsigned long long)in[3] << 24;
+    result |= (unsigned long long)in[4] << 32;
+    result |= (unsigned long long)in[5] << 40;
+    result |= (unsigned long long)in[6] << 48;
+    result |= (unsigned long long)in[7] << 56;
+    
+    return result;
+}
+
+/* Write a 48-bit quantity out into memory in LITTLE ENDIAN order */
+void write_le48(in, out)
+    unsigned long long in;
+    unsigned char *out;
+{
+    out[0] = in & 0xFF;
+    out[1] = (in >> 8) & 0xFF;
+    out[2] = (in >> 16) & 0xFF;
+    out[3] = (in >> 24) & 0xFF;
+    out[4] = (in >> 32) & 0xFF;
+    out[5] = (in >> 40) & 0xFF;
+}
+
+/* Read in a 48-bit LITTLE ENDIAN quantity */
+unsigned long long read_le48(in)
+    unsigned char *in;
+{
+    unsigned long long result = 0;
+
+    result |= (unsigned long long)in[0];
+    result |= (unsigned long long)in[1] << 8;
+    result |= (unsigned long long)in[2] << 16;
+    result |= (unsigned long long)in[3] << 24;
+    result |= (unsigned long long)in[4] << 32;
+    result |= (unsigned long long)in[5] << 40;
+    
+    return result;
+}
+
+/* Write a 32-bit quantity out into memory in BIG ENDIAN order */
+void write_be32(in, out)
+    unsigned long in;
+    unsigned char *out;
+{
+    out[0] = (in >> 24) & 0xFF;
+    out[1] = (in >> 16) & 0xFF;
+    out[2] = (in >> 8) & 0xFF;
+    out[3] = in & 0xFF;
+}
+
+/* Read in a 32-bit BIG ENDIAN quantity */
+unsigned long read_be32(in)
+    unsigned char *in;
+{
+    unsigned long result = 0;
+
+    result |= (unsigned long)in[0] << 24;
+    result |= (unsigned long)in[1] << 16;
+    result |= (unsigned long)in[2] << 8;
+    result |= (unsigned long)in[3];
+    
+    return result;
+}
+
+/* Write a 32-bit quantity out into memory in LITTLE ENDIAN order */
+void write_le32(in, out)
+    unsigned long in;
+    unsigned char *out;
+{
+    out[0] = in & 0xFF;
+    out[1] = (in >> 8) & 0xFF;
+    out[2] = (in >> 16) & 0xFF;
+    out[3] = (in >> 24) & 0xFF;
+}
+
+/* Read in a 32-bit LITTLE ENDIAN quantity */
+unsigned long read_le32(in)
+    unsigned char *in;
+{
+    unsigned long result = 0;
+
+    result |= (unsigned long)in[0];
+    result |= (unsigned long)in[1] << 8;
+    result |= (unsigned long)in[2] << 16;
+    result |= (unsigned long)in[3] << 24;
+    
+    return result;
+}
+
+/* Write a 16-bit quantity out into memory in BIG ENDIAN order */
+void write_be16(in, out)
+    unsigned short in;
+    unsigned char *out;
+{
+    out[0] = (in >> 8) & 0xFF;
+    out[1] = in & 0xFF;
+}
+    
+/* Read in a 16-bit BIG ENDIAN quantity */
+unsigned short read_be16(in)
+    unsigned char *in;
+{
+    unsigned short result = 0;
+    
+    result |= (unsigned short)in[0] << 8;
+    result |= (unsigned short)in[1];
+    return result;
+}
+
+/* Write a 16-bit quantity out into memory in LITTLE ENDIAN order */
+void write_le16(in, out)
+    unsigned short in;
+    unsigned char *out;
+{
+    out[0] = in & 0xFF;
+    out[1] = in & 0xFF >> 8;
+}
+    
+/* Read in a 16-bit LITTLE ENDIAN quantity */
+unsigned short read_le16(in)
+    unsigned char *in;
+{
+    unsigned short result = 0;
+    
+    result |= (unsigned short)in[0];
+    result |= (unsigned short)in[1] << 8;
+    return result;
+}
+
diff --git a/endian.h b/endian.h
new file mode 100644 (file)
index 0000000..97017ab
--- /dev/null
+++ b/endian.h
@@ -0,0 +1,17 @@
+void                  write_be64  (unsigned long long in, unsigned char *out);
+unsigned long long    read_be64   (unsigned char *in);
+void                  write_le64  (unsigned long long in, unsigned char *out);
+unsigned long long    read_le64   (unsigned char *in);
+
+void                  write_le48  (unsigned long long in, unsigned char *out);
+unsigned long long    read_le48   (unsigned char *in);
+
+void                  write_be32  (unsigned long in, unsigned char *out);
+unsigned long         read_be32   (unsigned char *in);
+void                  write_le32  (unsigned long in, unsigned char *out);
+unsigned long         read_le32   (unsigned char *in);
+
+void                  write_be16  (unsigned short in, unsigned char *out);
+unsigned short        read_be16   (unsigned char *in);
+void                  write_le16  (unsigned short in, unsigned char *out);
+unsigned short        read_le16   (unsigned char *in);
diff --git a/md5.c b/md5.c
new file mode 100644 (file)
index 0000000..ee01182
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,326 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
+   not require an integer type which is exactly 32 bits.  This work
+   draws on the changes for the same purpose by Tatu Ylonen
+   <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
+   that code, there is no copyright issue.  I hereby disclaim
+   copyright in any changes I have made; this code remains in the
+   public domain.  */
+
+/* Note regarding cvs_* namespace: this avoids potential conflicts
+   with libraries such as some versions of Kerberos.  No particular
+   need to worry about whether the system supplies an MD5 library, as
+   this file is only about 3k of object code.  */
+
+/* Steve McIntyre, 2004/05/31: borrowed this code from the CVS
+   library. s/cvs_/mk_/ across the source */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>    /* for memcpy() and memset() */
+
+#include "md5.h"
+
+/* Little-endian byte-swapping routines.  Note that these do not
+   depend on the size of datatypes such as mk_uint32, nor do they require
+   us to detect the endianness of the machine we are running on.  It
+   is possible they should be macros for speed, but I would be
+   surprised if they were a performance bottleneck for MD5.  */
+
+static mk_uint32
+getu32 (addr)
+     const unsigned char *addr;
+{
+       return (((((unsigned long)addr[3] << 8) | addr[2]) << 8)
+               | addr[1]) << 8 | addr[0];
+}
+
+static void
+putu32 (data, addr)
+     mk_uint32 data;
+     unsigned char *addr;
+{
+       addr[0] = (unsigned char)data;
+       addr[1] = (unsigned char)(data >> 8);
+       addr[2] = (unsigned char)(data >> 16);
+       addr[3] = (unsigned char)(data >> 24);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+mk_MD5Init (ctx)
+     struct mk_MD5Context *ctx;
+{
+       ctx->buf[0] = 0x67452301;
+       ctx->buf[1] = 0xefcdab89;
+       ctx->buf[2] = 0x98badcfe;
+       ctx->buf[3] = 0x10325476;
+
+       ctx->bits[0] = 0;
+       ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+mk_MD5Update (ctx, buf, len)
+     struct mk_MD5Context *ctx;
+     unsigned char const *buf;
+     unsigned len;
+{
+       mk_uint32 t;
+
+       /* Update bitcount */
+
+       t = ctx->bits[0];
+       if ((ctx->bits[0] = (t + ((mk_uint32)len << 3)) & 0xffffffff) < t)
+               ctx->bits[1]++; /* Carry from low to high */
+       ctx->bits[1] += len >> 29;
+
+       t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
+
+       /* Handle any leading odd-sized chunks */
+
+       if ( t ) {
+               unsigned char *p = ctx->in + t;
+
+               t = 64-t;
+               if (len < t) {
+                       memcpy(p, buf, len);
+                       return;
+               }
+               memcpy(p, buf, t);
+               mk_MD5Transform (ctx->buf, ctx->in);
+               buf += t;
+               len -= t;
+       }
+
+       /* Process data in 64-byte chunks */
+
+       while (len >= 64) {
+               memcpy(ctx->in, buf, 64);
+               mk_MD5Transform (ctx->buf, ctx->in);
+               buf += 64;
+               len -= 64;
+       }
+
+       /* Handle any remaining bytes of data. */
+
+       memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+mk_MD5Final (digest, ctx)
+     unsigned char digest[16];
+     struct mk_MD5Context *ctx;
+{
+       unsigned count;
+       unsigned char *p;
+
+       /* Compute number of bytes mod 64 */
+       count = (ctx->bits[0] >> 3) & 0x3F;
+
+       /* Set the first char of padding to 0x80.  This is safe since there is
+          always at least one byte free */
+       p = ctx->in + count;
+       *p++ = 0x80;
+
+       /* Bytes of padding needed to make 64 bytes */
+       count = 64 - 1 - count;
+
+       /* Pad out to 56 mod 64 */
+       if (count < 8) {
+               /* Two lots of padding:  Pad the first block to 64 bytes */
+               memset(p, 0, count);
+               mk_MD5Transform (ctx->buf, ctx->in);
+
+               /* Now fill the next block with 56 bytes */
+               memset(ctx->in, 0, 56);
+       } else {
+               /* Pad block to 56 bytes */
+               memset(p, 0, count-8);
+       }
+
+       /* Append length in bits and transform */
+       putu32(ctx->bits[0], ctx->in + 56);
+       putu32(ctx->bits[1], ctx->in + 60);
+
+       mk_MD5Transform (ctx->buf, ctx->in);
+       putu32(ctx->buf[0], digest);
+       putu32(ctx->buf[1], digest + 4);
+       putu32(ctx->buf[2], digest + 8);
+       putu32(ctx->buf[3], digest + 12);
+       memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+mk_MD5Transform (buf, inraw)
+     mk_uint32 buf[4];
+     const unsigned char inraw[64];
+{
+       register mk_uint32 a, b, c, d;
+       mk_uint32 in[16];
+       int i;
+
+       for (i = 0; i < 16; ++i)
+               in[i] = getu32 (inraw + 4 * i);
+
+       a = buf[0];
+       b = buf[1];
+       c = buf[2];
+       d = buf[3];
+
+       MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
+       MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+       MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+       MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+       MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
+       MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+       MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+       MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+       MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
+       MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+       MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+       MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+       MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
+       MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+       MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+       MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+       MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
+       MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
+       MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+       MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+       MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
+       MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
+       MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+       MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+       MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
+       MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
+       MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+       MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+       MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
+       MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
+       MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+       MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+       MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
+       MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+       MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+       MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+       MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
+       MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+       MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+       MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+       MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
+       MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+       MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+       MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+       MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
+       MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+       MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+       MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+       MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
+       MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+       MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+       MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+       MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
+       MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+       MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+       MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+       MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
+       MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+       MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+       MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+       MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
+       MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+       MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+       MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+#endif
+
+#ifdef TEST
+/* Simple test program.  Can use it to manually run the tests from
+   RFC1321 for example.  */
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+       struct mk_MD5Context context;
+       unsigned char checksum[16];
+       int i;
+       int j;
+
+       if (argc < 2)
+       {
+               fprintf (stderr, "usage: %s string-to-hash\n", argv[0]);
+               exit (1);
+       }
+       for (j = 1; j < argc; ++j)
+       {
+               printf ("MD5 (\"%s\") = ", argv[j]);
+               mk_MD5Init (&context);
+               mk_MD5Update (&context, argv[j], strlen (argv[j]));
+               mk_MD5Final (checksum, &context);
+               for (i = 0; i < 16; i++)
+               {
+                       printf ("%02x", (unsigned int) checksum[i]);
+               }
+               printf ("\n");
+       }
+       return 0;
+}
+#endif /* TEST */
diff --git a/md5.h b/md5.h
new file mode 100644 (file)
index 0000000..6c57445
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,26 @@
+/* See md5.c for explanation and copyright information.  */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Unlike previous versions of this code, uint32 need not be exactly
+   32 bits, merely 32 bits or more.  Choosing a data type which is 32
+   bits instead of 64 is not important; speed is considerably more
+   important.  ANSI guarantees that "unsigned long" will be big enough,
+   and always using it seems to have few disadvantages.  */
+typedef unsigned long mk_uint32;
+
+struct mk_MD5Context {
+       mk_uint32 buf[4];
+       mk_uint32 bits[2];
+       unsigned char in[64];
+};
+
+void mk_MD5Init (struct mk_MD5Context *context);
+void mk_MD5Update (struct mk_MD5Context *context,
+                          unsigned char const *buf, unsigned len);
+void mk_MD5Final (unsigned char digest[16],
+                         struct mk_MD5Context *context);
+void mk_MD5Transform (mk_uint32 buf[4], const unsigned char in[64]);
+
+#endif /* !MD5_H */
diff --git a/mkimage.c b/mkimage.c
new file mode 100644 (file)
index 0000000..f88efa3
--- /dev/null
+++ b/mkimage.c
@@ -0,0 +1,779 @@
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include "endian.h"
+#include "md5.h"
+
+typedef unsigned long long UINT64;
+typedef unsigned long      UINT32;
+
+#define BUF_SIZE 65536
+
+#ifndef MIN
+#define MIN(x,y)        ( ((x) < (y)) ? (x) : (y))
+#endif
+
+FILE *logfile = NULL;
+int quick = 0;
+
+typedef enum state_
+{
+    STARTING,
+    IN_DATA,
+    IN_DESC,
+    DUMP_DESC,
+    DONE,
+    ERROR
+} e_state;
+
+typedef struct match_list_
+{
+    struct match_list_ *next;
+    char *match;
+    char *mirror_path;
+} match_list_t;
+
+match_list_t *match_list_head = NULL;
+match_list_t *match_list_tail = NULL;
+
+typedef struct jigdo_list_
+{
+    struct jigdo_list_ *next;
+    off_t file_size;
+    char *md5;
+    char *full_path;
+} jigdo_list_t;
+
+jigdo_list_t *jigdo_list_head = NULL;
+jigdo_list_t *jigdo_list_tail = NULL;
+
+struct
+{
+    char   *data_buf;
+    size_t  buf_size;
+    off_t   curr_offset;
+} zip_state;
+
+static int add_match_entry(char *match)
+{
+    match_list_t *entry = NULL;
+    char *mirror_path = NULL;
+    char *ptr = match;
+
+    /* Split "Foo=/mirror/foo" into its components */
+    while (*ptr)
+    {
+        if ('=' == *ptr)
+        {
+            *ptr = 0;
+            ptr++;
+            mirror_path = ptr;
+            break;
+        }
+        ptr++;
+    }
+
+    if (!mirror_path)
+    {
+        fprintf(logfile, "Could not parse malformed match entry \"%s\"\n", match);
+        return EINVAL;
+    }        
+    
+    entry = calloc(1, sizeof(*entry));
+    if (!entry)
+        return ENOMEM;
+
+    fprintf(logfile, "Adding match entry %s:%s\n", match, mirror_path);
+
+    entry->match = match;
+    entry->mirror_path = mirror_path;
+    
+    if (!match_list_head)
+    {
+        match_list_head = entry;
+        match_list_tail = entry;
+    }
+    else
+    {
+        match_list_tail->next = entry;
+        match_list_tail = entry;
+    }
+    
+    return 0;
+}
+
+static int file_exists(char *path, off_t *size)
+{
+    struct stat sb;
+    int error = 0;
+    
+    error = stat(path, &sb);
+    if (!error && S_ISREG(sb.st_mode))
+    {
+        *size = sb.st_size;
+        return 1;
+    }
+    
+    /* else */
+    return 0;
+}
+
+static char *base64_dump(unsigned char *buf, size_t buf_size)
+{
+    const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+    int value = 0;
+    unsigned int i;
+    int bits = 0;
+    char *out = calloc(1, 30);
+    unsigned int out_pos = 0;
+
+    if (!out)
+        return NULL;
+
+    for (i = 0; i < buf_size ; i++)
+    {
+        value = (value << 8) | buf[i];
+        bits += 2;
+        out[out_pos++] = b64_enc[(value >> bits) & 63U];
+        if (bits >= 8) {
+            bits -= 6;
+            out[out_pos++] = b64_enc[(value >> bits) & 63U];
+        }
+    }
+    if (bits > 0)
+    {
+        value <<= 8 - bits;
+        out[out_pos++] = b64_enc[(value >> bits) & 63U];
+    }
+    return out;
+}
+
+static int find_file_in_mirror(char *jigdo_entry, char **mirror_path, char **md5sum, off_t *file_size)
+{
+    match_list_t *entry = match_list_head;
+    char path[PATH_MAX];
+    char *jigdo_name = NULL;
+    char *match = NULL;
+    char *ptr = jigdo_entry;
+
+    *md5sum = jigdo_entry;
+
+    /* Grab out the component strings from the entry in the jigdo file */
+    while (0 != *ptr)
+    {
+        if ('=' == *ptr)
+        {
+            *ptr = 0;
+            ptr++;
+            match = ptr;
+        }
+        else if (':' == *ptr)
+        {
+            *ptr = 0;
+            ptr++;
+            jigdo_name = ptr;
+        }
+        else if ('\n' == *ptr)
+            *ptr = 0;
+        else
+            ptr++;
+    }
+
+    if (NULL == match || NULL == jigdo_name)
+    {
+        fprintf(logfile, "Could not parse malformed jigdo entry \"%s\"\n", jigdo_entry);
+        return EINVAL;
+    }
+
+    while (entry)
+    {
+        if (!strcmp(entry->match, match))
+        {
+            sprintf(path, "%s/%s", entry->mirror_path, jigdo_name);
+            if (file_exists(path, file_size))
+            {
+                *mirror_path = strdup(path);
+                return 0;
+            }
+        }
+        entry = entry->next;
+    }
+    
+    fprintf(logfile, "Could not find file %s:%s in any path\n", match, jigdo_name);
+    return ENOENT;
+}
+
+/* DELIBERATELY do not sort these, or do anything clever with
+   insertion. The entries in the jigdo file should be in the same
+   order as the ones we'll want from the template. Simply add to the
+   end of the singly-linked list each time! */
+static int add_file_entry(char *jigdo_entry)
+{
+    int error = 0;
+    char *file_name = NULL;
+    char *md5 = NULL;
+    jigdo_list_t *new = NULL;
+    off_t file_size = 0;
+    
+    error = find_file_in_mirror(jigdo_entry, &file_name, &md5, &file_size);
+    if (error)
+        return error;
+    
+    new = calloc(1, sizeof(*new));
+    if (!new)
+        return ENOMEM;
+
+    new->md5 = md5;
+    new->full_path = file_name;
+    new->file_size = file_size;
+    
+    if (!jigdo_list_head)
+    {
+        jigdo_list_head = new;
+        jigdo_list_tail = new;
+    }
+    else
+    {
+        jigdo_list_tail->next = new;
+        jigdo_list_tail = new;
+    }
+    
+    return 0;
+}
+
+static int parse_jigdo_file(char *filename)
+{
+    unsigned char buf[2048];
+    FILE *file = NULL;
+    char *ret = NULL;
+    int error = 0;
+    
+    file = fopen(filename, "rb");
+    if (!file)
+    {
+        fprintf(logfile, "Failed to open jigdo file %s, error %d!\n", filename, errno);
+        return errno;
+    }
+
+    /* Find the [Parts] section of the jigdo file */
+    while (1)
+    {
+        ret = fgets(buf, sizeof(buf), file);
+        if (NULL == ret)
+            break;
+        if (!strncmp(buf, "[Parts]", 7))
+            break;
+    }
+
+    /* Now grab the individual file entries and build a list */
+    while (1)
+    {
+        ret = fgets(buf, sizeof(buf), file);
+        if (NULL == ret || !strcmp(buf, "\n"))
+            break;
+        if (!strcmp(buf, "[") || !strcmp(buf, "#"))
+            continue;
+        error = add_file_entry(strdup(buf));
+        if (error)
+            return error;
+    }
+    
+    return 0;
+}
+
+static off_t get_file_size(char *filename)
+{
+    struct stat sb;
+    int error = 0;
+    
+    error = stat(filename, &sb);
+    if (error)
+        return -1;
+    else
+        return sb.st_size;
+}
+
+static int decompress_data_block(char *in_buf, size_t in_len, char *out_buf, size_t out_len)
+{
+    int error = 0;
+    z_stream uc_stream;
+    
+    uc_stream.zalloc = NULL;
+    uc_stream.zfree = NULL;
+    uc_stream.opaque = NULL;
+    uc_stream.next_in = in_buf;
+    uc_stream.avail_in = in_len;
+
+    error = inflateInit(&uc_stream);
+    if (Z_OK != error)
+    {
+        fprintf(logfile, "decompress_data_block: failed to init, error %d\n", error);
+        return EIO;
+    }
+    
+    uc_stream.next_out = out_buf;
+    uc_stream.avail_out = out_len;
+
+    error = inflate(&uc_stream, Z_FINISH);
+    if (Z_OK != error && Z_STREAM_END != error)
+    {
+        fprintf(logfile, "decompress_data_block: failed to decompress, error %d\n", error);
+        return EIO;
+    }
+    
+    error = inflateEnd(&uc_stream);
+    if (Z_OK != error)
+    {
+        fprintf(logfile, "decompress_data_block: failed to end, error %d\n", error);
+        return EIO;
+    }
+    
+    return 0;
+}    
+
+static int read_data_block(FILE *template_file)
+{
+    char inbuf[1024];
+    off_t i = 0;
+    static off_t template_offset = -1;
+    off_t compressed_len = 0;
+    off_t uncompressed_len = 0;
+    char *comp_buf = NULL;
+    int read_num = 0;
+    int error = 0;
+
+    if (-1 == template_offset)
+    {
+        fseek(template_file, 0, SEEK_SET);
+        fread(inbuf, sizeof(inbuf), 1, template_file);
+        for (i = 0; i < sizeof(inbuf); i++)
+        {
+            if (!strncmp(&inbuf[i], "DATA", 4))
+            {
+                template_offset = i;
+                break;
+            }
+        }
+        if (-1 == template_offset)
+        {
+            fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
+                    template_offset);
+            return EINVAL;
+        }    
+    }
+    
+    fseek(template_file, template_offset, SEEK_SET);
+    fread(inbuf, 16, 1, template_file);
+    if (strncmp(inbuf, "DATA", 4))
+    {
+        fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
+                template_offset);
+        return EINVAL;
+    }    
+    
+    compressed_len = read_le48(&inbuf[4]);
+    uncompressed_len = read_le48(&inbuf[10]);
+
+    comp_buf = calloc(1, compressed_len);
+    if (!comp_buf)
+    {
+        fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
+                template_offset);
+        return ENOMEM;
+    }
+    
+    zip_state.data_buf = calloc(1, uncompressed_len);
+    if (!zip_state.data_buf)
+    {
+        fprintf(logfile, "Unable to allocate %lld bytes for decompression\n",
+                uncompressed_len);
+        return ENOMEM;
+    }
+
+    read_num = fread(comp_buf, compressed_len, 1, template_file);
+    if (0 == read_num)
+    {
+        fprintf(logfile, "Unable to read %lld bytes for decompression\n",
+                uncompressed_len);
+        return EIO;
+    }
+
+    error = decompress_data_block(comp_buf, compressed_len,
+                                  zip_state.data_buf, uncompressed_len);
+    if (error)
+    {
+        fprintf(logfile, "Unable to decompress data block, error %d\n", error);
+        return error;
+    }
+        
+    template_offset += compressed_len;
+    zip_state.buf_size = uncompressed_len;
+    zip_state.curr_offset = 0;
+    free (comp_buf);
+    return 0;
+}
+
+static int parse_data_block(size_t data_size, FILE *template_file, struct mk_MD5Context *context)
+{
+    int error = 0;
+    size_t remaining = data_size;
+    size_t size = 0;
+
+    while (remaining)
+    {
+        if (!zip_state.data_buf)
+        {
+            error = read_data_block(template_file);
+            if (error)
+            {
+                fprintf(logfile, "Unable to decompress template data, error %d\n",
+                        error);
+                return error;
+            }
+        }
+        size = MIN((zip_state.buf_size - zip_state.curr_offset), remaining);
+        fwrite(&zip_state.data_buf[zip_state.curr_offset], 1, size, stdout);
+        if (!quick)
+            mk_MD5Update(context, &zip_state.data_buf[zip_state.curr_offset], size);
+        zip_state.curr_offset += size;
+        remaining -= size;
+        
+        if (zip_state.curr_offset == zip_state.buf_size)
+        {
+            free(zip_state.data_buf);
+            zip_state.data_buf = NULL;
+        }
+    }
+    
+    fprintf(logfile, "parse_data_block: wrote %d bytes of unmatched data\n", data_size);
+    return error;
+}
+
+static int parse_file_block(off_t file_size, char *md5, struct mk_MD5Context *image_context)
+{
+    char *base64_md5 = base64_dump(md5, 16);
+    FILE *input_file = NULL;
+    char buf[BUF_SIZE];
+    size_t remaining = file_size;
+    int num_read = 0;
+    struct mk_MD5Context file_context;
+    char file_md5[16];
+    jigdo_list_t *jigdo_list_current = jigdo_list_head;
+    
+    if (!quick)
+        mk_MD5Init(&file_context);
+
+    while (jigdo_list_current)
+    {        
+        if ( (jigdo_list_current->file_size == file_size) &&
+             (!memcmp(jigdo_list_current->md5, base64_md5, 16) ) )
+        {
+            input_file = fopen(jigdo_list_current->full_path, "rb");
+            if (!input_file)
+            {
+                fprintf(logfile, "Unable to open mirror file %s, error %d\n",
+                        jigdo_list_current->full_path, errno);
+                return errno;
+            }
+            while (remaining)
+            {
+                int size = MIN(BUF_SIZE, remaining);
+                memset(buf, 0, BUF_SIZE);
+
+                num_read = fread(buf, size, 1, input_file);
+                if (!num_read)
+                {
+                    fprintf(logfile, "Unable to open mirror file %s, error %d\n",
+                            jigdo_list_current->full_path, errno);
+                    fclose(input_file);
+                    return errno;
+                }
+                if (!quick)
+                {
+                    mk_MD5Update(image_context, buf, size);
+                    mk_MD5Update(&file_context, buf, size);
+                }
+            
+                fwrite(buf, size, 1, stdout);
+            
+                remaining -= size;
+            }
+            fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
+                    file_size, jigdo_list_current->full_path);
+            fclose(input_file);
+
+            if (!quick)
+            {
+                mk_MD5Final(file_md5, &file_context);
+        
+                if (memcmp(file_md5, md5, 16))
+                {
+                    fprintf(logfile, "MD5 MISMATCH for file %s\n", jigdo_list_current->full_path);
+                    fprintf(logfile, "    template looking for %s\n", md5);
+                    fprintf(logfile, "    file in mirror is    %s\n", file_md5);
+                    return EINVAL;
+                }
+            }
+            return 0;
+        }
+        jigdo_list_current = jigdo_list_current->next;
+    }
+    return ENOENT;
+}
+
+static int parse_template_file(char *filename)
+{
+    off_t offset = 0;
+    off_t bytes = 0;
+    unsigned char *buf = NULL;
+    FILE *file = NULL;
+    off_t file_size = 0;
+    off_t desc_start = 0;
+    off_t image_length = 0;
+    off_t written_length = 0;
+    int i = 0;
+    int error = 0;
+    struct mk_MD5Context template_context;
+    unsigned char image_md5sum[16];
+    
+    file = fopen(filename, "rb");
+    if (!file)
+    {
+        fprintf(logfile, "Failed to open template file %s, error %d!\n", filename, errno);
+        return errno;
+    }
+
+    buf = malloc(BUF_SIZE);
+    if (!buf)
+    {
+        fprintf(logfile, "Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
+        fclose(file);
+        return ENOMEM;
+    }
+
+    /* Find the beginning of the desc block */
+    file_size = get_file_size(filename);
+    fseek(file, file_size - 6, SEEK_SET);
+    fread(buf, 6, 1, file);
+    desc_start = file_size - read_le48(buf);
+
+    /* Now seek back to the beginning image desc block to grab the MD5
+       and image length */
+    fseek(file, file_size - 33, SEEK_SET);
+    fread(buf, BUF_SIZE, 1, file);
+    if (buf[0] != 5) /* image data */
+    {
+        fprintf(logfile, "Failed to find image desc in the template file\n");
+        fclose(file);
+        return EINVAL;
+    }
+    image_length = read_le48(&buf[1]);
+    fprintf(logfile, "Image is %lld bytes\n", image_length);
+    fprintf(logfile, "Image MD5 is ");
+    for (i = 0; i < 16; i++)
+        fprintf(logfile, "%2.2x", buf[i+7]);
+    fprintf(logfile, "\n");
+
+    /* Now seek back to the start of the desc block and start
+       assembling the image */
+    fseek(file, desc_start, SEEK_SET);
+    fread(buf, 10, 1, file);
+    if (strncmp(buf, "DESC", 4))
+    {
+        fprintf(logfile, "Failed to find desc start in the template file\n");
+        fclose(file);
+        return EINVAL;
+    }
+    if ((file_size - desc_start) != read_le48(&buf[4]))
+    {
+        fprintf(logfile, "Inconsistent desc length in the template file!\n");
+        fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
+                file_size - desc_start, read_le48(&buf[4]));
+        fclose(file);
+        return EINVAL;
+    }
+
+    if (!quick)
+        mk_MD5Init(&template_context);
+    offset = desc_start + 10;
+    while (1)
+    {
+        if (offset >= (file_size - 33))
+            break; /* Finished! */
+
+        fseek(file, offset, SEEK_SET);
+        bytes = fread(buf, (MIN (BUF_SIZE, file_size - offset)), 1, file);
+        if (1 != bytes)
+        {
+            fprintf(logfile, "Failed to read template file!\n");
+            fclose(file);
+            return EINVAL;
+        }
+        
+        switch (buf[0])
+        {
+            case 2: /* unmatched data */
+                error = parse_data_block(read_le48(&buf[1]), file, &template_context);
+                if (error)
+                {
+                    fprintf(logfile, "Unable to read data block, error %d\n", error);
+                    fclose(file);
+                    return error;
+                }
+                offset += 7;
+                written_length += read_le48(&buf[1]);
+                break;
+            case 6:
+                error = parse_file_block(read_le48(&buf[1]), &buf[15], &template_context);
+                if (error)
+                {
+                    fprintf(logfile, "Unable to read file block, error %d\n", error);
+                    fclose(file);
+                    return error;
+                }
+                offset += 31;
+                written_length += read_le48(&buf[1]);
+                break;
+            default:
+                fprintf(logfile, "Unknown block type %d!\n", buf[0]);
+                fclose(file);
+                return EINVAL;
+        }
+    }
+    
+    fclose(file);
+    if (!quick)
+    {
+        mk_MD5Final (image_md5sum, &template_context);
+        fprintf(logfile, "Output image MD5 is ");
+        for (i = 0; i < 16; i++)
+            fprintf(logfile, "%2.2x", image_md5sum[i]);
+        fprintf(logfile, "\n");
+    }
+    fprintf(logfile, "Output image length is %lld\n", written_length);
+
+    return 0;
+}
+
+static void usage(char *progname)
+{
+    printf("%s [OPTIONS]\n\n", progname);
+    printf(" Options:\n");
+    printf(" -j <jigdo name>     Specify the input jigdo file\n");
+    printf(" -t <template name>  Specify the input template file\n");
+    printf(" -m <item=path>      Map <item> to <path> to find the files in the mirror\n");
+    printf(" -l <logfile>        Specify a logfile to append to.\n");
+    printf("                     If not specified, will log to stderr\n");
+    printf(" -q                  Quick mode. Don't check MD5sums. Dangerous!\n");
+}
+
+int main(int argc, char **argv)
+{
+    char *template_filename = NULL;
+    char *jigdo_filename = NULL;
+    int c = -1;
+    int error = 0;
+
+    logfile = stderr;
+
+    bzero(&zip_state, sizeof(zip_state));
+
+    while(1)
+    {
+        c = getopt(argc, argv, ":ql:j:t:m:h?");
+        if (-1 == c)
+            break;
+        
+        switch(c)
+        {
+            case 'q':
+                quick = 1;
+                break;
+            case 'l':
+                logfile = fopen(optarg, "wb");
+                if (!logfile)
+                {
+                    fprintf(logfile, "Unable to open log file %s\n", optarg);
+                    return errno;
+                }
+                break;
+            case 'j':
+                if (jigdo_filename)
+                {
+                    fprintf(logfile, "Can only specify one jigdo file!\n");
+                    return EINVAL;
+                }
+                /* else */
+                jigdo_filename = optarg;
+                break;
+            case 't':
+                if (template_filename)
+                {
+                    fprintf(logfile, "Can only specify one template file!\n");
+                    return EINVAL;
+                }
+                /* else */
+                template_filename = optarg;
+                break;
+            case 'm':
+                error = add_match_entry(strdup(optarg));
+                if (error)
+                    return error;
+                break;
+            case ':':
+                fprintf(logfile, "Missing argument!\n");
+                return EINVAL;
+                break;
+            case 'h':
+            case '?':
+                usage(argv[0]);
+                return 0;
+                break;
+            default:
+                fprintf(logfile, "Unknown option!\n");
+                return EINVAL;
+        }
+    }
+
+    if (NULL == jigdo_filename)
+    {
+        fprintf(logfile, "No jigdo file specified!\n");
+        usage(argv[0]);
+        return EINVAL;
+    }
+    
+    if (NULL == template_filename)
+    {
+        fprintf(logfile, "No template file specified!\n");
+        usage(argv[0]);
+        return EINVAL;
+    }    
+
+    /* Build up a list of file mappings */
+    error = parse_jigdo_file(jigdo_filename);
+    if (error)
+    {
+        fprintf(logfile, "Unable to parse the jigdo file %s\n", jigdo_filename);
+        return error;
+    }
+
+    /* Read the template file and actually build the image to stdout */
+    error = parse_template_file(template_filename);
+    if (error)
+    {
+        fprintf(logfile, "Unable to recreate image from template file %s\n", template_filename);
+        return error;
+    }        
+
+    return 0;
+}
diff --git a/mkisofs-JTE-v1.4.gz b/mkisofs-JTE-v1.4.gz
new file mode 100644 (file)
index 0000000..a956008
--- /dev/null
@@ -0,0 +1,3129 @@
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/Makefile cdrtools-2.0+a30.pre1/mkisofs.steve/Makefile
+--- cdrtools-2.0+a30.pre1/mkisofs/Makefile     2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/Makefile       2004-06-16 19:24:36.000000000 +0100
+@@ -34,9 +34,10 @@
+ CPPOPTS +=    -DSORTING
+ CPPOPTS +=    -DUSE_ICONV
+ CPPOPTS +=    -I../libhfs_iso/
+-CPPOPTS       +=      -DHAVE_CONFIG_H -DUSE_LIBSCHILY -DUSE_SCG \
++CPPOPTS       +=      -DHAVE_CONFIG_H -DUSE_LIBSCHILY -DUSE_SCG -DJIGDO_TEMPLATE \
+               '-DAPPID_DEFAULT="MKISOFS ISO 9660/HFS FILESYSTEM BUILDER & CDRECORD CD-R/DVD CREATOR (C) 1993 E.YOUNGDALE (C) 1997 J.PEARSON/J.SCHILLING"' \
+               -I../cdrecord
++CFLAGS  +=  -Wall -Werror -Wno-unused -g -O2 -fno-strict-aliasing
+ CFILES=               mkisofs.c tree.c write.c hash.c rock.c udf.c multi.c \
+               joliet.c match.c name.c fnmatch.c eltorito.c boot.c \
+               getopt.c getopt1.c \
+@@ -45,14 +46,16 @@
+               modes.c \
+               apple.c volume.c desktop.c mac_label.c stream.c \
+               ifo_read.c dvd_file.c dvd_reader.c \
+-              defaults.c getnum.c
++              defaults.c getnum.c md5.c jte.c \
++              boot-alpha.c boot-hppa.c boot-mips.c \
++              boot-mipsel.c endian.c
+ HFILES=               apple.h bootinfo.h config.h defaults.h diskmbr.h exclude.h \
+               fnmatch.h getopt.h iso9660.h mac_label.h mactypes.h match.h \
+               mkisofs.h sunlabel.h udf.h udf_fs.h vms.h \
+               ifo_read.h dvd_file.h dvd_reader.h bswap.h ifo_types.h \
+-              ../cdrecord/defaults.h
++              ../cdrecord/defaults.h md5.h jte.h endian.h
+-LIBS=         -lhfs -lfile -lunls -lrscg -lscg $(LIB_VOLMGT) -ldeflt -lschily $(SCSILIB) $(LIB_SOCKET)
++LIBS=         -lz -lhfs -lfile -lunls -lrscg -lscg $(LIB_VOLMGT) -ldeflt -lschily $(SCSILIB) $(LIB_SOCKET)
+ XMK_FILE=     Makefile.man hybridman.mk
+ ###########################################################################
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/boot-alpha.c cdrtools-2.0+a30.pre1/mkisofs.steve/boot-alpha.c
+--- cdrtools-2.0+a30.pre1/mkisofs/boot-alpha.c 1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/boot-alpha.c   2004-06-16 19:24:36.000000000 +0100
+@@ -0,0 +1,114 @@
++/*
++ * Program boot-alpha.c - Handle Linux alpha boot extensions to iso9660.
++ *
++ * Written by Steve McIntyre <steve@einval.com> June 2004
++ *
++ * Heavily inspired by isomarkboot by David Mosberger in 1996.
++ *
++ * Copyright 2004 Steve McIntyre
++ *
++ * 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; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <mconfig.h>
++#include "mkisofs.h"
++#include <fctldefs.h>
++#include <utypes.h>
++#include <intcvt.h>
++#include "match.h"
++#include "diskmbr.h"
++#include "bootinfo.h"
++#include <schily.h>
++#include "endian.h"
++
++        int     add_boot_alpha_filename   __PR((char *filename));
++static  int     boot_alpha_write          __PR((FILE *outfile));
++static  char   *boot_file_name = NULL;
++
++#define BOOT_STRING "Linux/Alpha aboot for ISO filesystem."
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_alpha_filename(filename)
++    char *filename;
++{
++    boot_file_name = filename;
++    return 0;
++}
++
++static int boot_alpha_write(outfile)
++    FILE *outfile;
++{
++    unsigned long long boot_sector[256]; /* One (ISO) sector */
++    unsigned long long sum = 0;
++      struct directory_entry  *boot_file;     /* Boot file we need to search for */
++    unsigned long length = 0;
++    unsigned long extent = 0;
++    int i = 0;
++
++    memset(boot_sector, 0, sizeof(boot_sector));    
++
++    /* Write the text header into the boot sector */
++    strcpy((char *)boot_sector, BOOT_STRING);
++
++    /* Find the dir entry for the boot file by walking our file list */
++    boot_file = search_tree_file(root, boot_file_name);
++    if (!boot_file) {
++#ifdef        USE_LIBSCHILY
++              comerrno(EX_BAD, "Uh oh, I cant find the Alpha boot file '%s'!\n",
++                                                      boot_file_name);
++#else
++              fprintf(stderr, "Uh oh, I cant find the Alpha boot file '%s'!\n",
++                                                      boot_file_name);
++              exit(1);
++#endif
++    }
++
++    /* Grab the ISO start sector and length from the dir entry. ISO
++       uses 2048-byte sectors, but we convert to 512-byte sectors here
++       for the sake of the firmware */
++    extent = get_733(boot_file->isorec.extent);
++    extent *= 4;
++    
++    length = get_733(boot_file->isorec.size);
++    length /= 512; /* I'm sure we should take account of any overlap
++                      here, but I'm copying what isomarkboot
++                      does. Maybe the boot files are specified to be
++                      exact multiples of 512 bytes? */
++
++    fprintf(stderr, "Found alpha boot image %s: using extent %lu, #blocks %lu\n",
++            boot_file_name, extent, length);
++
++    /* Now write those values into the appropriate area of the boot
++       sector in LITTLE ENDIAN format. */
++    write_le64(length, (unsigned char *)(unsigned long long *)&boot_sector[60]);
++    write_le64(extent, (unsigned char *)&boot_sector[61]);
++
++    /* Now generate a checksum of the first 504 bytes of the boot
++       sector and place it in boot_sector[63]. Isomarkboot currently
++       gets this wrong and will not work on big-endian systems! */
++    for (i = 0; i < 63; i++)
++        sum += read_le64((unsigned char *)&boot_sector[i]);
++
++    write_le64(sum, (unsigned char *)&boot_sector[63]);
++
++    jtwrite(boot_sector, sizeof(boot_sector), 1, 0, FALSE);
++    xfwrite(boot_sector, sizeof(boot_sector), 1, outfile, 0, FALSE);
++    last_extent_written++;
++
++    return 0;
++}
++
++struct output_fragment alphaboot_desc = {NULL, oneblock_size, NULL, boot_alpha_write, "alpha boot block"};
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/boot-hppa.c cdrtools-2.0+a30.pre1/mkisofs.steve/boot-hppa.c
+--- cdrtools-2.0+a30.pre1/mkisofs/boot-hppa.c  1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/boot-hppa.c    2004-06-17 13:10:05.000000000 +0100
+@@ -0,0 +1,203 @@
++/*
++ * Program boot-hppa.c - Handle HPPA boot extensions to iso9660.
++ *
++ * Written by Steve McIntyre <steve@einval.com> June 2004.
++ *
++ * Heavily inspired by palo:
++ *
++ ****************************************************************************
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com
++ *
++ ****************************************************************************
++ * Copyright 2004 Steve McIntyre
++ *
++ * 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; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <mconfig.h>
++#include "mkisofs.h"
++#include <fctldefs.h>
++#include <utypes.h>
++#include <intcvt.h>
++#include "match.h"
++#include "diskmbr.h"
++#include "bootinfo.h"
++#include <schily.h>
++#include "endian.h"
++
++int     add_boot_hppa_cmdline     __PR((char *cmdline));
++int     add_boot_hppa_kernel_32   __PR((char *filename));
++int     add_boot_hppa_kernel_64   __PR((char *filename));
++int     add_boot_hppa_bootloader  __PR((char *filename));
++int     add_boot_hppa_ramdisk     __PR((char *filename));
++
++static  int     boot_hppa_write   __PR((FILE *outfile));
++
++static  char   *hppa_cmdline = NULL;
++static  char   *hppa_kernel_32 = NULL;
++static  char   *hppa_kernel_64 = NULL;
++static  char   *hppa_bootloader = NULL;
++static  char   *hppa_ramdisk = NULL;
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_hppa_cmdline(cmdline)
++    char *cmdline;
++{
++    char *ptr = NULL;
++    hppa_cmdline = strdup(cmdline);
++    ptr = hppa_cmdline;
++    while (*ptr)
++    {
++        if (',' == *ptr)
++            *ptr = ' ';
++        ptr++;
++    }    
++    return 0;
++}
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_hppa_kernel_32(filename)
++    char *filename;
++{
++    hppa_kernel_32 = filename;
++    return 0;
++}
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_hppa_kernel_64(filename)
++    char *filename;
++{
++    hppa_kernel_64 = filename;
++    return 0;
++}
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_hppa_bootloader(filename)
++    char *filename;
++{
++    hppa_bootloader = filename;
++    return 0;
++}
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_hppa_ramdisk(filename)
++    char *filename;
++{
++    hppa_ramdisk = filename;
++    return 0;
++}
++
++static void exit_fatal(type, filename)
++    char *type;
++    char *filename;
++{
++#ifdef        USE_LIBSCHILY
++    comerrno(EX_BAD, "Uh oh, I can't find the %s '%s'!\n",
++             type, filename);
++#else
++    fprintf(stderr, "Uh oh, I can't find the %s '%s'!\n",
++             type, filename);
++    exit(1);
++#endif
++}
++
++static int boot_hppa_write(outfile)
++    FILE *outfile;
++{
++    unsigned char boot_sector[2048]; /* One (ISO) sector */
++      struct directory_entry  *boot_file;     /* Boot file we need to search for */
++    unsigned long length = 0;
++    unsigned long extent = 0;
++    int i = 0;
++
++    memset(boot_sector, 0, sizeof(boot_sector));    
++
++    boot_sector[0] = 0x80;  /* magic */
++    boot_sector[1] = 0x00;  /* magic */
++    boot_sector[2] = 'P';
++    boot_sector[3] = 'A';
++    boot_sector[4] = 'L';
++    boot_sector[5] = 'O';
++    boot_sector[6] = 0x00;
++    boot_sector[7] = 0x04;  /* version */
++
++    /* Find the dir entry for the 32-bit kernel by walking our file list */
++    boot_file = search_tree_file(root, hppa_kernel_32);
++    if (!boot_file)
++        exit_fatal("HPPA 32-bit kernel", hppa_kernel_32);
++    extent = 2048 * get_733(boot_file->isorec.extent);
++    length = get_733(boot_file->isorec.size);
++    fprintf(stderr, "Found hppa 32-bit kernel %s: using extent %lu (0x%lX), size %lu (0x%lX)\n",
++            hppa_kernel_32, extent, extent, length, length);
++    write_be32(extent, &boot_sector[8]);
++    write_be32(length, &boot_sector[12]);
++
++    /* Find the dir entry for the ramdisk by walking our file list */
++    boot_file = search_tree_file(root, hppa_ramdisk);
++    if (!boot_file)
++        exit_fatal("HPPA ramdisk", hppa_ramdisk);
++    extent = 2048 * get_733(boot_file->isorec.extent);
++    length = get_733(boot_file->isorec.size);
++    fprintf(stderr, "Found hppa ramdisk %s: using extent %lu (0x%lX), size %lu (0x%lX)\n",
++            hppa_ramdisk, extent, extent, length, length);
++    write_be32(extent, &boot_sector[16]);
++    write_be32(length, &boot_sector[20]);
++
++    /* Now the commandline */
++    snprintf(&boot_sector[24], 127, "%s", hppa_cmdline);
++
++    /* Find the dir entry for the 64-bit kernel by walking our file list */
++    boot_file = search_tree_file(root, hppa_kernel_64);
++    if (!boot_file)
++        exit_fatal("HPPA 64-bit kernel", hppa_kernel_64);
++    extent = 2048 * get_733(boot_file->isorec.extent);
++    length = get_733(boot_file->isorec.size);
++    fprintf(stderr, "Found hppa 64-bit kernel %s: using extent %lu (0x%lX), size %lu (0x%lX)\n",
++            hppa_kernel_64, extent, extent, length, length);
++    write_be32(extent, &boot_sector[232]);
++    write_be32(length, &boot_sector[236]);
++
++    /* Find the dir entry for the IPL by walking our file list */
++    boot_file = search_tree_file(root, hppa_bootloader);
++    if (!boot_file)
++        exit_fatal("HPPA bootloader", hppa_bootloader);
++    extent = 2048 * get_733(boot_file->isorec.extent);
++    length = get_733(boot_file->isorec.size);
++    fprintf(stderr, "Found hppa bootloader %s: using extent %lu (0x%lX), size %lu (0x%lX)\n",
++            hppa_bootloader, extent, extent, length, length);
++    write_be32(extent, &boot_sector[240]);
++    write_be32(length, &boot_sector[244]);
++
++    jtwrite(boot_sector, sizeof(boot_sector), 1, 0, FALSE);
++    xfwrite(boot_sector, sizeof(boot_sector), 1, outfile, 0, FALSE);
++    last_extent_written++;
++
++    return 0;
++}
++
++struct output_fragment hppaboot_desc = {NULL, oneblock_size, NULL, boot_hppa_write, "hppa boot block"};
++
++
++
++
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/boot-mips.c cdrtools-2.0+a30.pre1/mkisofs.steve/boot-mips.c
+--- cdrtools-2.0+a30.pre1/mkisofs/boot-mips.c  1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/boot-mips.c    2004-06-16 19:24:36.000000000 +0100
+@@ -0,0 +1,328 @@
++/*
++ * Program boot-mips.c - Handle big-endian boot extensions to iso9660.
++ *
++ * Written by Steve McIntyre <steve@einval.com> June 2004
++ *
++ * Heavily inspired by / borrowed from genisovh:
++ *
++ * Copyright: (C) 2002 by Florian Lohoff <flo@rfc822.org>
++ *            (C) 2004 by Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
++ *
++ * This program is free software; you can redistribute it and/or modify it under
++ * the terms of the GNU General Public License, Version 2, as published by the
++ * Free Software Foundation.
++ *
++ * Format for volume header information
++ *
++ * The volume header is a block located at the beginning of all disk
++ * media (sector 0).  It contains information pertaining to physical
++ * device parameters and logical partition information.
++ *
++ * The volume header is manipulated by disk formatters/verifiers,
++ * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers.
++ *
++ * Previous versions of IRIX wrote a copy of the volume header is
++ * located at sector 0 of each track of cylinder 0.  These copies were
++ * never used, and reduced the capacity of the volume header to hold large
++ * files, so this practice was discontinued.
++ * The volume header is constrained to be less than or equal to 512
++ * bytes long.  A particular copy is assumed valid if no drive errors
++ * are detected, the magic number is correct, and the 32 bit 2's complement
++ * of the volume header is correct.  The checksum is calculated by initially
++ * zeroing vh_csum, summing the entire structure and then storing the
++ * 2's complement of the sum.  Thus a checksum to verify the volume header
++ * should be 0.
++ *
++ * The error summary table, bad sector replacement table, and boot blocks are
++ * located by searching the volume directory within the volume header.
++ *
++ * Tables are sized simply by the integral number of table records that
++ * will fit in the space indicated by the directory entry.
++ *
++ * The amount of space allocated to the volume header, replacement blocks,
++ * and other tables is user defined when the device is formatted.
++ */
++
++#include <stdint.h>
++
++/*
++ * device parameters are in the volume header to determine mapping
++ * from logical block numbers to physical device addresses
++ *
++ * Linux doesn't care ...
++ */
++struct device_parameters {
++      uint8_t dp_skew;        /* spiral addressing skew */
++      uint8_t dp_gap1;        /* words of 0 before header */
++      uint8_t dp_gap2;        /* words of 0 between hdr and data */
++      uint8_t dp_spares_cyl;  /* This is for drives (such as SCSI
++              that support zone oriented sparing, where the zone is larger
++              than one track.  It gets subracteded from the cylinder size
++              ( dp_trks0 * dp_sec) when doing partition size calculations */
++      uint16_t dp_cyls;       /* number of usable cylinders (i.e.,
++              doesn't include cylinders reserved by the drive for badblocks,
++              etc.). For drives with variable geometry, this number may be
++              decreased so that:
++              dp_cyls * ((dp_heads * dp_trks0) - dp_spares_cyl) <= actualcapacity
++              This happens on SCSI drives such as the Wren IV and Toshiba 156
++              Also see dp_cylshi below */
++      uint16_t dp_shd0;       /* starting head vol 0 */
++      uint16_t dp_trks0;      /* number of tracks / cylinder vol 0*/
++      uint8_t dp_ctq_depth;   /* Depth of CTQ queue */
++      uint8_t dp_cylshi;      /* high byte of 24 bits of cylinder count */
++      uint16_t dp_unused;     /* not used */
++      uint16_t dp_secs;       /* number of sectors/track */
++      uint16_t dp_secbytes;   /* length of sector in bytes */
++      uint16_t dp_interleave; /* sector interleave */
++      int32_t dp_flags;               /* controller characteristics */
++      int32_t dp_datarate;            /* bytes/sec for kernel stats */
++      int32_t dp_nretries;            /* max num retries on data error */
++      int32_t dp_mspw;                /* ms per word to xfer, for iostat */
++      uint16_t dp_xgap1;      /* Gap 1 for xylogics controllers */
++      uint16_t dp_xsync;    /* sync delay for xylogics controllers */
++      uint16_t dp_xrdly;    /* read delay for xylogics controllers */
++      uint16_t dp_xgap2;    /* gap 2 for xylogics controllers */
++      uint16_t dp_xrgate;   /* read gate for xylogics controllers */
++      uint16_t dp_xwcont;   /* write continuation for xylogics */
++};
++
++/*
++ * Device characterization flags
++ * (dp_flags)
++ */
++#define       DP_SECTSLIP     0x00000001      /* sector slip to spare sector */
++#define       DP_SECTFWD      0x00000002      /* forward to replacement sector */
++#define       DP_TRKFWD       0x00000004      /* forward to replacement track */
++#define       DP_MULTIVOL     0x00000008      /* multiple volumes per spindle */
++#define       DP_IGNOREERRORS 0x00000010      /* transfer data regardless of errors */
++#define DP_RESEEK     0x00000020      /* recalibrate as last resort */
++#define       DP_CTQ_EN       0x00000040      /* enable command tag queueing */
++
++/*
++ * Boot blocks, bad sector tables, and the error summary table, are located
++ * via the volume_directory.
++ */
++#define VDNAMESIZE    8
++
++struct volume_directory {
++      int8_t  vd_name[VDNAMESIZE];    /* name */
++      int32_t vd_lbn;                 /* logical block number */
++      int32_t vd_nbytes;              /* file length in bytes */
++};
++
++/*
++ * partition table describes logical device partitions
++ * (device drivers examine this to determine mapping from logical units
++ * to cylinder groups, device formatters/verifiers examine this to determine
++ * location of replacement tracks/sectors, etc)
++ *
++ * NOTE: pt_firstlbn SHOULD BE CYLINDER ALIGNED
++ */
++struct partition_table {              /* one per logical partition */
++      int32_t pt_nblks;               /* # of logical blks in partition */
++      int32_t pt_firstlbn;            /* first lbn of partition */
++      int32_t pt_type;                /* use of partition */
++};
++
++#define       PTYPE_VOLHDR    0               /* partition is volume header */
++#define       PTYPE_TRKREPL   1               /* partition is used for repl trks */
++#define       PTYPE_SECREPL   2               /* partition is used for repl secs */
++#define       PTYPE_RAW       3               /* partition is used for data */
++#define       PTYPE_BSD42     4               /* partition is 4.2BSD file system */
++#define       PTYPE_BSD       4               /* partition is 4.2BSD file system */
++#define       PTYPE_SYSV      5               /* partition is SysV file system */
++#define       PTYPE_VOLUME    6               /* partition is entire volume */
++#define       PTYPE_EFS       7               /* partition is sgi EFS */
++#define       PTYPE_LVOL      8               /* partition is part of a logical vol */
++#define       PTYPE_RLVOL     9               /* part of a "raw" logical vol */
++#define       PTYPE_XFS       10              /* partition is sgi XFS */
++#define       PTYPE_XFSLOG    11              /* partition is sgi XFS log */
++#define       PTYPE_XLV       12              /* partition is part of an XLV vol */
++#define       PTYPE_XVM       13              /* partition is sgi XVM */
++#define       PTYPE_LSWAP     0x82            /* partition is Linux swap */
++#define       PTYPE_LINUX     0x83            /* partition is Linux native */
++#define NPTYPES               16
++
++#define       VHMAGIC         0xbe5a941       /* randomly chosen value */
++#define       NPARTAB         16              /* 16 unix partitions */
++#define       NVDIR           15              /* max of 15 directory entries */
++#define BFNAMESIZE    16              /* max 16 chars in boot file name */
++
++/* Partition types for ARCS */
++#define NOT_USED        0       /* Not used                           */
++#define FAT_SHORT       1       /* FAT filesystem, 12-bit FAT entries         */
++#define FAT_LONG        4       /* FAT filesystem, 16-bit FAT entries         */
++#define EXTENDED        5       /* extended partition                         */
++#define HUGE            6       /* huge partition- MS/DOS 4.0 and later */
++
++/* Active flags for ARCS */
++#define BOOTABLE        0x00;
++#define NOT_BOOTABLE    0x80;
++
++struct volume_header {
++      int32_t vh_magic; /* identifies volume header */
++      int16_t vh_rootpt; /* root partition number */
++      int16_t vh_swappt; /* swap partition number */
++      int8_t vh_bootfile[BFNAMESIZE]; /* name of file to boot */
++      struct device_parameters vh_dp; /* device parameters */
++      struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */
++      struct partition_table vh_pt[NPARTAB]; /* device partition layout */
++      int32_t vh_csum; /* volume header checksum */
++      int32_t vh_fill; /* fill out to 512 bytes */
++    char pad[1536];  /* pad out to 2048 */
++};
++
++#include <mconfig.h>
++#include "mkisofs.h"
++#include <fctldefs.h>
++#include <utypes.h>
++#include <intcvt.h>
++#include "match.h"
++#include "diskmbr.h"
++#include "bootinfo.h"
++#include <schily.h>
++#include "endian.h"
++
++int     add_boot_mips_filename    __PR((char *filename));
++
++static  int     boot_mips_write   __PR((FILE *outfile));
++
++#define MAX_NAMES 15
++static char *boot_mips_filename[MAX_NAMES] =
++{
++    NULL, NULL, NULL,
++    NULL, NULL, NULL,
++    NULL, NULL, NULL,
++    NULL, NULL, NULL,
++    NULL, NULL, NULL
++};
++
++static int boot_mips_num_files = 0;
++
++#define SECTORS_PER_TRACK     32
++#define BYTES_PER_SECTOR      512
++
++int add_boot_mips_filename(filename)
++    char *filename;
++{
++    if (boot_mips_num_files < MAX_NAMES)
++    {
++        boot_mips_filename[boot_mips_num_files] = filename;
++        boot_mips_num_files++;
++    }
++
++    else
++    {
++#ifdef        USE_LIBSCHILY
++        comerrno(EX_BAD, "Too many MIPS boot files!\n");
++#else
++        fprintf(stderr, "Too many MIPS boot files!\n");
++        exit(1);
++#endif
++    }
++    return 0;
++}
++
++static void vh_calc_checksum(struct volume_header *vh)
++{
++      uint32_t newsum = 0;
++      unsigned char *buffer = (unsigned char *)vh;
++      unsigned int i;
++
++      vh->vh_csum = 0;
++
++      for(i = 0; i < sizeof(struct volume_header); i += 4)
++        newsum -= read_be32(&buffer[i]);
++
++    write_be32(newsum, (unsigned char *)&vh->vh_csum);
++}
++
++static char *file_base_name(char *path)
++{
++    char *endptr = path;
++    char *ptr = path;
++    
++    while (*ptr != '\0')
++    {
++        if ('/' == *ptr)
++            endptr = ++ptr;
++        else
++            ++ptr;
++    }
++    return endptr;
++}
++
++static int boot_mips_write(outfile)
++    FILE *outfile;
++{
++      struct directory_entry  *boot_file;     /* Boot file we need to search for */
++    unsigned long length = 0;
++    unsigned long extent = 0;
++      int i;
++      struct volume_header vh;
++    unsigned long long iso_size = 0;
++    char *filename = NULL;
++
++      memset(&vh, 0, sizeof(vh));
++
++    iso_size = last_extent * 2048;
++
++    write_be32(VHMAGIC, (unsigned char *)&vh.vh_magic);
++
++      /* Values from an IRIX cd */
++    write_be16(BYTES_PER_SECTOR, (unsigned char *)&vh.vh_dp.dp_secbytes);
++    write_be16(SECTORS_PER_TRACK, (unsigned char *)&vh.vh_dp.dp_secs);
++    write_be32(DP_RESEEK|DP_IGNOREERRORS|DP_TRKFWD, (unsigned char *)&vh.vh_dp.dp_flags);
++    write_be16(1, (unsigned char *)&vh.vh_dp.dp_trks0);
++
++    write_be16((iso_size + BYTES_PER_SECTOR - 1) / (SECTORS_PER_TRACK * BYTES_PER_SECTOR),
++               (unsigned char *)&vh.vh_dp.dp_cyls);
++
++      for(i = 0; i < boot_mips_num_files; i++)
++    {
++        boot_file = search_tree_file(root, boot_mips_filename[i]);
++        
++        if (!boot_file) {
++#ifdef        USE_LIBSCHILY
++            comerrno(EX_BAD, "Uh oh, I cant find the MIPS boot file '%s'!\n",
++                     boot_mips_filename[i]);
++#else
++            fprintf(stderr, "Uh oh, I cant find the MIPS boot file '%s'!\n",
++                    boot_mips_filename[i]);
++            exit(1);
++#endif
++        }
++
++        extent = get_733(boot_file->isorec.extent) * 4;
++        length = ((get_733(boot_file->isorec.size) + 2047) / 2048) * 2048;
++        filename = file_base_name(boot_mips_filename[i]);
++
++        strncpy(vh.vh_vd[i].vd_name, filename, MIN(VDNAMESIZE, strlen(filename)));
++        write_be32(extent, (unsigned char *)&vh.vh_vd[i].vd_lbn);
++        write_be32(length, (unsigned char *)&vh.vh_vd[i].vd_nbytes);
++        
++        fprintf(stderr, "Found mips boot image %s, using extent %lu (0x%lX), #blocks %lu (0x%lX)\n",
++                filename, extent, extent, length, length);
++      }
++
++      /* Create volume partition on whole cd iso */
++    write_be32((iso_size + (BYTES_PER_SECTOR - 1))/ BYTES_PER_SECTOR, (unsigned char *)&vh.vh_pt[10].pt_nblks);
++    write_be32(0, (unsigned char *)&vh.vh_pt[10].pt_firstlbn);
++    write_be32(PTYPE_VOLUME, (unsigned char *)&vh.vh_pt[10].pt_type);
++
++      /* Create volume header partition, also on WHOLE cd iso */
++    write_be32((iso_size + (BYTES_PER_SECTOR - 1))/ BYTES_PER_SECTOR, (unsigned char *)&vh.vh_pt[8].pt_nblks);
++    write_be32(0, (unsigned char *)&vh.vh_pt[8].pt_firstlbn);
++    write_be32(PTYPE_VOLHDR, (unsigned char *)&vh.vh_pt[8].pt_type);
++
++      /* Create checksum */
++      vh_calc_checksum(&vh);
++
++    jtwrite(&vh, sizeof(vh), 1, 0, FALSE);
++    xfwrite(&vh, sizeof(vh), 1, outfile, 0, FALSE);
++    last_extent_written++;
++
++      return 0;
++}
++
++struct output_fragment mipsboot_desc = {NULL, oneblock_size, NULL, boot_mips_write, "MIPS boot block"};
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/boot-mipsel.c cdrtools-2.0+a30.pre1/mkisofs.steve/boot-mipsel.c
+--- cdrtools-2.0+a30.pre1/mkisofs/boot-mipsel.c        1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/boot-mipsel.c  2004-06-17 11:36:06.000000000 +0100
+@@ -0,0 +1,242 @@
++/*
++ * Program boot-mipsel.c - Handle Mipsel boot extensions to iso9660.
++ *
++ *  Written by Steve McIntyre <steve@einval.com> (2004).
++ *
++ * Heavily inspired by / borrowed from delo:
++ *
++ * Copyright: (C) 2002 by Florian Lohoff <flo@rfc822.org>
++ *            (C) 2004 by Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
++ *
++ * This program is free software; you can redistribute it and/or modify it under
++ * the terms of the GNU General Public License, Version 2, as published by the
++ * Free Software Foundation.
++ *
++ * Format for volume header information
++ *
++ * The volume header is a block located at the beginning of all disk
++ * media (sector 0).  It contains information pertaining to physical
++ * device parameters and logical partition information.
++ *
++ * The volume header is manipulated by disk formatters/verifiers,
++ * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers.
++ *
++ * Previous versions of IRIX wrote a copy of the volume header is
++ * located at sector 0 of each track of cylinder 0.  These copies were
++ * never used, and reduced the capacity of the volume header to hold large
++ * files, so this practice was discontinued.
++ * The volume header is constrained to be less than or equal to 512
++ * bytes long.  A particular copy is assumed valid if no drive errors
++ * are detected, the magic number is correct, and the 32 bit 2's complement
++ * of the volume header is correct.  The checksum is calculated by initially
++ * zeroing vh_csum, summing the entire structure and then storing the
++ * 2's complement of the sum.  Thus a checksum to verify the volume header
++ * should be 0.
++ *
++ * The error summary table, bad sector replacement table, and boot blocks are
++ * located by searching the volume directory within the volume header.
++ *
++ * Tables are sized simply by the integral number of table records that
++ * will fit in the space indicated by the directory entry.
++ *
++ * The amount of space allocated to the volume header, replacement blocks,
++ * and other tables is user defined when the device is formatted.
++ */
++
++#include <mconfig.h>
++#include "mkisofs.h"
++#include <fctldefs.h>
++#include <utypes.h>
++#include <intcvt.h>
++#include "match.h"
++#include "diskmbr.h"
++#include "bootinfo.h"
++#include <schily.h>
++#include "endian.h"
++#include <errno.h>
++#include <elf.h>
++
++int             add_boot_mipsel_filename  __PR((char *filename));
++static  int     boot_mipsel_write         __PR((FILE *outfile));
++
++static  char   *boot_file_name = NULL;
++
++#define MAX_MAPS        51
++#define DEC_BOOT_MAGIC  0x02757a
++#define HD_SECTOR_SIZE  512
++
++/* Those were stolen from linux kernel headers. */
++
++struct extent {
++    uint32_t count;
++    uint32_t start;
++} __attribute__((packed));
++
++struct dec_bootblock {
++    int8_t pad[8];
++    int32_t magic;          /* We are a DEC BootBlock */
++    int32_t mode;           /* 0: Single extent, 1: Multi extent boot */
++    int32_t loadAddr;       /* Load below kernel */
++    int32_t execAddr;       /* And exec there */
++    struct extent bootmap[MAX_MAPS];
++} __attribute__((packed));
++
++static void swap_in_elf32_ehdr(Elf32_Ehdr *ehdr)
++{
++    ehdr->e_type = read_le16((unsigned char *)&ehdr->e_type);
++    ehdr->e_machine = read_le16((unsigned char *)&ehdr->e_machine);
++    ehdr->e_version = read_le32((unsigned char *)&ehdr->e_version);
++    ehdr->e_entry = read_le32((unsigned char *)&ehdr->e_entry);
++    ehdr->e_phoff = read_le32((unsigned char *)&ehdr->e_phoff);
++    ehdr->e_shoff = read_le32((unsigned char *)&ehdr->e_shoff);
++    ehdr->e_flags = read_le32((unsigned char *)&ehdr->e_flags);
++    ehdr->e_ehsize = read_le16((unsigned char *)&ehdr->e_ehsize);
++    ehdr->e_phentsize = read_le16((unsigned char *)&ehdr->e_phentsize);
++    ehdr->e_phnum = read_le16((unsigned char *)&ehdr->e_phnum);
++    ehdr->e_shentsize = read_le16((unsigned char *)&ehdr->e_shentsize);
++    ehdr->e_shnum = read_le16((unsigned char *)&ehdr->e_shnum);
++    ehdr->e_shstrndx = read_le16((unsigned char *)&ehdr->e_shstrndx);
++}
++
++static void swap_in_elf32_phdr(Elf32_Phdr *phdr)
++{
++    phdr->p_type = read_le32((unsigned char *)&phdr->p_type);
++    phdr->p_offset = read_le32((unsigned char *)&phdr->p_offset);
++    phdr->p_vaddr = read_le32((unsigned char *)&phdr->p_vaddr);
++    phdr->p_paddr = read_le32((unsigned char *)&phdr->p_paddr);
++    phdr->p_filesz = read_le32((unsigned char *)&phdr->p_filesz);
++    phdr->p_memsz = read_le32((unsigned char *)&phdr->p_memsz);
++    phdr->p_flags = read_le32((unsigned char *)&phdr->p_flags);
++    phdr->p_align = read_le32((unsigned char *)&phdr->p_align);
++}
++
++/* Simple function: store the filename to be used later when we need
++   to find the boot file */
++extern int add_boot_mipsel_filename(filename)
++    char *filename;
++{
++    boot_file_name = filename;
++    return 0;
++}
++
++/* Parse the ELF header of the boot loaded to work out the load
++   address and exec address */
++static int parse_boot_file(char *filename, int32_t *loadaddr, int32_t *execaddr, int32_t *offset, int32_t *count)
++{
++    int error = 0;
++    FILE *loader = NULL;
++    Elf32_Ehdr ehdr;
++    Elf32_Phdr phdr;
++    
++    loader = fopen(filename, "rb");
++    if (!loader)
++        return errno;
++    
++    error = fread(&ehdr, sizeof(ehdr), 1, loader);
++    if (1 != error)
++        return EIO;
++
++    swap_in_elf32_ehdr(&ehdr);
++    if (!(ehdr.e_ident[EI_MAG0] == ELFMAG0
++          && ehdr.e_ident[EI_MAG1] == ELFMAG1
++          && ehdr.e_ident[EI_MAG2] == ELFMAG2
++          && ehdr.e_ident[EI_MAG3] == ELFMAG3
++          && ehdr.e_ident[EI_CLASS] == ELFCLASS32
++          && ehdr.e_ident[EI_DATA] == ELFDATA2LSB
++          && ehdr.e_ident[EI_VERSION] == EV_CURRENT
++          && ehdr.e_type == ET_EXEC
++          && ehdr.e_machine == EM_MIPS
++          && ehdr.e_version == EV_CURRENT))
++    {
++        fprintf(stderr, "Sorry, %s is not a MIPS ELF32 little endian file", filename);        
++        return EINVAL;
++    }
++    if (ehdr.e_phnum != 1)
++    {
++        fprintf(stderr, "Sorry, %s has more than one ELF segment", filename);
++        return EINVAL;
++    }
++    fseek(loader, ehdr.e_phoff, SEEK_SET);
++    error = fread(&phdr, sizeof(phdr), 1, loader);
++    if (1 != error)
++        return EIO;
++
++    *loadaddr = phdr.p_vaddr;
++    *execaddr = ehdr.e_entry;
++      *offset = (phdr.p_offset + HD_SECTOR_SIZE - 1) / HD_SECTOR_SIZE;
++      *count = (phdr.p_filesz + HD_SECTOR_SIZE - 1) / HD_SECTOR_SIZE;
++
++    fprintf(stderr, "Parsed mipsel boot image %s: using loadaddr 0x%X, execaddr 0x%X, offset 0x%X, count 0x%X\n",
++            filename, *loadaddr, *execaddr, *offset, *count);
++
++    fclose(loader);
++    return 0;
++}
++
++static int boot_mipsel_write(outfile)
++    FILE *outfile;
++{
++    char sector[2048];
++    struct dec_bootblock *bb = (struct dec_bootblock *)sector;
++    int error = 0;
++    int offset = 0;
++    int count = 0;
++    struct directory_entry    *boot_file;     /* Boot file we need to search for in the image */
++    unsigned long length = 0;
++    unsigned long extent = 0;
++    int loadaddr = 0;
++    int execaddr = 0;
++
++    memset(sector, 0, sizeof(sector));
++
++    /* Fill in our values we care on */
++    write_le32(DEC_BOOT_MAGIC, (unsigned char *)&bb->magic);
++    write_le32(1, (unsigned char *)&bb->mode);
++
++    /* Find the file entry in the CD image */
++    boot_file = search_tree_file(root, boot_file_name);
++    if (!boot_file)
++    {
++#ifdef        USE_LIBSCHILY
++              comerrno(EX_BAD, "Uh oh, unable to find the mipsel boot file '%s'!\n",
++                 boot_file_name);
++#else
++              fprintf(stderr, "Uh oh, unable to find the mipsel boot file '%s'!\n",
++                boot_file_name);
++              exit(1);
++#endif
++    }
++
++    extent = get_733(boot_file->isorec.extent);
++    length = get_733(boot_file->isorec.size);
++    fprintf(stderr, "Found mipsel boot loader %s: using extent %lu, #blocks %lu\n",
++            boot_file_name, extent, length);
++
++    /* Parse the ELF headers on the boot file */
++    error = parse_boot_file(boot_file->whole_name, &loadaddr, &execaddr, &offset, &count);
++    if (error)
++    {
++#ifdef        USE_LIBSCHILY
++              comerrno(EX_BAD, "Uh oh, unable to parse the mipsel boot file '%s'!\n",
++                 boot_file->whole_name);
++#else
++              fprintf(stderr, "Uh oh, unable to parse the mipsel boot file '%s'!\n",
++                boot_file->whole_name);
++              exit(1);
++#endif
++    }
++
++    write_le32(loadaddr, (unsigned char *)&bb->loadAddr);
++    write_le32(execaddr, (unsigned char *)&bb->execAddr);
++    write_le32((extent * 4) + offset, (unsigned char *)&bb->bootmap[0].start);
++    write_le32(count, (unsigned char *)&bb->bootmap[0].count);
++    
++    jtwrite(sector, sizeof(sector), 1, 0, FALSE);
++    xfwrite(sector, sizeof(sector), 1, outfile, 0, FALSE);
++    last_extent_written++;
++
++    return 0;
++}
++
++struct output_fragment mipselboot_desc = {NULL, oneblock_size, NULL, boot_mipsel_write, "mipsel boot block"};
++
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/boot.c cdrtools-2.0+a30.pre1/mkisofs.steve/boot.c
+--- cdrtools-2.0+a30.pre1/mkisofs/boot.c       2004-02-22 15:25:09.000000000 +0000
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/boot.c 2004-06-16 19:24:36.000000000 +0100
+@@ -373,6 +373,7 @@
+        */
+       amt = roundup(last_extent_written, (CD_CYLSIZE/SECTOR_SIZE)) - last_extent_written;
+       for (n = 0; n < amt; n++) {
++        jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
+               last_extent_written++;
+       }
+@@ -403,6 +404,7 @@
+                       memset(buffer, 0, sizeof (buffer));
+                       if (read(f, buffer, SECTOR_SIZE) < 0)
+                               comerr("Read error on '%s'.\n", boot_files[i]);
++            jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
+                       xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
+                       last_extent_written++;
+               }
+@@ -484,6 +486,7 @@
+               memcpy(buffer, &cd_label, 512);
+       }
++    jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+@@ -523,6 +526,7 @@
+                       comerr("Read error on '%s'.\n", genboot_image);
+               if (i != 0 || last_extent_written == session_start) {
++                      jtwrite(buffer, SECTOR_SIZE, 1, 0, FALSE);
+                       xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE);
+                       last_extent_written++;
+               }
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/eltorito.c cdrtools-2.0+a30.pre1/mkisofs.steve/eltorito.c
+--- cdrtools-2.0+a30.pre1/mkisofs/eltorito.c   2004-03-04 22:39:29.000000000 +0000
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/eltorito.c     2004-06-16 19:24:36.000000000 +0100
+@@ -666,6 +666,7 @@
+       }
+       /* Next we write out the boot volume descriptor for the disc */
+       get_torito_desc(&gboot_desc);
++      jtwrite(&gboot_desc, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(&gboot_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/endian.c cdrtools-2.0+a30.pre1/mkisofs.steve/endian.c
+--- cdrtools-2.0+a30.pre1/mkisofs/endian.c     1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/endian.c       2004-06-16 19:24:36.000000000 +0100
+@@ -0,0 +1,188 @@
++#include <mconfig.h>
++#include "endian.h"
++
++/* Write a 64-bit quantity out into memory in BIG ENDIAN order */
++void write_be64(in, out)
++    unsigned long long in;
++    unsigned char *out;
++{
++    out[0] = (in >> 56) & 0xFF;
++    out[1] = (in >> 48) & 0xFF;
++    out[2] = (in >> 40) & 0xFF;
++    out[3] = (in >> 32) & 0xFF;
++    out[4] = (in >> 24) & 0xFF;
++    out[5] = (in >> 16) & 0xFF;
++    out[6] = (in >> 8) & 0xFF;
++    out[7] = in & 0xFF;
++}
++
++/* Read in a 64-bit BIG ENDIAN quantity */
++unsigned long long read_be64(in)
++    unsigned char *in;
++{
++    unsigned long long result = 0;
++
++    result |= (unsigned long long)in[0] << 56;
++    result |= (unsigned long long)in[1] << 48;
++    result |= (unsigned long long)in[2] << 40;
++    result |= (unsigned long long)in[3] << 32;
++    result |= (unsigned long long)in[4] << 24;
++    result |= (unsigned long long)in[5] << 16;
++    result |= (unsigned long long)in[6] << 8;
++    result |= (unsigned long long)in[7];
++    
++    return result;
++}
++
++/* Write a 64-bit quantity out into memory in LITTLE ENDIAN order */
++void write_le64(in, out)
++    unsigned long long in;
++    unsigned char *out;
++{
++    out[0] = in & 0xFF;
++    out[1] = (in >> 8) & 0xFF;
++    out[2] = (in >> 16) & 0xFF;
++    out[3] = (in >> 24) & 0xFF;
++    out[4] = (in >> 32) & 0xFF;
++    out[5] = (in >> 40) & 0xFF;
++    out[6] = (in >> 48) & 0xFF;
++    out[7] = (in >> 56) & 0xFF;
++}
++
++/* Read in a 64-bit LITTLE ENDIAN quantity */
++unsigned long long read_le64(in)
++    unsigned char *in;
++{
++    unsigned long long result = 0;
++
++    result |= (unsigned long long)in[0];
++    result |= (unsigned long long)in[1] << 8;
++    result |= (unsigned long long)in[2] << 16;
++    result |= (unsigned long long)in[3] << 24;
++    result |= (unsigned long long)in[4] << 32;
++    result |= (unsigned long long)in[5] << 40;
++    result |= (unsigned long long)in[6] << 48;
++    result |= (unsigned long long)in[7] << 56;
++    
++    return result;
++}
++
++/* Write a 48-bit quantity out into memory in LITTLE ENDIAN order */
++void write_le48(in, out)
++    unsigned long long in;
++    unsigned char *out;
++{
++    out[0] = in & 0xFF;
++    out[1] = (in >> 8) & 0xFF;
++    out[2] = (in >> 16) & 0xFF;
++    out[3] = (in >> 24) & 0xFF;
++    out[4] = (in >> 32) & 0xFF;
++    out[5] = (in >> 40) & 0xFF;
++}
++
++/* Read in a 48-bit LITTLE ENDIAN quantity */
++unsigned long long read_le48(in)
++    unsigned char *in;
++{
++    unsigned long long result = 0;
++
++    result |= (unsigned long long)in[0];
++    result |= (unsigned long long)in[1] << 8;
++    result |= (unsigned long long)in[2] << 16;
++    result |= (unsigned long long)in[3] << 24;
++    result |= (unsigned long long)in[4] << 32;
++    result |= (unsigned long long)in[5] << 40;
++    
++    return result;
++}
++
++/* Write a 32-bit quantity out into memory in BIG ENDIAN order */
++void write_be32(in, out)
++    unsigned long in;
++    unsigned char *out;
++{
++    out[0] = (in >> 24) & 0xFF;
++    out[1] = (in >> 16) & 0xFF;
++    out[2] = (in >> 8) & 0xFF;
++    out[3] = in & 0xFF;
++}
++
++/* Read in a 32-bit BIG ENDIAN quantity */
++unsigned long read_be32(in)
++    unsigned char *in;
++{
++    unsigned long result = 0;
++
++    result |= (unsigned long)in[0] << 24;
++    result |= (unsigned long)in[1] << 16;
++    result |= (unsigned long)in[2] << 8;
++    result |= (unsigned long)in[3];
++    
++    return result;
++}
++
++/* Write a 32-bit quantity out into memory in LITTLE ENDIAN order */
++void write_le32(in, out)
++    unsigned long in;
++    unsigned char *out;
++{
++    out[0] = in & 0xFF;
++    out[1] = (in >> 8) & 0xFF;
++    out[2] = (in >> 16) & 0xFF;
++    out[3] = (in >> 24) & 0xFF;
++}
++
++/* Read in a 32-bit LITTLE ENDIAN quantity */
++unsigned long read_le32(in)
++    unsigned char *in;
++{
++    unsigned long result = 0;
++
++    result |= (unsigned long)in[0];
++    result |= (unsigned long)in[1] << 8;
++    result |= (unsigned long)in[2] << 16;
++    result |= (unsigned long)in[3] << 24;
++    
++    return result;
++}
++
++/* Write a 16-bit quantity out into memory in BIG ENDIAN order */
++void write_be16(in, out)
++    unsigned short in;
++    unsigned char *out;
++{
++    out[0] = (in >> 8) & 0xFF;
++    out[1] = in & 0xFF;
++}
++    
++/* Read in a 16-bit BIG ENDIAN quantity */
++unsigned short read_be16(in)
++    unsigned char *in;
++{
++    unsigned short result = 0;
++    
++    result |= (unsigned short)in[0] << 8;
++    result |= (unsigned short)in[1];
++    return result;
++}
++
++/* Write a 16-bit quantity out into memory in LITTLE ENDIAN order */
++void write_le16(in, out)
++    unsigned short in;
++    unsigned char *out;
++{
++    out[0] = in & 0xFF;
++    out[1] = in & 0xFF >> 8;
++}
++    
++/* Read in a 16-bit LITTLE ENDIAN quantity */
++unsigned short read_le16(in)
++    unsigned char *in;
++{
++    unsigned short result = 0;
++    
++    result |= (unsigned short)in[0];
++    result |= (unsigned short)in[1] << 8;
++    return result;
++}
++
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/endian.h cdrtools-2.0+a30.pre1/mkisofs.steve/endian.h
+--- cdrtools-2.0+a30.pre1/mkisofs/endian.h     1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/endian.h       2004-06-16 19:24:36.000000000 +0100
+@@ -0,0 +1,17 @@
++void                  write_be64  __PR((unsigned long long in, unsigned char *out));
++unsigned long long    read_be64   __PR((unsigned char *in));
++void                  write_le64  __PR((unsigned long long in, unsigned char *out));
++unsigned long long    read_le64   __PR((unsigned char *in));
++
++void                  write_le48  __PR((unsigned long long in, unsigned char *out));
++unsigned long long    read_le48   __PR((unsigned char *in));
++
++void                  write_be32  __PR((unsigned long in, unsigned char *out));
++unsigned long         read_be32   __PR((unsigned char *in));
++void                  write_le32  __PR((unsigned long in, unsigned char *out));
++unsigned long         read_le32   __PR((unsigned char *in));
++
++void                  write_be16  __PR((unsigned short in, unsigned char *out));
++unsigned short        read_be16   __PR((unsigned char *in));
++void                  write_le16  __PR((unsigned short in, unsigned char *out));
++unsigned short        read_le16   __PR((unsigned char *in));
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/joliet.c cdrtools-2.0+a30.pre1/mkisofs.steve/joliet.c
+--- cdrtools-2.0+a30.pre1/mkisofs/joliet.c     2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/joliet.c       2004-06-16 19:24:37.000000000 +0100
+@@ -989,6 +989,7 @@
+                       dir_index, dpnt->de_name);
+ #endif
+       }
++      jtwrite(directory_buffer, total_size, 1, 0, FALSE);
+       xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
+       last_extent_written += total_size >> 11;
+       free(directory_buffer);
+@@ -1393,9 +1394,12 @@
+       FILE    *outfile;
+ {
+       /* Next we write the path tables */
++      jtwrite(jpath_table_l, jpath_blocks << 11, 1, 0, FALSE);
+       xfwrite(jpath_table_l, jpath_blocks << 11, 1, outfile, 0, FALSE);
++      last_extent_written += jpath_blocks;
++      jtwrite(jpath_table_m, jpath_blocks << 11, 1, 0, FALSE);
+       xfwrite(jpath_table_m, jpath_blocks << 11, 1, outfile, 0, FALSE);
+-      last_extent_written += 2 * jpath_blocks;
++      last_extent_written += jpath_blocks;
+       free(jpath_table_l);
+       free(jpath_table_m);
+       jpath_table_l = NULL;
+@@ -1448,6 +1452,7 @@
+       /* Next we write out the boot volume descriptor for the disc */
+       jvol_desc = vol_desc;
+       get_joliet_vol_desc(&jvol_desc);
++      jtwrite(&jvol_desc, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(&jvol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/jte.c cdrtools-2.0+a30.pre1/mkisofs.steve/jte.c
+--- cdrtools-2.0+a30.pre1/mkisofs/jte.c        1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/jte.c  2004-06-26 00:09:46.000000000 +0100
+@@ -0,0 +1,790 @@
++#include <mconfig.h>
++#include "mkisofs.h"
++#include <timedefs.h>
++#include <fctldefs.h>
++#include <zlib.h>
++#include <regex.h>
++#ifdef SORTING
++#include "match.h"
++#endif /* SORTING */
++#include <errno.h>
++#include <schily.h>
++#ifdef DVD_VIDEO
++#include "dvd_reader.h"
++#include "dvd_file.h"
++#include "ifo_read.h"
++#include "md5.h"
++#include "endian.h"
++#endif
++#ifdef APPLE_HYB
++#include <ctype.h>
++#endif
++
++#ifdef        VMS
++#include "vms.h"
++#endif
++
++/* Different types used in building our state list below */
++#define JTET_FILE_MATCH 1
++#define JTET_NOMATCH    2
++
++#define JTE_VER_MAJOR     0x0001
++#define JTE_VER_MINOR     0x0004
++#define JTE_NAME          "JTE"
++#define JTE_COMMENT       "JTE at http://www.einval.com/~steve/software/JTE/ ; jigdo at http://atterer.net/jigdo/"
++
++#define JIGDO_TEMPLATE_VERSION "1.1"
++
++/* Simple list to hold the results of -jigdo-exclude command line
++   options. Seems easiest to do this using regexps. We use 4 copies of
++   each to allow us to emulate glob-style matching:
++
++   ^PATTERN$
++   ^PATTERN/
++   /PATTERN$
++   /PATTERN/
++
++   We convert the patterns from the command-line into (simple) regular
++   expressions (x4), then compile those regular expressions and store
++   them raw here for speed.
++*/
++struct exclude_regex
++{
++    regex_t match_pattern[4];
++    char *exclude_rule;
++    struct exclude_regex *next;
++};
++
++struct path_mapping
++{
++    char *from;
++    char *to;
++    struct path_mapping *next;
++};
++
++FILE  *jtjigdo = NULL;
++FILE  *jttemplate = NULL;
++char    *jjigdo_out = NULL; /* Output name for jigdo .jigdo file; NULL means don't do it */
++char    *jtemplate_out = NULL; /* Output name for jigdo template file; NULL means don't do it */
++int      jte_min_size = 8192; /* 8KB seems a reasonable minimum */
++struct  exclude_regex *exclude_list = NULL;
++struct  path_mapping  *map_list = NULL;
++unsigned long long template_size = 0;
++unsigned long long image_size = 0;
++
++static struct mk_MD5Context iso_context;
++static struct mk_MD5Context template_context;
++
++typedef struct _file_entry
++{
++    unsigned char md5[16];
++    off_t file_length;
++    char *filename;
++} file_entry_t;
++
++typedef struct _unmatched_entry
++{
++    off_t uncompressed_length;
++} unmatched_entry_t;    
++
++typedef struct _entry
++{
++    int entry_type; /* JTET_TYPE as above */
++    struct _entry *next;
++    union
++    {
++        file_entry_t      file;
++        unmatched_entry_t chunk;
++    } data;
++} entry_t;
++
++typedef struct _jigdo_file_entry
++{
++    unsigned char type;
++    unsigned char fileLen[6];
++    unsigned char fileRsync[8];
++    unsigned char fileMD5[16];
++} jigdo_file_entry_t;
++
++typedef struct _jigdo_chunk_entry
++{
++    unsigned char type;
++    unsigned char skipLen[6];
++} jigdo_chunk_entry_t;
++
++typedef struct _jigdo_image_entry
++{
++    unsigned char type;
++    unsigned char imageLen[6];
++    unsigned char imageMD5[16];
++    unsigned char blockLen[4];
++} jigdo_image_entry_t;
++
++entry_t *entry_list = NULL;
++entry_t *last_entry = NULL;
++FILE    *t_file = NULL;
++FILE    *j_file = NULL;
++int      num_matches = 0;
++int      num_chunks = 0;
++
++/* Convert a glob-style pattern to a regexp */
++static void convert_pattern(char *converted, char *prefix, char *in, char *suffix)
++{
++    char *p = in;
++    char *out = converted;
++
++    out += sprintf(out, prefix);    
++    while (*p)
++    {
++        switch (*p)
++        {
++            case '*':
++                out += sprintf(out, ".*");
++                break;
++            case '?':
++                out += sprintf(out, ".");
++                break;
++            case '.':
++            case '\\':
++            case '^':
++            case '$':
++                out += sprintf(out, "\\");
++                out += sprintf(out, "%c", *p);
++                break;
++            default:
++                out += sprintf(out, "%c", *p);
++                break;
++        }
++        p++;
++    }
++    out += sprintf(out, suffix);
++}
++
++/* Build the list of exclusion regexps */
++extern int jte_add_exclude(char *pattern)
++{
++    int size = 3 + (2 * strlen(pattern)); /* Worst case */
++    char *conversion = NULL;
++    struct exclude_regex *new = NULL;
++    
++    conversion = malloc(size);
++    if (!conversion)
++        return ENOMEM;
++
++    new = malloc(sizeof *new);
++    if (!new)
++        return ENOMEM;    
++    
++    memset(conversion, 0, size);
++    convert_pattern(conversion, "^", pattern, "/");
++    regcomp(&new->match_pattern[0], conversion, REG_NEWLINE);
++
++    memset(conversion, 0, size);
++    convert_pattern(conversion, "^", pattern, "$");
++    regcomp(&new->match_pattern[1], conversion, REG_NEWLINE);
++
++    memset(conversion, 0, size);
++    convert_pattern(conversion, "/", pattern, "/");
++    regcomp(&new->match_pattern[2], conversion, REG_NEWLINE);
++
++    memset(conversion, 0, size);
++    convert_pattern(conversion, "/", pattern, "$");
++    regcomp(&new->match_pattern[3], conversion, REG_NEWLINE);
++
++    new->exclude_rule = pattern;
++    free(conversion);
++
++    /* Order on the exclude list doesn't matter! */
++    if (NULL != exclude_list)
++        new->next = exclude_list;
++
++    exclude_list = new;
++    return 0;
++}
++
++/* Check if the file should be excluded because of a filename match. 1
++   means exclude, 0 means not */
++static int check_exclude_by_name(char *filename, char **matched)
++{
++    struct exclude_regex *ptr = exclude_list;
++    regmatch_t pmatch[1];
++    int i = 0;
++
++    while (ptr)
++    {
++        for (i = 0; i < 4; i++)
++        {
++            if (!regexec(&ptr->match_pattern[i], filename, 1, pmatch, 0))
++            {
++                *matched = ptr->exclude_rule;
++                return 1;
++            }
++        }
++        ptr = ptr->next;
++    }
++    
++    /* Not matched, so return 0 */
++    return 0;
++}
++
++/* Should we list a file separately in the jigdo output, or should we
++   just dump it into the template file as binary data? Two common
++   cases to look for here:
++
++   1. Small files are better simply folded in, as they take less space that way.
++
++   2. Files in /doc (for example) may change in the archive all the
++      time and it's better to not have to fetch snapshot copies if we
++      can avoid it.      
++*/
++extern int list_file_in_jigdo(char *filename, off_t size)
++{
++    char *matched_rule;
++    
++    /* Cheaper to check file size first */
++    if (size < jte_min_size)
++    {
++        if (verbose > 0)
++            fprintf(stderr, "Jigdo-ignoring file %s; it's too small\n", filename);
++        return 0;
++    }
++    
++    /* Now check the excluded list by name */
++    if (check_exclude_by_name(filename, &matched_rule))
++    {
++        if (verbose > 0)
++            fprintf(stderr, "Jigdo-ignoring file %s; it's covered in the exclude list by \"%s\"\n", filename, matched_rule);
++        return 0;
++    }
++    
++    /* else */
++    return 1;
++}
++
++/* Add a mapping of pathnames (e.g. Debian=/mirror/debian). We should
++   be passed TO=FROM here */
++extern int jte_add_mapping(char *arg)
++{
++    int error = 0;
++    struct path_mapping *new = NULL;
++    struct path_mapping *entry = NULL;
++    char *p = arg;
++    char *from = NULL;
++    char *to = NULL;
++
++    /* Find the "=" in the string passed. Set it to NULL and we can
++       use the string in-place */
++    while (*p)
++    {
++        if ('=' == *p)
++        {
++            *p = 0;
++            p++;
++            to = arg;
++            from = p;
++        }
++        p++;
++    }
++    if (!from || !strlen(from) || !to || !strlen(to))
++        return EINVAL;
++    
++    new = malloc(sizeof(*new));
++    if (!new)
++        return ENOMEM;
++    
++    new->from = from;
++    new->to = to;
++    new->next = NULL;
++
++    if (verbose > 0)
++        fprintf(stderr, "Adding mapping from %s to %s for the jigdo file\n", from, to);
++    if (!map_list)
++        map_list = new;
++    else
++    {
++        /* Order is important; add to the end of the list */
++        entry = map_list;
++        while (NULL != entry->next)
++            entry = entry->next;
++        entry->next = new;
++    }
++    return 0;
++}
++
++/* Check if the filename should be remapped; if so, map it otherwise
++   return the original name. */
++static char *remap_filename(char *filename)
++{
++    char *new_name = filename;
++    struct path_mapping *entry = map_list;
++    
++    while (entry)
++    {
++        if (!strncmp(filename, entry->from, strlen(entry->from)))
++        {
++            new_name = calloc(1, 2 + strlen(filename) + strlen(entry->to) - strlen(entry->from));
++            if (!new_name)
++            {
++                fprintf(stderr, "Failed to malloc new filename; abort!\n");
++                exit(1);
++            }
++            sprintf(new_name, "%s:%s", entry->to, &filename[strlen(entry->from)]);
++            return new_name;
++        }
++        entry = entry->next;
++    }
++
++    /* No mapping in effect */
++    return strdup(filename);
++}    
++
++static size_t template_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
++{
++    mk_MD5Update(&template_context, ptr, size * nmemb);
++    template_size += (unsigned long long)size * nmemb;
++    return fwrite(ptr, size, nmemb, stream);
++}
++
++static void write_template_header()
++{
++    char buf[2048];
++    int i = 0;
++    char *p = buf;
++
++    memset(buf, 0, sizeof(buf));
++
++    mk_MD5Init(&template_context);
++    i += sprintf(p, "JigsawDownload template %s %s/%d.%d \r\n",
++                 JIGDO_TEMPLATE_VERSION, JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
++    p = &buf[i];
++
++    i += sprintf(p, "%s \r\n", JTE_COMMENT);
++    p = &buf[i];
++
++    i += sprintf(p, "\r\n");
++    template_fwrite(buf, i, 1, t_file);
++}
++
++/* Initialise state and start the jigdo template file */
++void write_jt_header(FILE *template_file, FILE *jigdo_file)
++{
++    t_file = template_file;
++    j_file = jigdo_file;
++
++    /* Start MD5 work for the image */
++    mk_MD5Init(&iso_context);
++
++    /* Start the template file */
++    write_template_header();
++}
++
++static void flush_compressed_chunk(void *buffer, off_t size)
++{
++    z_stream c_stream; /* compression stream */
++    unsigned char comp_size_out[6];
++    unsigned char uncomp_size_out[6];
++    off_t compressed_size_out = 0;
++    int err = 0;
++    unsigned char *comp_buf = NULL;
++
++    c_stream.zalloc = NULL;
++    c_stream.zfree = NULL;
++    c_stream.opaque = NULL;
++
++    err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
++    comp_buf = malloc(2 * size); /* Worst case */
++    c_stream.next_out = comp_buf;
++    c_stream.avail_out = 2 * size;
++    c_stream.next_in = buffer;
++    c_stream.avail_in = size;
++    
++    err = deflate(&c_stream, Z_NO_FLUSH);
++    err = deflate(&c_stream, Z_FINISH);
++    
++    compressed_size_out = c_stream.total_out + 16;
++    err = deflateEnd(&c_stream);
++
++    template_fwrite("DATA", 4, 1, t_file);
++
++    write_le48(compressed_size_out, &comp_size_out[0]);
++    template_fwrite(comp_size_out, sizeof(comp_size_out), 1, t_file);
++
++    write_le48(size, &uncomp_size_out[0]);
++    template_fwrite(uncomp_size_out, sizeof(uncomp_size_out), 1, t_file);
++    
++    template_fwrite(comp_buf, c_stream.total_out, 1, t_file);
++    free(comp_buf);
++}
++
++static void write_compressed_chunk(unsigned char *buffer, size_t size)
++{
++    static unsigned char uncomp_buf[1024 * 1024];
++    static size_t uncomp_buf_used = 0;
++
++    if ((uncomp_buf_used + size) > sizeof(uncomp_buf))
++    {
++        flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
++        uncomp_buf_used = 0;
++    }
++
++    if (!size) /* Signal a flush before we start writing the DESC entry */
++    {
++        flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
++        return;
++    }
++    
++    if (!uncomp_buf_used)
++        memset(uncomp_buf, 0, sizeof(uncomp_buf));
++
++    while (size > sizeof(uncomp_buf))
++    {
++        flush_compressed_chunk(buffer, sizeof(uncomp_buf));
++        buffer += sizeof(uncomp_buf);
++        size -= sizeof(uncomp_buf);
++    }
++    memcpy(&uncomp_buf[uncomp_buf_used], buffer, size);
++    uncomp_buf_used += size;
++}
++
++static void write_template_desc_entries(off_t image_len, char *image_md5)
++{
++    entry_t *entry = entry_list;
++    off_t desc_len = 0;
++    unsigned char out_len[6];
++    jigdo_image_entry_t jimage;
++
++    desc_len = 16 /* DESC + length twice */
++        + (sizeof(jigdo_file_entry_t) * num_matches)
++        + (sizeof(jigdo_chunk_entry_t) * num_chunks)
++        + sizeof(jigdo_image_entry_t);
++
++    write_le48(desc_len, &out_len[0]);
++    write_compressed_chunk(NULL, 0);
++    template_fwrite("DESC", 4, 1, t_file);
++    template_fwrite(out_len, sizeof(out_len), 1, t_file);
++    
++    while (entry)
++    {
++        switch (entry->entry_type)
++        {
++            case JTET_FILE_MATCH:
++            {
++                jigdo_file_entry_t jfile;
++                jfile.type = 6; /* Matched file */
++                write_le48(entry->data.file.file_length, &jfile.fileLen[0]);
++                memset(jfile.fileRsync, 0, sizeof(jfile.fileRsync)); /* Write a blank rsyncsum; we don't use it */
++                memcpy(jfile.fileMD5, entry->data.file.md5, sizeof(jfile.fileMD5));
++                template_fwrite(&jfile, sizeof(jfile), 1, t_file);
++                break;
++            }
++            case JTET_NOMATCH:
++            {
++                jigdo_chunk_entry_t jchunk;
++                jchunk.type = 2; /* Raw data */
++                write_le48(entry->data.chunk.uncompressed_length, &jchunk.skipLen[0]);
++                template_fwrite(&jchunk, sizeof(jchunk), 1, t_file);
++                break;
++            }
++        }
++        entry = entry->next;
++    }
++
++    jimage.type = 5;
++    write_le48(image_len, &jimage.imageLen[0]);
++    memcpy(jimage.imageMD5, image_md5, sizeof(jimage.imageMD5));
++    memset(jimage.blockLen, 0, sizeof(jimage.blockLen)); /* Might work... */
++    template_fwrite(&jimage, sizeof(jimage), 1, t_file);    
++    template_fwrite(out_len, sizeof(out_len), 1, t_file);
++}
++
++static char *base64_dump(unsigned char *buf, size_t buf_size)
++{
++    const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
++    int value = 0;
++    unsigned int i;
++    int bits = 0;
++    static char output_buffer[2048];
++    char *p = output_buffer;
++
++    memset(output_buffer, 0, sizeof(output_buffer));
++    if (buf_size >= (sizeof(output_buffer) * 6/8))
++    {
++        fprintf(stderr, "base64_dump: Buffer too small!\n");
++        exit(1);
++    }
++
++    for (i = 0; i < buf_size ; i++)
++    {
++        value = (value << 8) | buf[i];
++        bits += 2;
++        p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
++        if (bits >= 8) {
++            bits -= 6;
++            p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
++        }
++    }
++    if (bits > 0)
++    {
++        value <<= 8 - bits;
++        p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
++    }
++    return output_buffer;
++}
++
++static char *hex_dump(unsigned char *buf, size_t buf_size)
++{
++    unsigned int i;
++    static char output_buffer[2048];
++    char *p = output_buffer;
++
++    memset(output_buffer, 0, sizeof(output_buffer));
++    if (buf_size >= (sizeof(output_buffer) / 2))
++    {
++        fprintf(stderr, "hex_dump: Buffer too small!\n");
++        exit(1);
++    }
++
++    for (i = 0; i < buf_size ; i++)
++        p += sprintf(p, "%2.2x", buf[i]);
++
++    return output_buffer;
++}
++
++static char *file_base_name(char *path)
++{
++    char *endptr = path;
++    char *ptr = path;
++    
++    while (*ptr != '\0')
++    {
++        if ('/' == *ptr)
++            endptr = ++ptr;
++        else
++            ++ptr;
++    }
++    return endptr;
++}
++
++/* Write the .jigdo file to match the .template we've just finished. */
++static void write_jigdo_file(void)
++{
++    unsigned char template_md5sum[16];
++    entry_t *entry = entry_list;
++    struct path_mapping *map = map_list;
++
++    mk_MD5Final(&template_md5sum[0], &template_context);
++
++    fprintf(j_file, "# JigsawDownload\n");
++    fprintf(j_file, "# See <http://atterer.net/jigdo/> for details about jigdo\n");
++    fprintf(j_file, "# See <http://www.einval.com/~steve/software/CD/JTE/> for details about JTE\n\n");
++    
++    fprintf(j_file, "[Jigdo]\n");
++    fprintf(j_file, "Version=%s\n", JIGDO_TEMPLATE_VERSION);
++    fprintf(j_file, "Generator=%s/%d.%d\n\n", JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
++
++    fprintf(j_file, "[Image]\n");
++    fprintf(j_file, "Filename=%s\n", file_base_name(outfile));
++    fprintf(j_file, "Template=http://localhost/%s\n", jtemplate_out);
++    fprintf(j_file, "Template-MD5Sum=%s \n",
++            base64_dump(&template_md5sum[0], sizeof(template_md5sum)));
++    fprintf(j_file, "# Template Hex MD5sum %s\n",
++            hex_dump(&template_md5sum[0], sizeof(template_md5sum)));
++    fprintf(j_file, "# Template size %lld bytes\n", template_size);
++    fprintf(j_file, "# Image size %lld bytes\n\n", image_size);
++
++    fprintf(j_file, "[Parts]\n");
++    while (entry)
++    {
++        if (JTET_FILE_MATCH == entry->entry_type)
++        {
++            char *new_name = remap_filename(entry->data.file.filename);
++            fprintf(j_file, "%s=%s\n",
++                    base64_dump(&entry->data.file.md5[0], sizeof(entry->data.file.md5)),
++                    new_name);
++            free(new_name);
++        }
++        entry = entry->next;
++    }
++
++    fprintf(j_file, "\n[Servers]\n");
++    while (map)
++    {
++        fprintf(j_file, "%s=%s\n", map->to, map->from);
++        map = map->next;
++    }
++
++    fflush(j_file);
++}
++
++/* Finish and flush state; for now:
++   
++   1. Dump the DESC blocks and the footer information in the jigdo template file
++   2. Write the jigdo .jigdo file containing file pointers
++*/
++void write_jt_footer(void)
++{
++    unsigned char md5[16]; /* MD5SUM of the entire image */
++
++    /* Finish calculating the image's checksum */
++    mk_MD5Final(&md5[0], &iso_context);
++
++    /* And caclulate the image size */
++    image_size = (unsigned long long)SECTOR_SIZE * last_extent_written;
++
++    write_template_desc_entries(image_size, md5);
++
++    write_jigdo_file();
++}
++
++static void add_unmatched_entry(int uncompressed_length)
++{
++    entry_t *new_entry = NULL;
++
++    /* Can we extend a previous non-match entry? */
++    if (last_entry && (JTET_NOMATCH == last_entry->entry_type))
++    {
++        last_entry->data.chunk.uncompressed_length += uncompressed_length;
++        return;
++    }
++
++    new_entry = calloc(1, sizeof(entry_t));
++    new_entry->entry_type = JTET_NOMATCH;
++    new_entry->next = NULL;
++    new_entry->data.chunk.uncompressed_length = uncompressed_length;
++
++    /* Add to the end of the list */
++    if (NULL == last_entry)
++    {
++        last_entry = new_entry;
++        entry_list = new_entry;
++    }
++    else
++    {
++        last_entry->next = new_entry;
++        last_entry = new_entry;
++    }
++    num_chunks++;
++}
++
++static void add_file_entry(char *filename, off_t size, unsigned char *md5)
++{
++    entry_t *new_entry = NULL;
++
++    new_entry = calloc(1, sizeof(entry_t));
++    new_entry->entry_type = JTET_FILE_MATCH;
++    new_entry->next = NULL;
++    memcpy(new_entry->data.file.md5, md5, sizeof(new_entry->data.file.md5));
++    new_entry->data.file.file_length = size;
++    new_entry->data.file.filename = strdup(filename);
++
++    /* Add to the end of the list */
++    if (NULL == last_entry)
++    {
++        last_entry = new_entry;
++        entry_list = new_entry;
++    }
++    else
++    {
++        last_entry->next = new_entry;
++        last_entry = new_entry;
++    }
++    num_matches++;
++}    
++
++/* Cope with an unmatched block in the .iso file:
++
++   1. Write a compressed data chunk in the jigdo template file
++   2. Add an entry in our list of unmatched chunks for later */
++void jtwrite(buffer, size, count, submode, islast)
++      void    *buffer;
++      int     size;
++      int     count;
++      int     submode;
++      BOOL    islast;
++{
++#ifdef        JTWRITE_DEBUG
++      if (count != 1 || (size % 2048) != 0)
++              error("Count: %d, size: %d\n", count, size);
++#endif
++
++    /* Update the global image checksum */
++    mk_MD5Update(&iso_context, buffer, size);
++
++    /* Write a compressed version of the data to the template file,
++       and add a reference on the state list so we can write that
++       later. */
++    write_compressed_chunk(buffer, size);
++    add_unmatched_entry(size);
++}
++
++/* Cope with an file entry in the .iso file:
++
++   1. Read the file for the image's md5 checksum
++   2. Add an entry in our list of files to be written into the .jigdo later
++*/
++void write_jt_match_record(char *filename, int sector_size, off_t size)
++{
++    unsigned long long tmp_size = 0;
++    struct mk_MD5Context file_context;
++    unsigned char md5[16];
++    char buf[32768];
++    off_t remain = size;
++      FILE            *infile = NULL;
++      int     use;
++
++    memset(buf, 0, sizeof(buf));
++    mk_MD5Init(&file_context);
++
++    if ((infile = fopen(filename, "rb")) == NULL) {
++#ifdef        USE_LIBSCHILY
++              comerr("cannot open '%s'\n", filename);
++#else
++#ifndef       HAVE_STRERROR
++              fprintf(stderr, "cannot open '%s': (%d)\n",
++                              filename, errno);
++#else
++              fprintf(stderr, "cannot open '%s': %s\n",
++                              filename, strerror(errno));
++#endif
++              exit(1);
++#endif
++      }
++
++    while (remain > 0)
++    {
++        use = remain;
++        if (remain > sizeof(buf))
++            use = sizeof(buf);
++              if (fread(buf, 1, use, infile) == 0) {
++#ifdef        USE_LIBSCHILY
++                      comerr("cannot read from '%s'\n", filename);
++#else
++                      fprintf(stderr, "cannot read from '%s'\n", filename);
++                      exit(1);
++#endif
++              }
++        mk_MD5Update(&iso_context, buf, use);
++        mk_MD5Update(&file_context, buf, use);
++        remain -= use;
++    }
++
++    fclose(infile);
++    
++    mk_MD5Final(&md5[0], &file_context);
++
++    /* Update the image checksum with any necessary padding data */
++    if (size % sector_size)
++    {
++        int pad_size = sector_size - (size % sector_size);
++        memset(buf, 0, pad_size);
++        mk_MD5Update(&iso_context, buf, pad_size);
++    }
++    
++    add_file_entry(filename, size, &md5[0]);
++    if (size % sector_size)
++    {
++        int pad_size = sector_size - (size % sector_size);
++        write_compressed_chunk(buf, pad_size);
++        add_unmatched_entry(pad_size);
++    }        
++}
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/jte.h cdrtools-2.0+a30.pre1/mkisofs.steve/jte.h
+--- cdrtools-2.0+a30.pre1/mkisofs/jte.h        1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/jte.h  2004-06-16 19:24:37.000000000 +0100
+@@ -0,0 +1,14 @@
++extern char *jtemplate_out;
++extern char *jjigdo_out;
++extern FILE   *jthelper;
++extern FILE *jtjigdo;
++extern FILE *jttemplate;
++extern int  jte_min_size;
++
++extern void write_jt_header(FILE *template_file, FILE *jigdo_file);
++extern void write_jt_footer(void);
++extern void jtwrite(void *buffer, int size, int count, int submode, BOOL islast);
++extern void write_jt_match_record(char *filename, int sector_size, off_t size);
++extern int  list_file_in_jigdo(char *filename, off_t size);
++extern int  jte_add_exclude(char *pattern);
++extern int  jte_add_mapping(char *arg);
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/md5.c cdrtools-2.0+a30.pre1/mkisofs.steve/md5.c
+--- cdrtools-2.0+a30.pre1/mkisofs/md5.c        1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/md5.c  2004-06-16 19:24:37.000000000 +0100
+@@ -0,0 +1,326 @@
++/*
++ * This code implements the MD5 message-digest algorithm.
++ * The algorithm is due to Ron Rivest.  This code was
++ * written by Colin Plumb in 1993, no copyright is claimed.
++ * This code is in the public domain; do with it what you wish.
++ *
++ * Equivalent code is available from RSA Data Security, Inc.
++ * This code has been tested against that, and is equivalent,
++ * except that you don't need to include two pages of legalese
++ * with every copy.
++ *
++ * To compute the message digest of a chunk of bytes, declare an
++ * MD5Context structure, pass it to MD5Init, call MD5Update as
++ * needed on buffers full of bytes, and then call MD5Final, which
++ * will fill a supplied 16-byte array with the digest.
++ */
++
++/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
++   not require an integer type which is exactly 32 bits.  This work
++   draws on the changes for the same purpose by Tatu Ylonen
++   <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
++   that code, there is no copyright issue.  I hereby disclaim
++   copyright in any changes I have made; this code remains in the
++   public domain.  */
++
++/* Note regarding cvs_* namespace: this avoids potential conflicts
++   with libraries such as some versions of Kerberos.  No particular
++   need to worry about whether the system supplies an MD5 library, as
++   this file is only about 3k of object code.  */
++
++/* Steve McIntyre, 2004/05/31: borrowed this code from the CVS
++   library. s/cvs_/mk_/ across the source */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <string.h>   /* for memcpy() and memset() */
++
++#include "md5.h"
++
++/* Little-endian byte-swapping routines.  Note that these do not
++   depend on the size of datatypes such as mk_uint32, nor do they require
++   us to detect the endianness of the machine we are running on.  It
++   is possible they should be macros for speed, but I would be
++   surprised if they were a performance bottleneck for MD5.  */
++
++static mk_uint32
++getu32 (addr)
++     const unsigned char *addr;
++{
++      return (((((unsigned long)addr[3] << 8) | addr[2]) << 8)
++              | addr[1]) << 8 | addr[0];
++}
++
++static void
++putu32 (data, addr)
++     mk_uint32 data;
++     unsigned char *addr;
++{
++      addr[0] = (unsigned char)data;
++      addr[1] = (unsigned char)(data >> 8);
++      addr[2] = (unsigned char)(data >> 16);
++      addr[3] = (unsigned char)(data >> 24);
++}
++
++/*
++ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
++ * initialization constants.
++ */
++void
++mk_MD5Init (ctx)
++     struct mk_MD5Context *ctx;
++{
++      ctx->buf[0] = 0x67452301;
++      ctx->buf[1] = 0xefcdab89;
++      ctx->buf[2] = 0x98badcfe;
++      ctx->buf[3] = 0x10325476;
++
++      ctx->bits[0] = 0;
++      ctx->bits[1] = 0;
++}
++
++/*
++ * Update context to reflect the concatenation of another buffer full
++ * of bytes.
++ */
++void
++mk_MD5Update (ctx, buf, len)
++     struct mk_MD5Context *ctx;
++     unsigned char const *buf;
++     unsigned len;
++{
++      mk_uint32 t;
++
++      /* Update bitcount */
++
++      t = ctx->bits[0];
++      if ((ctx->bits[0] = (t + ((mk_uint32)len << 3)) & 0xffffffff) < t)
++              ctx->bits[1]++; /* Carry from low to high */
++      ctx->bits[1] += len >> 29;
++
++      t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
++
++      /* Handle any leading odd-sized chunks */
++
++      if ( t ) {
++              unsigned char *p = ctx->in + t;
++
++              t = 64-t;
++              if (len < t) {
++                      memcpy(p, buf, len);
++                      return;
++              }
++              memcpy(p, buf, t);
++              mk_MD5Transform (ctx->buf, ctx->in);
++              buf += t;
++              len -= t;
++      }
++
++      /* Process data in 64-byte chunks */
++
++      while (len >= 64) {
++              memcpy(ctx->in, buf, 64);
++              mk_MD5Transform (ctx->buf, ctx->in);
++              buf += 64;
++              len -= 64;
++      }
++
++      /* Handle any remaining bytes of data. */
++
++      memcpy(ctx->in, buf, len);
++}
++
++/*
++ * Final wrapup - pad to 64-byte boundary with the bit pattern 
++ * 1 0* (64-bit count of bits processed, MSB-first)
++ */
++void
++mk_MD5Final (digest, ctx)
++     unsigned char digest[16];
++     struct mk_MD5Context *ctx;
++{
++      unsigned count;
++      unsigned char *p;
++
++      /* Compute number of bytes mod 64 */
++      count = (ctx->bits[0] >> 3) & 0x3F;
++
++      /* Set the first char of padding to 0x80.  This is safe since there is
++         always at least one byte free */
++      p = ctx->in + count;
++      *p++ = 0x80;
++
++      /* Bytes of padding needed to make 64 bytes */
++      count = 64 - 1 - count;
++
++      /* Pad out to 56 mod 64 */
++      if (count < 8) {
++              /* Two lots of padding:  Pad the first block to 64 bytes */
++              memset(p, 0, count);
++              mk_MD5Transform (ctx->buf, ctx->in);
++
++              /* Now fill the next block with 56 bytes */
++              memset(ctx->in, 0, 56);
++      } else {
++              /* Pad block to 56 bytes */
++              memset(p, 0, count-8);
++      }
++
++      /* Append length in bits and transform */
++      putu32(ctx->bits[0], ctx->in + 56);
++      putu32(ctx->bits[1], ctx->in + 60);
++
++      mk_MD5Transform (ctx->buf, ctx->in);
++      putu32(ctx->buf[0], digest);
++      putu32(ctx->buf[1], digest + 4);
++      putu32(ctx->buf[2], digest + 8);
++      putu32(ctx->buf[3], digest + 12);
++      memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
++}
++
++#ifndef ASM_MD5
++
++/* The four core functions - F1 is optimized somewhat */
++
++/* #define F1(x, y, z) (x & y | ~x & z) */
++#define F1(x, y, z) (z ^ (x & (y ^ z)))
++#define F2(x, y, z) F1(z, x, y)
++#define F3(x, y, z) (x ^ y ^ z)
++#define F4(x, y, z) (y ^ (x | ~z))
++
++/* This is the central step in the MD5 algorithm. */
++#define MD5STEP(f, w, x, y, z, data, s) \
++      ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
++
++/*
++ * The core of the MD5 algorithm, this alters an existing MD5 hash to
++ * reflect the addition of 16 longwords of new data.  MD5Update blocks
++ * the data and converts bytes into longwords for this routine.
++ */
++void
++mk_MD5Transform (buf, inraw)
++     mk_uint32 buf[4];
++     const unsigned char inraw[64];
++{
++      register mk_uint32 a, b, c, d;
++      mk_uint32 in[16];
++      int i;
++
++      for (i = 0; i < 16; ++i)
++              in[i] = getu32 (inraw + 4 * i);
++
++      a = buf[0];
++      b = buf[1];
++      c = buf[2];
++      d = buf[3];
++
++      MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
++      MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
++      MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
++      MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
++      MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
++      MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
++      MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
++      MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
++      MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
++      MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
++      MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
++      MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
++      MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
++      MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
++      MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
++      MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
++
++      MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
++      MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
++      MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
++      MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
++      MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
++      MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
++      MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
++      MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
++      MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
++      MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
++      MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
++      MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
++      MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
++      MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
++      MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
++      MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
++
++      MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
++      MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
++      MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
++      MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
++      MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
++      MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
++      MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
++      MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
++      MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
++      MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
++      MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
++      MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
++      MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
++      MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
++      MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
++      MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
++
++      MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
++      MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
++      MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
++      MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
++      MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
++      MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
++      MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
++      MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
++      MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
++      MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
++      MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
++      MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
++      MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
++      MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
++      MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
++      MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
++
++      buf[0] += a;
++      buf[1] += b;
++      buf[2] += c;
++      buf[3] += d;
++}
++#endif
++
++#ifdef TEST
++/* Simple test program.  Can use it to manually run the tests from
++   RFC1321 for example.  */
++#include <stdio.h>
++
++int
++main (int argc, char **argv)
++{
++      struct mk_MD5Context context;
++      unsigned char checksum[16];
++      int i;
++      int j;
++
++      if (argc < 2)
++      {
++              fprintf (stderr, "usage: %s string-to-hash\n", argv[0]);
++              exit (1);
++      }
++      for (j = 1; j < argc; ++j)
++      {
++              printf ("MD5 (\"%s\") = ", argv[j]);
++              mk_MD5Init (&context);
++              mk_MD5Update (&context, argv[j], strlen (argv[j]));
++              mk_MD5Final (checksum, &context);
++              for (i = 0; i < 16; i++)
++              {
++                      printf ("%02x", (unsigned int) checksum[i]);
++              }
++              printf ("\n");
++      }
++      return 0;
++}
++#endif /* TEST */
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/md5.h cdrtools-2.0+a30.pre1/mkisofs.steve/md5.h
+--- cdrtools-2.0+a30.pre1/mkisofs/md5.h        1970-01-01 01:00:00.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/md5.h  2004-06-16 19:24:37.000000000 +0100
+@@ -0,0 +1,26 @@
++/* See md5.c for explanation and copyright information.  */
++
++#ifndef MD5_H
++#define MD5_H
++
++/* Unlike previous versions of this code, uint32 need not be exactly
++   32 bits, merely 32 bits or more.  Choosing a data type which is 32
++   bits instead of 64 is not important; speed is considerably more
++   important.  ANSI guarantees that "unsigned long" will be big enough,
++   and always using it seems to have few disadvantages.  */
++typedef unsigned long mk_uint32;
++
++struct mk_MD5Context {
++      mk_uint32 buf[4];
++      mk_uint32 bits[2];
++      unsigned char in[64];
++};
++
++void mk_MD5Init (struct mk_MD5Context *context);
++void mk_MD5Update (struct mk_MD5Context *context,
++                         unsigned char const *buf, unsigned len);
++void mk_MD5Final (unsigned char digest[16],
++                        struct mk_MD5Context *context);
++void mk_MD5Transform (mk_uint32 buf[4], const unsigned char in[64]);
++
++#endif /* !MD5_H */
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/mkisofs.c cdrtools-2.0+a30.pre1/mkisofs.steve/mkisofs.c
+--- cdrtools-2.0+a30.pre1/mkisofs/mkisofs.c    2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/mkisofs.c      2004-06-17 09:55:21.000000000 +0100
+@@ -100,7 +100,11 @@
+ int   load_addr = 0;
+ int   load_size = 0;
+ int   boot_info_table = 0;
++int use_alphaboot = 0;
+ int   use_sparcboot = 0;
++int use_hppaboot = 0;
++int use_mipsboot = 0;
++int use_mipselboot = 0;
+ int   use_sunx86boot = 0;
+ int   use_genboot = 0;
+ int   use_RockRidge = 0;
+@@ -373,6 +377,26 @@
+ #define       OPTION_SUNX86BOOT               1068
+ #define       OPTION_SUNX86LABEL              1069
++#ifdef JIGDO_TEMPLATE
++#define OPTION_JTT_OUTPUT       1101
++#define OPTION_JTJ_OUTPUT       1102
++#define OPTION_JT_MIN_SIZE      1103
++#define OPTION_JT_EXCLUDE       1104
++#define OPTION_JT_PATH_MAP      1105
++#endif
++
++#define       OPTION_BOOTALPHA            1200
++
++#define       OPTION_HPPA_CMDLINE     1210
++#define OPTION_HPPA_KERNEL_32   1211
++#define OPTION_HPPA_KERNEL_64   1212
++#define OPTION_HPPA_BOOTLOADER  1213
++#define OPTION_HPPA_RAMDISK     1214
++
++#define       OPTION_BOOTMIPS         1220
++
++#define       OPTION_BOOTMIPSEL       1230
++
+ #ifdef UDF
+ #define       OPTION_UDF                      1500
+ #endif
+@@ -584,6 +608,39 @@
+       {{"sectype", required_argument, NULL, 's'},
+       's', "TYPE", "Set output sector type to e.g. data/xa1/raw", ONE_DASH},
++      {{"alpha-boot", required_argument, NULL, OPTION_BOOTALPHA},
++      '\0', "FILE", "Set alpha boot image name (relative to image root)", ONE_DASH},
++
++      {{"hppa-cmdline", required_argument, NULL, OPTION_HPPA_CMDLINE},
++      '\0', "CMDLINE", "Set hppa boot command line (relative to image root)", ONE_DASH},
++      {{"hppa-kernel-32", required_argument, NULL, OPTION_HPPA_KERNEL_32},
++      '\0', "FILE", "Set hppa 32-bit image name (relative to image root)", ONE_DASH},
++      {{"hppa-kernel-64", required_argument, NULL, OPTION_HPPA_KERNEL_64},
++      '\0', "FILE", "Set hppa 64-bit image name (relative to image root)", ONE_DASH},
++      {{"hppa-bootloader", required_argument, NULL, OPTION_HPPA_BOOTLOADER},
++      '\0', "FILE", "Set hppa boot loader file name (relative to image root)", ONE_DASH},
++      {{"hppa-ramdisk", required_argument, NULL, OPTION_HPPA_RAMDISK},
++      '\0', "FILE", "Set hppa ramdisk file name (relative to image root)", ONE_DASH},
++
++      {{"mips-boot", required_argument, NULL, OPTION_BOOTMIPS},
++      '\0', "FILE", "Set mips boot image name (relative to image root)", ONE_DASH},
++
++      {{"mipsel-boot", required_argument, NULL, OPTION_BOOTMIPSEL},
++      '\0', "FILE", "Set mipsel boot image name (relative to image root)", ONE_DASH},
++
++#ifdef JIGDO_TEMPLATE
++    {{"jigdo-jigdo", required_argument, NULL, OPTION_JTJ_OUTPUT},
++     '\0', "FILE", "Produce a jigdo .jigdo file as well as the .iso", ONE_DASH },
++    {{"jigdo-template", required_argument, NULL, OPTION_JTT_OUTPUT},
++     '\0', "FILE", "Produce a jigdo .template file as well as the .iso", ONE_DASH },
++    {{"jigdo-min-file-size", required_argument, NULL, OPTION_JT_MIN_SIZE},
++     '\0', "SIZE", "Minimum size for a file to be listed in the jigdo file", ONE_DASH },
++    {{"jigdo-exclude", required_argument, NULL, OPTION_JT_EXCLUDE},
++     '\0', "PATTERN", "Pattern(s) to exclude from the jigdo file", ONE_DASH },
++    {{"jigdo-map", required_argument, NULL, OPTION_JT_PATH_MAP},
++     '\0', "PATTERN1=PATTERN2", "Pattern(s) to map paths (e.g. Debian=/mirror/debian)", ONE_DASH },
++#endif
++
+ #ifdef SORTING
+       { {"sort", required_argument, NULL, OPTION_SORT},
+       '\0', "FILE", "Sort file content locations according to rules in FILE", ONE_DASH },
+@@ -1348,6 +1405,41 @@
+               case OPTION_OUTPUT_CHARSET:
+                       ocharset = optarg;
+                       break;
++#ifdef JIGDO_TEMPLATE
++        case OPTION_JTT_OUTPUT:
++            jtemplate_out = optarg;
++            break;
++        case OPTION_JTJ_OUTPUT:
++            jjigdo_out = optarg;
++            break;
++        case OPTION_JT_MIN_SIZE:
++            jte_min_size = atoi(optarg);
++            break;
++        case OPTION_JT_EXCLUDE:
++            if (jte_add_exclude(optarg)) {
++#ifdef        USE_LIBSCHILY
++                              comerrno(EX_BAD,
++                         "Failed to build jigdo-exclude list\n");
++#else
++                              fprintf(stderr,
++                        "Failed to build jigdo-exclude list\n");
++                              exit(1);
++#endif
++                      }    
++            break;
++        case OPTION_JT_PATH_MAP:
++            if (jte_add_mapping(optarg)) {
++#ifdef        USE_LIBSCHILY
++                              comerrno(EX_BAD,
++                         "Failed to build jigdo mapping list\n");
++#else
++                              fprintf(stderr,
++                        "Failed to build jigdo mapping list\n");
++                              exit(1);
++#endif
++                      }    
++            break;
++#endif /* JIGDO_TEMPLATE */
+               case OPTION_NOBAK:
+                       all_files = 0;
+                       break;
+@@ -1374,6 +1466,41 @@
+                        */
+                       new_boot_entry();
+                       break;
++              case OPTION_BOOTALPHA:
++                      use_alphaboot++;
++                      /* list of pathnames of boot images */
++                      add_boot_alpha_filename(optarg);
++                      break;
++              case OPTION_HPPA_CMDLINE:
++                      use_hppaboot++;
++                      add_boot_hppa_cmdline(optarg);
++                      break;
++              case OPTION_HPPA_KERNEL_32:
++                      use_hppaboot++;
++                      add_boot_hppa_kernel_32(optarg);
++                      break;
++              case OPTION_HPPA_KERNEL_64:
++                      use_hppaboot++;
++                      add_boot_hppa_kernel_64(optarg);
++                      break;
++              case OPTION_HPPA_BOOTLOADER:
++                      use_hppaboot++;
++                      add_boot_hppa_bootloader(optarg);
++                      break;
++              case OPTION_HPPA_RAMDISK:
++                      use_hppaboot++;
++                      /* list of pathnames of boot images */
++                      add_boot_hppa_ramdisk(optarg);
++                      break;
++              case OPTION_BOOTMIPS:
++                      use_mipsboot++;
++                      /* list of pathnames of boot images */
++                      add_boot_mips_filename(optarg);
++                      break;
++              case OPTION_BOOTMIPSEL:
++                      use_mipselboot++;
++                      add_boot_mipsel_filename(optarg);
++                      break;
+               case 'B':
+                       if (use_sunx86boot)
+                               comerrno(EX_BAD,
+@@ -3156,6 +3283,27 @@
+                       exit(1);
+ #endif
+               }
++        if (jtemplate_out || jjigdo_out) {
++            if (!jtemplate_out || !jjigdo_out) {
++#ifdef        USE_LIBSCHILY
++                comerr("Bad options - need to specify all jigdo output names!\n");
++#else
++                fprintf(stderr, "Bad options - need to specify all jigdo output names!\n");
++                exit(1);
++#endif
++            }
++            jtjigdo = fopen(jjigdo_out, "wb");
++            jttemplate = fopen(jtemplate_out, "wb");
++            if (!jtjigdo || !jttemplate) {
++#ifdef        USE_LIBSCHILY
++                comerr("Unable to open jigdo template image file\n");
++#else
++                fprintf(stderr, "Unable to open jigdo template image file\n");
++                exit(1);
++#endif
++            }
++            write_jt_header(jttemplate, jtjigdo);
++        }
+       } else {
+               discimage = stdout;
+@@ -3188,6 +3336,14 @@
+ #endif        /* APPLE_HYB */
+       if (use_sparcboot || use_sunx86boot)
+               outputlist_insert(&sunlabel_desc);
++    if (use_alphaboot)
++        outputlist_insert(&alphaboot_desc);
++    if (use_hppaboot)
++        outputlist_insert(&hppaboot_desc);
++    if (use_mipsboot)
++        outputlist_insert(&mipsboot_desc);
++    if (use_mipselboot)
++        outputlist_insert(&mipselboot_desc);
+       if (use_genboot)
+               outputlist_insert(&genboot_desc);
+       outputlist_insert(&startpad_desc);
+@@ -3411,6 +3567,14 @@
+                               last_extent, last_extent_written);
+       }
++    if (jttemplate)
++    {
++        write_jt_footer();
++        fclose(jttemplate);
++    }
++    if (jtjigdo)
++        fclose(jtjigdo);
++    
+       if (verbose > 0) {
+ #ifdef HAVE_SBRK
+               fprintf(stderr, "Max brk space used %x\n",
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/mkisofs.h cdrtools-2.0+a30.pre1/mkisofs.steve/mkisofs.h
+--- cdrtools-2.0+a30.pre1/mkisofs/mkisofs.h    2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/mkisofs.h      2004-06-17 09:55:47.000000000 +0100
+@@ -34,6 +34,9 @@
+ #include <utypes.h>
+ #include <standard.h>
+ #include <libport.h>
++#ifdef JIGDO_TEMPLATE
++#include "jte.h"
++#endif
+ #ifdef        DVD_VIDEO
+ #ifndef       UDF
+@@ -189,6 +192,10 @@
+ extern struct output_fragment strfile_desc;
+ extern struct output_fragment strdir_desc;
+ extern struct output_fragment strpath_desc;
++extern struct output_fragment alphaboot_desc;
++extern struct output_fragment hppaboot_desc;
++extern struct output_fragment mipsboot_desc;
++extern struct output_fragment mipselboot_desc;
+ #ifdef APPLE_HYB
+ extern struct output_fragment hfs_desc;
+@@ -357,6 +364,7 @@
+ extern int    split_SL_component;
+ extern int    split_SL_field;
+ extern char   *trans_tbl;
++char          *outfile;
+ #define       JMAX            64      /* maximum Joliet file name length (spec) */
+ #define       JLONGMAX        103     /* out of spec Joliet file name length */
+@@ -454,6 +462,22 @@
+ extern int make_sun_label __PR((void));
+ extern int make_sunx86_label __PR((void));
++/* boot-alpha.c */
++extern int add_boot_alpha_filename __PR((char *filename));
++
++/* boot-hppa.c */
++extern int add_boot_hppa_cmdline    __PR((char *cmdline));
++extern int add_boot_hppa_kernel_32  __PR((char *filename));
++extern int add_boot_hppa_kernel_64  __PR((char *filename));
++extern int add_boot_hppa_bootloader __PR((char *filename));
++extern int add_boot_hppa_ramdisk    __PR((char *filename));
++
++/* boot-mips.c */
++extern int add_boot_mips_filename __PR((char *filename));
++
++/* boot-mipsel.c */
++extern int add_boot_mipsel_filename  __PR((char *filename));
++
+ /* write.c */
+ extern int get_731 __PR((char *));
+ extern int get_732 __PR((char *));
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/stream.c cdrtools-2.0+a30.pre1/mkisofs.steve/stream.c
+--- cdrtools-2.0+a30.pre1/mkisofs/stream.c     2004-03-04 22:56:57.000000000 +0000
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/stream.c       2004-06-16 19:24:37.000000000 +0100
+@@ -157,6 +157,7 @@
+                       break;
+               }
+               idx += count;
++              jtwrite(buf, count, 1, 0, FALSE);
+               xfwrite(buf, count, 1, outfile, 0, FALSE);
+       }
+@@ -164,15 +165,23 @@
+       iso_blocks = ISO_BLOCKS(idx);
+       memset(buf, 0, SECTOR_SIZE);
+       if (SECTOR_SIZE * iso_blocks - idx)
++    {
++              jtwrite(buf, SECTOR_SIZE * iso_blocks - idx, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE * iso_blocks - idx, 1, outfile, 0, FALSE);
++    }
+       /*
+        * If we didn't fill the available area, pad to directory block
+        */
+       for (count = 0; count < (avail_extent - iso_blocks); count++)
++    {
++              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, outfile, 0, FALSE);
+-
++    }
+       for (count = 0; count < stream_pad; count++)
++    {
++              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, outfile, 0, FALSE);
++    }
+       last_extent_written += avail_extent + stream_pad;
+       return (0);
+@@ -201,7 +210,9 @@
+       set_723((char *)s_dir.volume_sequence_number, volume_sequence_number);
+       s_dir.name_len[0] = 1;
+       s_dir.name[0] = 0;
++      jtwrite(&s_dir, offsetof(struct iso_directory_record, name[0]) + 1, 1, 0, FALSE);
+       xfwrite(&s_dir, offsetof(struct iso_directory_record, name[0]) + 1, 1, outfile, 0, FALSE);
++      jtwrite(&s_dir, offsetof(struct iso_directory_record, name[0]) + 1, 1, 0, FALSE);
+       xfwrite(&s_dir, offsetof(struct iso_directory_record, name[0]) + 1, 1, outfile, 0, FALSE);
+       memset(&s_dir, 0, sizeof (struct iso_directory_record));
+       s_dir.length[0] = 34 + strlen(stream_filename);
+@@ -214,6 +225,8 @@
+       set_723((char *)s_dir.volume_sequence_number, volume_sequence_number);
+       s_dir.name_len[0] = strlen(stream_filename);
+       memcpy(s_dir.name, stream_filename, s_dir.name_len[0]);
++      jtwrite(&s_dir, offsetof(struct iso_directory_record, name[0])
++              + s_dir.name_len[0], 1, 0, FALSE);
+       xfwrite(&s_dir, offsetof(struct iso_directory_record, name[0])
+               + s_dir.name_len[0], 1, outfile, 0, FALSE);
+@@ -222,6 +235,8 @@
+        * with filename length stream_filename + round up for even lenght count
+        */
+       to_write = (s_dir.name_len[0] % 2) ? 0 : 1;
++      jtwrite(buf, SECTOR_SIZE - ((3 * 34) + s_dir.name_len[0]) +
++              to_write, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE - ((3 * 34) + s_dir.name_len[0]) +
+               to_write, 1, outfile, 0, FALSE);
+       free(buf);
+@@ -236,7 +251,9 @@
+ write_str_path(outfile)
+       FILE    *outfile;
+ {
++      jtwrite(l_path, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(l_path, SECTOR_SIZE, 1, outfile, 0, FALSE);
++      jtwrite(m_path, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(m_path, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written += 2;
+       free(l_path);
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/udf.c cdrtools-2.0+a30.pre1/mkisofs.steve/udf.c
+--- cdrtools-2.0+a30.pre1/mkisofs/udf.c        2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/udf.c  2004-06-16 19:24:37.000000000 +0100
+@@ -1045,6 +1045,7 @@
+               1,      /* is_directory */
+               directory_link_count(dpnt),
+               (dpnt == root) ? 0 : dpnt->self->udf_file_entry_sector);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+@@ -1062,6 +1063,7 @@
+               1,
+               parent->self->udf_file_entry_sector - lba_udf_partition_start,
+               (parent == root) ? 0 : parent->self->udf_file_entry_sector);
++      jtwrite(buf, ident_size, 1, 0, FALSE);
+       xfwrite(buf, ident_size, 1, outfile, 0, FALSE);
+       size_in_bytes = ident_size;
+@@ -1107,6 +1109,7 @@
+                       !!(de1->isorec.flags[0] & ISO_DIRECTORY),
+                       de1->udf_file_entry_sector - lba_udf_partition_start,
+                       de1->udf_file_entry_sector);
++              jtwrite(buf, ident_size, 1, 0, FALSE);
+               xfwrite(buf, ident_size, 1, outfile, 0, FALSE);
+               size_in_bytes += ident_size;
+       }
+@@ -1114,6 +1117,7 @@
+       padded_size_in_bytes = PAD(size_in_bytes, SECTOR_SIZE);
+       if (size_in_bytes < padded_size_in_bytes) {
+               memset(buf, 0, padded_size_in_bytes - size_in_bytes);
++              jtwrite(buf, padded_size_in_bytes - size_in_bytes, 1, 0, FALSE);
+               xfwrite(buf, padded_size_in_bytes - size_in_bytes, 1, outfile, 0, FALSE);
+       }
+@@ -1168,6 +1172,7 @@
+                                       0,      /* is_directory */
+                                       1,      /* link_count */
+                                       de->udf_file_entry_sector);
++                              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+                               xfwrite(buf, SECTOR_SIZE, 1, outfile, 0, FALSE);
+                       }
+               }
+@@ -1199,6 +1204,7 @@
+       set8(&vsd->structure_version, 1);
+       for (i = 0; i < 3; ++i) {
+               memcpy(vsd->standard_identifier, identifiers[i], 5);
++              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       }
+       last_extent_written += 3;
+@@ -1225,30 +1231,37 @@
+       memset(buf, 0, sizeof (buf));
+       set_primary_vol_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       set_impl_use_vol_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       set_partition_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       set_logical_vol_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       set_unallocated_space_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       set_terminating_desc(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       memset(buf, 0, sizeof (buf));
+       for (i = 6; i < UDF_MAIN_SEQ_LENGTH; ++i) {
++        jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+               last_extent_written++;
+       }
+@@ -1272,6 +1285,7 @@
+                                               last_extent_written++);
+       set_terminating_desc(buf+1*SECTOR_SIZE, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, UDF_INTEG_SEQ_LENGTH, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, UDF_INTEG_SEQ_LENGTH, out, 0, FALSE);
+       return (0);
+ }
+@@ -1288,6 +1302,7 @@
+       memset(buf, 0, sizeof (buf));
+       set_anchor_volume_desc_pointer(buf, last_extent_written++);
++      jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       return (0);
+ }
+@@ -1309,6 +1324,7 @@
+       set_terminating_desc(buf+1*SECTOR_SIZE,
+                       (last_extent_written++) - lba_udf_partition_start);
++      jtwrite(buf, SECTOR_SIZE, 2, 0, FALSE);
+       xfwrite(buf, SECTOR_SIZE, 2, out, 0, FALSE);
+       return (0);
+@@ -1350,6 +1366,7 @@
+       char buf[SECTOR_SIZE];
+       memset(buf, 0, sizeof (buf));
+       while (last_extent_written < last_extent_to_write) {
++              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+               ++last_extent_written;
+       }
+@@ -1395,6 +1412,7 @@
+       memset(buf, 0, sizeof (buf));
+       while (last_extent_written < last_extent_to_write) {
+               set_anchor_volume_desc_pointer(buf, last_extent_written++);
++              jtwrite(buf, SECTOR_SIZE, 1, 0, FALSE);
+               xfwrite(buf, SECTOR_SIZE, 1, out, 0, FALSE);
+       }
+       return (0);
+diff -urNb cdrtools-2.0+a30.pre1/mkisofs/write.c cdrtools-2.0+a30.pre1/mkisofs.steve/write.c
+--- cdrtools-2.0+a30.pre1/mkisofs/write.c      2004-06-16 17:48:45.000000000 +0100
++++ cdrtools-2.0+a30.pre1/mkisofs.steve/write.c        2004-06-16 19:31:42.000000000 +0100
+@@ -289,6 +289,7 @@
+ #endif
+               }
+       }
++
+       while (count) {
+               int     got;
+@@ -459,7 +460,7 @@
+       FILE            *infile;
+       off_t           remain;
+       int     use;
+-
++    int include_in_jigdo = list_file_in_jigdo(filename, size);
+       if ((infile = fopen(filename, "rb")) == NULL) {
+ #ifdef        USE_LIBSCHILY
+@@ -480,6 +481,9 @@
+ #endif        /* APPLE_HYB */
+       remain = size;
++    if (include_in_jigdo)
++        write_jt_match_record(filename, SECTOR_SIZE, size);
++
+       while (remain > 0) {
+               use = (remain > SECTOR_SIZE * NSECT - 1 ?
+                               NSECT * SECTOR_SIZE : remain);
+@@ -494,6 +498,9 @@
+                       exit(1);
+ #endif
+               }
++        if (!include_in_jigdo)
++            jtwrite(buffer, use, 1,
++                    XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
+               xfwrite(buffer, use, 1, outfile,
+                               XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
+               last_extent_written += use / SECTOR_SIZE;
+@@ -527,6 +534,7 @@
+               remain -= use;
+       }
+       fclose(infile);
++
+ }/* write_one_file(... */
+ static void
+@@ -546,6 +554,7 @@
+                               (Llong)dwpnt->size, dwpnt->extent);
+ #endif
+               if (dwpnt->table) {
++                      jtwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1, XA_SUBH_DATA, TRUE);
+                       xfwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1,
+                                                       outfile,
+                                                       XA_SUBH_DATA, TRUE);
+@@ -590,9 +599,11 @@
+                       Uint    i;
+                       for (i = 0; i < dwpnt->pad; i++)
++            {
++                              jtwrite(blk, SECTOR_SIZE, 1, 0, FALSE);
+                               xfwrite(blk, SECTOR_SIZE, 1, outfile, 0, FALSE);
+-
+-                      last_extent_written += dwpnt->pad;
++                last_extent_written++;
++            }
+               }
+ #endif        /* APPLE_HYB || DVD_VIDEO */
+@@ -804,10 +815,10 @@
+       if (dcount < 2) {
+ #ifdef        USE_LIBSCHILY
+               errmsgno(EX_BAD,
+-                      "Directory size too small (. or .. missing ???)\n");
++                      "Directory size too small (. or .. missing !)\n");
+ #else
+               fprintf(stderr,
+-                      "Directory size too small (. or .. missing ???)\n");
++                      "Directory size too small (. or .. missing !)\n");
+ #endif
+               sort_goof = 1;
+@@ -1508,6 +1519,7 @@
+                       dir_index, dpnt->de_name);
+ #endif
+       }
++      jtwrite(directory_buffer, total_size, 1, 0, FALSE);
+       xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
+       last_extent_written += total_size >> 11;
+       free(directory_buffer);
+@@ -1525,6 +1537,7 @@
+                               ce_index, dpnt->ce_bytes);
+ #endif
+               }
++              jtwrite(ce_buffer, ce_size, 1, 0, FALSE);
+               xfwrite(ce_buffer, ce_size, 1, outfile, 0, FALSE);
+               last_extent_written += ce_size >> 11;
+               free(ce_buffer);
+@@ -1759,9 +1772,11 @@
+                * write out padding to round up to HFS allocation block
+                */
+               for (i = 0; i < hfs_pad; i++)
++        {
++                      jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+                       xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
+-
+-              last_extent_written += hfs_pad;
++            last_extent_written++;
++        }
+       }
+ #endif        /* APPLE_HYB */
+@@ -1798,11 +1813,15 @@
+       /* write out extents/catalog/dt file */
+       if (apple_hyb) {
++              jtwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, 0, FALSE);
+               xfwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, outfile, 0, FALSE);
+               /* round up to a whole CD block */
+               if (HFS_ROUND_UP(hce->hfs_tot_size) -
+                                       hce->hfs_tot_size * HFS_BLOCKSZ) {
++                      jtwrite(buffer,
++                              HFS_ROUND_UP(hce->hfs_tot_size) -
++                              hce->hfs_tot_size * HFS_BLOCKSZ, 1, 0, FALSE);
+                       xfwrite(buffer,
+                               HFS_ROUND_UP(hce->hfs_tot_size) -
+                               hce->hfs_tot_size * HFS_BLOCKSZ, 1, outfile, 0, FALSE);
+@@ -1980,6 +1999,7 @@
+       }
+       /* if not a bootable cd do it the old way */
++      jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+@@ -1997,6 +2017,7 @@
+       vol_desc.file_structure_version[0] = 2;
+       /* if not a bootable cd do it the old way */
++      jtwrite(&vol_desc, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+@@ -2019,6 +2040,7 @@
+       evol_desc.type[0] = (unsigned char) ISO_VD_END;
+       memcpy(evol_desc.id, ISO_STANDARD_ID, sizeof (ISO_STANDARD_ID));
+       evol_desc.version[0] = 1;
++      jtwrite(&evol_desc, SECTOR_SIZE, 1, 0, TRUE);
+       xfwrite(&evol_desc, SECTOR_SIZE, 1, outfile, 0, TRUE);
+       last_extent_written += 1;
+       return (0);
+@@ -2074,10 +2096,16 @@
+       cp[SECTOR_SIZE - 1] = '\0';
+       /* Per default: keep privacy. Blackout the version and arguments. */
+       if(getenv("ISODEBUG"))
++    {
++        jtwrite(vers, SECTOR_SIZE, 1, 0, TRUE);
+               xfwrite(vers, SECTOR_SIZE, 1, outfile, 0, TRUE);
++    }
+       else
++    {
++              jtwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, 0, TRUE);
+               xfwrite(calloc(SECTOR_SIZE, 1), SECTOR_SIZE, 1, outfile, 0, TRUE);
+-      last_extent_written += 1;
++    }
++      last_extent_written ++;
+       return (0);
+ }
+@@ -2151,9 +2179,12 @@
+       FILE    *outfile;
+ {
+       /* Next we write the path tables */
++      jtwrite(path_table_l, path_blocks << 11, 1, 0, FALSE);
+       xfwrite(path_table_l, path_blocks << 11, 1, outfile, 0, FALSE);
++      last_extent_written += path_blocks;
++      jtwrite(path_table_m, path_blocks << 11, 1, 0, FALSE);
+       xfwrite(path_table_m, path_blocks << 11, 1, outfile, 0, FALSE);
+-      last_extent_written += 2 * path_blocks;
++      last_extent_written += path_blocks;
+       free(path_table_l);
+       free(path_table_m);
+       path_table_l = NULL;
+@@ -2165,6 +2196,7 @@
+ exten_write(outfile)
+       FILE    *outfile;
+ {
++      jtwrite(extension_record, SECTOR_SIZE, 1, 0, FALSE);
+       xfwrite(extension_record, SECTOR_SIZE, 1, outfile, 0, FALSE);
+       last_extent_written++;
+       return (0);
+@@ -2348,10 +2380,11 @@
+       npad = session_start + 16 - last_extent_written;
+       for (i = 0; i < npad; i++) {
++              jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+               xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
++        last_extent_written++;
+       }
+-      last_extent_written += npad;
+       return (0);
+ }
+@@ -2372,10 +2405,11 @@
+               npad += 16 - i;
+       for (i = 0; i < npad; i++) {
++              jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+               xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
++        last_extent_written++;
+       }
+-      last_extent_written += npad;
+       return (0);
+ }
+@@ -2389,10 +2423,11 @@
+       memset(buffer, 0, sizeof (buffer));
+       for (i = 0; i < 150; i++) {
++              jtwrite(buffer, sizeof (buffer), 1, 0, FALSE);
+               xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
++        last_extent_written++;
+       }
+-      last_extent_written += 150;
+       return (0);
+ }
+@@ -2748,10 +2783,12 @@
+       r = tot_size % HFS_BLK_CONV;
+       /* write out HFS volume header info */
++      jtwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, 0, FALSE);
+       xfwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, outfile, 0, FALSE);
+       /* fill up to a complete CD block */
+       if (r) {
++              jtwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, 0, FALSE);
+               xfwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, outfile, 0, FALSE);
+               n++;
+       }