summaryrefslogtreecommitdiff
path: root/package/aboot/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'package/aboot/src/lib')
-rw-r--r--package/aboot/src/lib/Makefile42
-rw-r--r--package/aboot/src/lib/_longjmp.S61
-rw-r--r--package/aboot/src/lib/_setjmp.S36
-rw-r--r--package/aboot/src/lib/divide.S160
-rw-r--r--package/aboot/src/lib/isolib.c1590
-rw-r--r--package/aboot/src/lib/memcpy.c23
-rw-r--r--package/aboot/src/lib/memset.c28
-rw-r--r--package/aboot/src/lib/string.c252
-rw-r--r--package/aboot/src/lib/vsprintf.c317
9 files changed, 2509 insertions, 0 deletions
diff --git a/package/aboot/src/lib/Makefile b/package/aboot/src/lib/Makefile
new file mode 100644
index 000000000..bd6d3c543
--- /dev/null
+++ b/package/aboot/src/lib/Makefile
@@ -0,0 +1,42 @@
+ifndef ($(CC))
+CC ?= gcc
+endif
+
+override CPPFLAGS += -D__KERNEL__ -I../include
+override ASFLAGS += $(CPPFLAGS) -D__ASSEMBLY__ -traditional
+
+ifeq ($(TESTING),)
+ifeq ($(FOREIGN),"yes")
+override CFLAGS += -Os -Wall -fno-builtin
+else
+override CFLAGS += -Os -Wall -mno-fp-regs -fno-builtin
+endif
+else
+override CFLAGS += -Os -g3 -Wall
+endif
+
+ifeq ($(TESTING),)
+libaboot.a: vsprintf.o memcpy.o memset.o string.o _setjmp.o \
+ _longjmp.o isolib.o __divqu.o __remqu.o __divlu.o \
+ __remlu.o
+ ar cru $@ $?
+else
+libaboot.a: isolib.o
+ ar cru $@ $?
+endif
+
+clean:
+ rm -f libaboot.a *.o
+
+__divqu.o: divide.S
+ $(CC) -DDIV -c -o $@ divide.S
+
+__remqu.o: divide.S
+ $(CC) -DREM -c -o $@ divide.S
+
+__divlu.o: divide.S
+ $(CC) -DDIV -DINTSIZE -c -o $@ divide.S
+
+__remlu.o: divide.S
+ $(CC) -DREM -DINTSIZE -c -o $@ divide.S
+
diff --git a/package/aboot/src/lib/_longjmp.S b/package/aboot/src/lib/_longjmp.S
new file mode 100644
index 000000000..19b27a33c
--- /dev/null
+++ b/package/aboot/src/lib/_longjmp.S
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1995 David Mosberger (davidm@cs.arizona.edu)
+ */
+#include <setjmp.h>
+#include "system.h"
+
+ .extern printf
+
+ .globl _longjmp
+ .ent _longjmp
+
+_longjmp:
+ ldgp $29, 0($27)
+
+ mov $17, $0
+ lda $1, JBMAGIC & 0xffff
+ ldah $1, ((JBMAGIC >> 16) & 0xffff)($1)
+ cmoveq $0, 1, $0 # ensure $0 != 0
+
+ ldq $2, JB_MAGIC($16)
+ subq $1, $2, $1
+ ldq $30, JB_SP($16)
+ bne $1, bad_magic
+
+ ldq $9, JB_S0($16)
+ ldq $10, JB_S1($16)
+ ldq $11, JB_S2($16)
+ ldq $12, JB_S3($16)
+ ldq $13, JB_S4($16)
+ ldq $14, JB_S5($16)
+ ldq $15, JB_S6($16)
+ ldq $29, JB_GP($16)
+ ldq $26, JB_RA($16)
+#ifdef FPU
+ ldt $f2, JB_F2($16)
+ ldt $f3, JB_F3($16)
+ ldt $f4, JB_F4($16)
+ ldt $f5, JB_F5($16)
+ ldt $f6, JB_F6($16)
+ ldt $f7, JB_F7($16)
+ ldt $f8, JB_F8($16)
+ ldt $f9, JB_F9($16)
+#endif
+ ret ($26)
+
+ .data
+error_msg:
+#ifdef __osf__
+ .asciiz "_longjmp: bad magic number"
+#else
+ .asciz "_longjmp: bad magic number"
+#endif
+
+ .text
+bad_magic:
+ lda $16, error_msg
+ lda $27, printf
+ jsr $27, printf
+ call_pal PAL_halt
+
+ .end _longjmp
diff --git a/package/aboot/src/lib/_setjmp.S b/package/aboot/src/lib/_setjmp.S
new file mode 100644
index 000000000..98d6366bb
--- /dev/null
+++ b/package/aboot/src/lib/_setjmp.S
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1995 David Mosberger (davidm@cs.arizona.edu)
+ */
+#include <setjmp.h>
+
+ .globl _setjmp
+ .ent _setjmp
+
+_setjmp:
+ stq $29, JB_GP($16)
+ stq $30, JB_SP($16)
+ stq $9, JB_S0($16)
+ stq $10, JB_S1($16)
+ stq $11, JB_S2($16)
+ stq $12, JB_S3($16)
+ stq $13, JB_S4($16)
+ stq $14, JB_S5($16)
+ stq $15, JB_S6($16)
+ stq $26, JB_RA($16)
+#ifdef FPU
+ stt $f2, JB_F2($16)
+ stt $f3, JB_F3($16)
+ stt $f4, JB_F4($16)
+ stt $f5, JB_F5($16)
+ stt $f6, JB_F6($16)
+ stt $f7, JB_F7($16)
+ stt $f8, JB_F8($16)
+ stt $f9, JB_F9($16)
+#endif
+ lda $1, JBMAGIC & 0xffff
+ ldah $1, ((JBMAGIC >> 16) & 0xffff)($1)
+ stq $1, JB_MAGIC($16)
+ clr $0
+ ret ($26)
+
+ .end _setjmp
diff --git a/package/aboot/src/lib/divide.S b/package/aboot/src/lib/divide.S
new file mode 100644
index 000000000..36668ab59
--- /dev/null
+++ b/package/aboot/src/lib/divide.S
@@ -0,0 +1,160 @@
+/*
+ * arch/alpha/lib/divide.S
+ *
+ * (C) 1995 Linus Torvalds
+ *
+ * Alpha division..
+ */
+
+/*
+ * The alpha chip doesn't provide hardware division, so we have to do it
+ * by hand. The compiler expects the functions
+ *
+ * __divqu: 64-bit unsigned long divide
+ * __remqu: 64-bit unsigned long remainder
+ * __divqs/__remqs: signed 64-bit
+ * __divlu/__remlu: unsigned 32-bit
+ * __divls/__remls: signed 32-bit
+ *
+ * These are not normal C functions: instead of the normal
+ * calling sequence, these expect their arguments in registers
+ * $24 and $25, and return the result in $27. Register $28 may
+ * be clobbered (assembly temporary), anything else must be saved.
+ *
+ * In short: painful.
+ *
+ * This is a rather simple bit-at-a-time algorithm: it's very good
+ * at dividing random 64-bit numbers, but the more usual case where
+ * the divisor is small is handled better by the DEC algorithm
+ * using lookup tables. This uses much less memory, though, and is
+ * nicer on the cache.. Besides, I don't know the copyright status
+ * of the DEC code.
+ */
+
+/*
+ * My temporaries:
+ * $0 - current bit
+ * $1 - shifted divisor
+ * $2 - modulus/quotient
+ *
+ * $23 - return address
+ * $24 - dividend
+ * $25 - divisor
+ *
+ * $27 - quotient/modulus
+ * $28 - compare status
+ */
+
+#define halt .long 0
+
+/*
+ * Select function type and registers
+ */
+#define mask $0
+#define divisor $1
+#define compare $28
+
+#ifdef DIV
+#define func(x) __div##x
+#define modulus $2
+#define quotient $27
+#define GETSIGN(x) xor $24,$25,x
+#else
+#define func(x) __rem##x
+#define modulus $27
+#define quotient $2
+#define GETSIGN(x) bis $24,$24,x
+#endif
+
+/*
+ * For 32-bit operations, we need to extend to 64-bit
+ */
+#ifdef INTSIZE
+#define ufunction func(lu)
+#define sfunction func(l)
+#define LONGIFY(x) zapnot x,15,x
+#define SLONGIFY(x) addl x,0,x
+#else
+#define ufunction func(qu)
+#define sfunction func(q)
+#define LONGIFY(x)
+#define SLONGIFY(x)
+#endif
+
+.set noat
+.globl ufunction
+.ent ufunction
+ufunction:
+ subq $30,32,$30
+ stq $0, 0($30)
+ stq $1, 8($30)
+ stq $2,16($30)
+
+ bis $25,$25,divisor
+ bis $24,$24,modulus
+ bis $31,$31,quotient
+ LONGIFY(divisor)
+ LONGIFY(modulus)
+ beq divisor, 9f /* div by zero */
+ bis $31,1,mask
+
+ /* shift divisor left */
+1: cmpult divisor,modulus,compare
+ blt divisor, 3f
+ addq divisor,divisor,divisor
+ addq mask,mask,mask
+ bne compare,1b
+
+ /* ok, start to go right again.. */
+2: srl divisor,1,divisor
+ beq mask,9f
+ srl mask,1,mask
+3: cmpule divisor,modulus,compare
+ beq compare,2b
+ addq quotient,mask,quotient
+ beq mask,9f
+ subq modulus,divisor,modulus
+ br 2b
+
+9: ldq $0, 0($30)
+ ldq $1, 8($30)
+ ldq $2, 16($30)
+ addq $30,32,$30
+ ret $31,($23),1
+ .end ufunction
+
+/*
+ * Uhh.. Ugly signed division. I'd rather not have it at all, but
+ * it's needed in some circumstances. There are different ways to
+ * handle this, really. This does:
+ * -a / b = a / -b = -(a / b)
+ * -a % b = a % b
+ * a % -b = -(a % b)
+ * which is probably not the best solution, but at least should
+ * have the property that (x/y)*y + (x%y) = x.
+ */
+.globl sfunction
+.ent sfunction
+sfunction:
+ bis $24,$25,$28
+ SLONGIFY($28)
+ bge $28,ufunction
+ subq $30,32,$30
+ stq $23,0($30)
+ stq $24,8($30)
+ stq $25,16($30)
+ subq $31,$24,$28
+ cmovlt $24,$28,$24 /* abs($24) */
+ subq $31,$25,$28
+ cmovlt $25,$28,$25 /* abs($25) */
+ bsr $23,ufunction
+ ldq $23,0($30)
+ ldq $24,8($30)
+ ldq $25,16($30)
+ addq $30,32,$30
+ GETSIGN($28)
+ SLONGIFY($28)
+ bge $28,1f
+ subq $31,$27,$27
+1: ret $31,($23),1
+ .end sfunction
diff --git a/package/aboot/src/lib/isolib.c b/package/aboot/src/lib/isolib.c
new file mode 100644
index 000000000..f0d186e30
--- /dev/null
+++ b/package/aboot/src/lib/isolib.c
@@ -0,0 +1,1590 @@
+/*
+ * This code is based on the ISO filesystem support in MILO (by
+ * Dave Rusling).
+ *
+ * This is a set of functions that provides minimal filesystem
+ * functionality to the Linux bootstrapper. All we can do is
+ * open and read files... but that's all we need 8-)
+ */
+#include <linux/stat.h>
+#include <sys/types.h>
+
+#include "string.h"
+#include "iso.h"
+#include "isolib.h"
+#include "utils.h"
+
+/* iso9660 support code */
+
+#define MAX_OPEN_FILES 5
+
+static struct inode_table_entry {
+ struct iso_inode inode;
+ int inumber;
+ int free;
+ unsigned short old_mode;
+ unsigned size;
+ int nlink;
+ int mode;
+ void *start;
+} inode_table[MAX_OPEN_FILES];
+
+static unsigned long root_inode = 0;
+static struct isofs_super_block sb;
+static char data_block[1024];
+static char big_data_block[2048];
+
+#ifndef S_IRWXUGO
+# define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
+# define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+# define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#endif
+
+extern long iso_dev_read (void * buf, long offset, long size);
+
+static int parse_rock_ridge_inode(struct iso_directory_record * de,
+ struct iso_inode * inode);
+static char *get_rock_ridge_symlink(struct iso_inode *inode);
+static int get_rock_ridge_filename(struct iso_directory_record * de,
+ char * retname,
+ struct iso_inode * inode);
+
+int
+isonum_711 (char * p)
+{
+ return (*p & 0xff);
+}
+
+
+int
+isonum_712 (char * p)
+{
+ int val;
+
+ val = *p;
+ if (val & 0x80)
+ val |= 0xffffff00;
+ return val;
+}
+
+
+int
+isonum_721 (char * p)
+{
+ return ((p[0] & 0xff) | ((p[1] & 0xff) << 8));
+}
+
+int
+isonum_722 (char * p)
+{
+ return (((p[0] & 0xff) << 8) | (p[1] & 0xff));
+}
+
+
+int
+isonum_723 (char * p)
+{
+ return isonum_721(p);
+}
+
+
+int
+isonum_731 (char * p)
+{
+ return ((p[0] & 0xff)
+ | ((p[1] & 0xff) << 8)
+ | ((p[2] & 0xff) << 16)
+ | ((p[3] & 0xff) << 24));
+}
+
+
+int
+isonum_732 (char * p)
+{
+ return (((p[0] & 0xff) << 24)
+ | ((p[1] & 0xff) << 16)
+ | ((p[2] & 0xff) << 8)
+ | (p[3] & 0xff));
+}
+
+
+int
+isonum_733 (char * p)
+{
+ return isonum_731(p);
+}
+
+
+static int
+iso_bmap (struct iso_inode *inode, int block)
+{
+ if (block < 0) {
+ printf("iso_bmap: block<0");
+ return 0;
+ }
+ return (inode->i_first_extent >> sb.s_blocksize_bits) + block;
+}
+
+static int
+iso_breadi (struct iso_inode *ip, long blkno, long nblks, char * buffer)
+{
+ long i_size, abs_blkno;
+
+ /* do some error checking */
+ if (!ip || !ip->i_first_extent)
+ return -1;
+
+ i_size = ((struct inode_table_entry *) ip)->size;
+ /* as in ext2.c - cons_read() doesn't really cope well with
+ EOF conditions - actually it should be fixed */
+ if ((blkno+nblks) * sb.s_blocksize > i_size)
+ nblks = ((i_size + sb.s_blocksize)
+ / sb.s_blocksize) - blkno;
+
+ /* figure out which iso block number(s) we're being asked for */
+ abs_blkno = iso_bmap(ip, blkno);
+ if (!abs_blkno)
+ return -1;
+ /* now try and read them (easy since ISO files are continguous) */
+ return iso_dev_read(buffer, abs_blkno * sb.s_blocksize,
+ nblks * sb.s_blocksize);
+}
+
+
+/*
+ * Release our hold on an inode. Since this is a read-only application,
+ * don't worry about putting back any changes...
+ */
+static void
+iso_iput (struct iso_inode *ip)
+{
+ struct inode_table_entry *itp;
+
+ /* Find and free the inode table slot we used... */
+ itp = (struct inode_table_entry *) ip;
+
+ itp->inumber = 0;
+ itp->free = 1;
+}
+
+/*
+ * Read the specified inode from the disk and return it to the user.
+ * Returns NULL if the inode can't be read...
+ *
+ * Uses data_block
+ */
+static struct iso_inode *
+iso_iget (int ino)
+{
+ int i;
+ struct iso_inode *inode;
+ struct inode_table_entry *itp;
+ struct iso_directory_record * raw_inode;
+ unsigned char *pnt = NULL;
+ void *cpnt = NULL;
+ int high_sierra;
+ int block;
+
+#ifdef DEBUG_ISO
+ printf("iso_iget(ino=%d)\n", ino);
+#endif
+
+ /* find a free inode to play with */
+ inode = NULL;
+ itp = NULL;
+ for (i = 0; i < MAX_OPEN_FILES; i++) {
+ if (inode_table[i].free) {
+ itp = &(inode_table[i]);
+ inode = &(itp->inode);
+ break;
+ }
+ }
+ if ((inode == NULL) || (itp == NULL)) {
+ printf("iso9660 (iget): no free inodes\n");
+ return (NULL);
+ }
+
+ block = ino >> sb.s_blocksize_bits;
+ if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
+ != sb.s_blocksize) {
+ printf("iso9660: unable to read i-node block");
+ return NULL;
+ }
+
+ pnt = ((unsigned char *) data_block + (ino & (sb.s_blocksize - 1)));
+ raw_inode = ((struct iso_directory_record *) pnt);
+ high_sierra = sb.s_high_sierra;
+
+ if ((ino & (sb.s_blocksize - 1)) + *pnt > sb.s_blocksize){
+ int frag1, offset;
+
+ offset = (ino & (sb.s_blocksize - 1));
+ frag1 = sb.s_blocksize - offset;
+ cpnt = big_data_block;
+ memcpy(cpnt, data_block + offset, frag1);
+ offset += *pnt - sb.s_blocksize; /* DUH! pnt would get
+ wiped out by the
+ iso_dev_read here. */
+ if (iso_dev_read(data_block, ++block * sb.s_blocksize,
+ sb.s_blocksize)
+ != sb.s_blocksize) {
+ printf("unable to read i-node block");
+ return NULL;
+ }
+ memcpy((char *)cpnt+frag1, data_block, offset);
+ pnt = ((unsigned char *) cpnt);
+ raw_inode = ((struct iso_directory_record *) pnt);
+ }
+
+ if (raw_inode->flags[-high_sierra] & 2) {
+ itp->mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ itp->nlink = 1; /* Set to 1. We know there are 2, but
+ the find utility tries to optimize
+ if it is 2, and it screws up. It is
+ easier to give 1 which tells find to
+ do it the hard way. */
+ } else {
+ itp->mode = sb.s_mode; /* Everybody gets to read the file. */
+ itp->nlink = 1;
+ itp->mode |= S_IFREG;
+ /*
+ * If there are no periods in the name, then set the
+ * execute permission bit
+ */
+ for(i=0; i< raw_inode->name_len[0]; i++)
+ if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
+ break;
+ if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';')
+ itp->mode |= S_IXUGO; /* execute permission */
+ }
+
+ itp->size = isonum_733 (raw_inode->size);
+
+ /* There are defective discs out there - we do this to protect
+ ourselves. A cdrom will never contain more than 700Mb */
+ if((itp->size < 0 || itp->size > 700000000) &&
+ sb.s_cruft == 'n')
+ {
+ printf("Warning: defective cdrom. "
+ "Enabling \"cruft\" mount option.\n");
+ sb.s_cruft = 'y';
+ }
+
+ /*
+ * Some dipshit decided to store some other bit of information
+ * in the high byte of the file length. Catch this and
+ * holler. WARNING: this will make it impossible for a file
+ * to be > 16Mb on the CDROM!!!
+ */
+ if(sb.s_cruft == 'y' &&
+ itp->size & 0xff000000)
+ {
+ itp->size &= 0x00ffffff;
+ }
+
+ if (raw_inode->interleave[0]) {
+ printf("Interleaved files not (yet) supported.\n");
+ itp->size = 0;
+ }
+
+ /* I have no idea what file_unit_size is used for, so
+ we will flag it for now */
+ if (raw_inode->file_unit_size[0] != 0){
+ printf("File unit size != 0 for ISO file (%d).\n", ino);
+ }
+
+ /* I have no idea what other flag bits are used for, so
+ we will flag it for now */
+#ifdef DEBUG_ISO
+ if ((raw_inode->flags[-high_sierra] & ~2)!= 0){
+ printf("Unusual flag settings for ISO file (%d %x).\n",
+ ino, raw_inode->flags[-high_sierra]);
+ }
+#endif
+
+ inode->i_first_extent = (isonum_733 (raw_inode->extent) +
+ isonum_711 (raw_inode->ext_attr_length))
+ << sb.s_log_zone_size;
+
+ /* Now we check the Rock Ridge extensions for further info */
+
+ if (sb.s_rock)
+ parse_rock_ridge_inode(raw_inode,inode);
+
+ /* Will be used for previous directory */
+ inode->i_backlink = 0xffffffff;
+ switch (sb.s_conversion) {
+ case 'a':
+ inode->i_file_format = ISOFS_FILE_UNKNOWN; /* File type */
+ break;
+ case 'b':
+ inode->i_file_format = ISOFS_FILE_BINARY; /* File type */
+ break;
+ case 't':
+ inode->i_file_format = ISOFS_FILE_TEXT; /* File type */
+ break;
+ case 'm':
+ inode->i_file_format = ISOFS_FILE_TEXT_M; /* File type */
+ break;
+ }
+
+ /* keep our inode table correct */
+ itp->free = 0;
+ itp->inumber = ino;
+
+ /* return a pointer to it */
+ return inode;
+}
+
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use iso_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, iso_match returns 1 for success, 0 for failure.
+ */
+static int
+iso_match (int len, const char *name, const char *compare, int dlen)
+{
+ if (!compare)
+ return 0;
+
+#ifdef DEBUG_ISO
+ printf("iso_match: comparing %d chars of %s with %s\n",
+ dlen, name, compare);
+#endif
+
+ /* check special "." and ".." files */
+ if (dlen == 1) {
+ /* "." */
+ if (compare[0] == 0) {
+ if (!len)
+ return 1;
+ compare = ".";
+ } else if (compare[0] == 1) {
+ compare = "..";
+ dlen = 2;
+ }
+ }
+ if (dlen != len)
+ return 0;
+ return !memcmp(name, compare, len);
+}
+
+/*
+ * Find an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as an inode number). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ *
+ * uses data_block
+ */
+static int
+iso_find_entry (struct iso_inode *dir, const char *name, int namelen,
+ unsigned long *ino, unsigned long *ino_back)
+{
+ unsigned long bufsize = sb.s_blocksize;
+ unsigned char bufbits = sb.s_blocksize_bits;
+ unsigned int block, f_pos, offset, inode_number = 0; /*shut up, gcc*/
+ void * cpnt = NULL;
+ unsigned int old_offset;
+ unsigned int backlink;
+ int dlen, match, i;
+ struct iso_directory_record * de;
+ char c;
+ struct inode_table_entry *itp = (struct inode_table_entry *) dir;
+
+ *ino = 0;
+ if (!dir) return -1;
+
+ if (!(block = dir->i_first_extent)) return -1;
+
+ f_pos = 0;
+ offset = 0;
+ block = iso_bmap(dir,f_pos >> bufbits);
+ if (!block) return -1;
+
+ if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
+ != sb.s_blocksize) return -1;
+
+ while (f_pos < itp->size) {
+ de = (struct iso_directory_record *) (data_block + offset);
+ backlink = itp->inumber;
+ inode_number = (block << bufbits) + (offset & (bufsize - 1));
+
+ /* If byte is zero, this is the end of file, or time to move to
+ the next sector. Usually 2048 byte boundaries. */
+
+ if (*((unsigned char *) de) == 0) {
+ offset = 0;
+
+ /* round f_pos up to the nearest blocksize */
+ f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1))
+ + ISOFS_BLOCK_SIZE);
+ block = iso_bmap(dir,f_pos>>bufbits);
+ if (!block) return -1;
+ if (iso_dev_read(data_block,
+ block * sb.s_blocksize,
+ sb.s_blocksize)
+ != sb.s_blocksize) return -1;
+ continue; /* Will kick out if past end of directory */
+ }
+
+ old_offset = offset;
+ offset += *((unsigned char *) de);
+ f_pos += *((unsigned char *) de);
+
+ /* Handle case where the directory entry spans two blocks.
+ Usually 1024 byte boundaries */
+ if (offset >= bufsize) {
+ unsigned int frag1;
+ frag1 = bufsize - old_offset;
+ cpnt = big_data_block;
+ memcpy(cpnt, data_block + old_offset, frag1);
+
+ de = (struct iso_directory_record *) cpnt;
+ offset = f_pos & (bufsize - 1);
+ block = iso_bmap(dir,f_pos>>bufbits);
+ if (!block) return -1;
+ if (iso_dev_read(data_block,
+ block * sb.s_blocksize,
+ sb.s_blocksize)
+ != sb.s_blocksize) return 0;
+ memcpy((char *)cpnt+frag1, data_block, offset);
+ }
+
+ /* Handle the '.' case */
+
+ if (de->name[0]==0 && de->name_len[0]==1) {
+ inode_number = itp->inumber;
+ backlink = 0;
+ }
+
+ /* Handle the '..' case */
+
+ if (de->name[0]==1 && de->name_len[0]==1) {
+
+ if((int) sb.s_firstdatazone != itp->inumber)
+ inode_number = (isonum_733(de->extent) +
+ isonum_711(de->ext_attr_length))
+ << sb.s_log_zone_size;
+ else
+ inode_number = itp->inumber;
+ backlink = 0;
+ }
+
+ {
+ /* should be sufficient, since get_rock_ridge_filename
+ * truncates at 254 chars */
+ char retname[256];
+ dlen = get_rock_ridge_filename(de, retname, dir);
+ if (dlen) {
+ strcpy(de->name, retname);
+ } else {
+ dlen = isonum_711(de->name_len);
+ if(sb.s_mapping == 'n') {
+ for (i = 0; i < dlen; i++) {
+ c = de->name[i];
+ if (c >= 'A' && c <= 'Z') c |= 0x20; /* lower case */
+ if (c == ';' && i == dlen-2
+ && de->name[i+1] == '1') {
+ dlen -= 2;
+ break;
+ }
+ if (c == ';') c = '.';
+ de->name[i] = c;
+ }
+ /* This allows us to match with and without a trailing
+ period. */
+ if(de->name[dlen-1] == '.' && namelen == dlen-1)
+ dlen--;
+ }
+ }
+ }
+ /*
+ * Skip hidden or associated files unless unhide is set
+ */
+ match = 0;
+ if( !(de->flags[-sb.s_high_sierra] & 5)
+ || sb.s_unhide == 'y' )
+ {
+ match = iso_match(namelen, name, de->name, dlen);
+ }
+
+ if (cpnt) cpnt = NULL;
+
+ if (match) {
+ if ((int) inode_number == -1) {
+ /* Should never happen */
+ printf("iso9660: error inode_number = -1\n");
+ return -1;
+ }
+
+ *ino = inode_number;
+ *ino_back = backlink;
+#ifdef DEBUG_ISO
+ printf("iso_find_entry returning successfully (ino = %d)\n",
+ inode_number);
+#endif
+ return 0;
+ }
+ }
+#ifdef DEBUG_ISO
+ printf("iso_find_entry returning unsuccessfully (ino = %d)\n",
+ inode_number);
+#endif
+ return -1;
+}
+
+
+/*
+ * Look up name in the current directory and return its corresponding
+ * inode if it can be found.
+ *
+ */
+struct iso_inode *
+iso_lookup(struct iso_inode *dir, const char *name)
+{
+ struct inode_table_entry *itp = (struct inode_table_entry *) dir;
+ unsigned long ino, ino_back;
+ struct iso_inode *result = NULL;
+ int first, last;
+
+#ifdef DEBUG_ISO
+ printf("iso_lookup: %s\n", name);
+#endif
+
+ /* is the current inode a directory? */
+ if (!S_ISDIR(itp->mode)) {
+#ifdef DEBUG_ISO
+ printf("iso_lookup: inode %d not a directory\n", itp->inumber);
+#endif
+ iso_iput(dir);
+ return NULL;
+ }
+
+ /* work through the name finding each directory in turn */
+ ino = 0;
+ first = last = 0;
+ while (last < (int) strlen(name)) {
+ if (name[last] == '/') {
+ if (iso_find_entry(dir, &name[first], last - first,
+ &ino, &ino_back))
+ return NULL;
+ /* throw away the old directory inode, we
+ don't need it anymore */
+ iso_iput(dir);
+
+ if (!(dir = iso_iget(ino)))
+ return NULL;
+ first = last + 1;
+ last = first;
+ } else
+ last++;
+ }
+ {
+ int rv;
+ if ((rv = iso_find_entry(dir, &name[first], last - first, &ino, &ino_back))) {
+ iso_iput(dir);
+ return NULL;
+ }
+ }
+ if (!(result = iso_iget(ino))) {
+ iso_iput(dir);
+ return NULL;
+ }
+ /*
+ * We need this backlink for the ".." entry unless the name
+ * that we are looking up traversed a mount point (in which
+ * case the inode may not even be on an iso9660 filesystem,
+ * and writing to u.isofs_i would only cause memory
+ * corruption).
+ */
+ result->i_backlink = ino_back;
+
+ iso_iput(dir);
+ return result;
+}
+
+/* follow a symbolic link, returning the inode of the file it points to */
+static struct iso_inode *
+iso_follow_link(struct iso_inode *from, const char *basename)
+{
+ struct inode_table_entry *itp = (struct inode_table_entry *)from;
+ struct iso_inode *root = iso_iget(root_inode);
+ /* HK: iso_iget expects an "int" but root_inode is "long" ?? */
+ struct iso_inode *result = NULL;
+ char *linkto;
+
+#ifdef DEBUG_ISO
+ printf("iso_follow_link(%s): ",basename);
+#endif
+
+ if (!S_ISLNK(itp->mode)) /* Hey, that's not a link! */
+ return NULL;
+
+ if (!itp->size)
+ return NULL;
+
+ if (!(linkto = get_rock_ridge_symlink(from)))
+ return NULL;
+
+ linkto[itp->size]='\0';
+#ifdef DEBUG_ISO
+ printf("%s->%s\n",basename,linkto ? linkto : "[failed]");
+#endif
+
+ /* Resolve relative links. */
+
+ if (linkto[0] !='/') {
+ char *end = strrchr(basename, '/');
+ if (end) {
+ char fullname[(end - basename + 1) + strlen(linkto) + 1];
+ strncpy(fullname, basename, end - basename + 1);
+ fullname[end - basename + 1] = '\0';
+ strcat(fullname, linkto);
+#ifdef DEBUG_ISO
+ printf("resolved to %s\n", fullname);
+#endif
+ result = iso_lookup(root,fullname);
+ } else {
+ /* Assume it's in the root */
+ result = iso_lookup(root,linkto);
+ }
+ } else {
+ result = iso_lookup(root,linkto);
+ }
+ free(linkto);
+ iso_iput(root);
+ return result;
+}
+
+/*
+ * look if the driver can tell the multi session redirection value
+ */
+static inline unsigned int
+iso_get_last_session (void)
+{
+#ifdef DEBUG_ISO
+ printf("iso_get_last_session() called\n");
+#endif
+ return 0;
+}
+
+
+int
+iso_read_super (void *data, int silent)
+{
+ static int first_time = 1;
+ int high_sierra;
+ unsigned int iso_blknum, vol_desc_start;
+ char rock = 'y';
+ int i;
+
+ struct iso_volume_descriptor *vdp;
+ struct hs_volume_descriptor *hdp;
+
+ struct iso_primary_descriptor *pri = NULL;
+ struct hs_primary_descriptor *h_pri = NULL;
+
+ struct iso_directory_record *rootp;
+
+ /* Initialize the inode table */
+ for (i = 0; i < MAX_OPEN_FILES; i++) {
+ inode_table[i].free = 1;
+ inode_table[i].inumber = 0;
+ }
+
+#ifdef DEBUG_ISO
+ printf("iso_read_super() called\n");
+#endif
+
+ /* set up the block size */
+ sb.s_blocksize = 1024;
+ sb.s_blocksize_bits = 10;
+
+ sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
+
+ vol_desc_start = iso_get_last_session();
+
+ for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100;
+ iso_blknum++) {
+#ifdef DEBUG_ISO
+ printf("iso_read_super: iso_blknum=%d\n", iso_blknum);
+#endif
+ if (iso_dev_read(data_block, iso_blknum * 2048,
+ sb.s_blocksize) != sb.s_blocksize)
+ {
+ printf("iso_read_super: bread failed, dev "
+ "iso_blknum %d\n", iso_blknum);
+ return -1;
+ }
+ vdp = (struct iso_volume_descriptor *)data_block;
+ hdp = (struct hs_volume_descriptor *)data_block;
+
+ if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
+ if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
+ return -1;
+ if (isonum_711 (hdp->type) == ISO_VD_END)
+ return -1;
+
+ sb.s_high_sierra = 1;
+ high_sierra = 1;
+ rock = 'n';
+ h_pri = (struct hs_primary_descriptor *)vdp;
+ break;
+ }
+
+ if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
+ if (isonum_711 (vdp->type) != ISO_VD_PRIMARY)
+ return -1;
+ if (isonum_711 (vdp->type) == ISO_VD_END)
+ return -1;
+
+ pri = (struct iso_primary_descriptor *)vdp;
+ break;
+ }
+ }
+ if(iso_blknum == vol_desc_start + 100) {
+ if (!silent)
+ printf("iso: Unable to identify CD-ROM format.\n");
+ return -1;
+ }
+
+ if (high_sierra) {
+ rootp = (struct iso_directory_record *)
+ h_pri->root_directory_record;
+#if 0
+//See http://www.y-adagio.com/public/standards/iso_cdromr/sect_1.htm
+//Section 4.16 and 4.17 for explanation why this check is invalid
+ if (isonum_723 (h_pri->volume_set_size) != 1) {
+ printf("Multi-volume disks not (yet) supported.\n");
+ return -1;
+ };
+#endif
+ sb.s_nzones = isonum_733 (h_pri->volume_space_size);
+ sb.s_log_zone_size =
+ isonum_723 (h_pri->logical_block_size);
+ sb.s_max_size = isonum_733(h_pri->volume_space_size);
+ } else {
+ rootp = (struct iso_directory_record *)
+ pri->root_directory_record;
+#if 0
+//See http://www.y-adagio.com/public/standards/iso_cdromr/sect_1.htm
+//Section 4.16 and 4.17 for explanation why this check is invalid
+ if (isonum_723 (pri->volume_set_size) != 1) {
+ printf("Multi-volume disks not (yet) supported.\n");
+ return -1;
+ }
+#endif
+ sb.s_nzones = isonum_733 (pri->volume_space_size);
+ sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
+ sb.s_max_size = isonum_733(pri->volume_space_size);
+ }
+
+ sb.s_ninodes = 0; /* No way to figure this out easily */
+
+ /* RDE: convert log zone size to bit shift */
+
+ switch (sb.s_log_zone_size) {
+ case 512: sb.s_log_zone_size = 9; break;
+ case 1024: sb.s_log_zone_size = 10; break;
+ case 2048: sb.s_log_zone_size = 11; break;
+
+ default:
+ printf("Bad logical zone size %ld\n", sb.s_log_zone_size);
+ return -1;
+ }
+
+ /* RDE: data zone now byte offset! */
+
+ sb.s_firstdatazone = (isonum_733( rootp->extent)
+ << sb.s_log_zone_size);
+ /*
+ * The CDROM is read-only, has no nodes (devices) on it, and
+ * since all of the files appear to be owned by root, we
+ * really do not want to allow suid. (suid or devices will
+ * not show up unless we have Rock Ridge extensions).
+ */
+ if (first_time) {
+ first_time = 0;
+ printf("iso: Max size:%ld Log zone size:%ld\n",
+ sb.s_max_size, 1UL << sb.s_log_zone_size);
+ printf("iso: First datazone:%ld Root inode number %d\n",
+ sb.s_firstdatazone >> sb.s_log_zone_size,
+ isonum_733 (rootp->extent) << sb.s_log_zone_size);
+ if (high_sierra)
+ printf("iso: Disc in High Sierra format.\n");
+ }
+
+ /* set up enough so that it can read an inode */
+
+ sb.s_mapping = 'n';
+ sb.s_rock = (rock == 'y' ? 1 : 0);
+ sb.s_conversion = 'b';
+ sb.s_cruft = 'n';
+ sb.s_unhide = 'n';
+ /*
+ * It would be incredibly stupid to allow people to mark every file
+ * on the disk as suid, so we merely allow them to set the default
+ * permissions.
+ */
+ sb.s_mode = S_IRUGO & 0777;
+
+ /* return successfully */
+ root_inode = isonum_733 (rootp->extent) << sb.s_log_zone_size;
+ /* HK: isonum_733 returns an "int" but root_inode is a long ? */
+ return 0;
+}
+
+
+int
+iso_bread (int fd, long blkno, long nblks, char * buffer)
+{
+ struct iso_inode *inode;
+
+ /* find the inode for this file */
+ inode = &inode_table[fd].inode;
+ return iso_breadi(inode, blkno, nblks, buffer);
+}
+
+
+int
+iso_open (const char *filename)
+{
+ struct iso_inode *inode, *oldinode;
+ struct iso_inode *root;
+
+ /* get the root directory */
+ root = iso_iget(root_inode);
+/* HK: iso_iget expects an "int" but root_inode is "long" ?? */
+ if (!root) {
+ printf("iso9660: get root inode failed\n");
+ return -1;
+ }
+
+ /* lookup the file */
+ inode = iso_lookup(root, filename);
+
+#ifdef DEBUG_ISO
+ if (inode && S_ISLNK(((struct inode_table_entry *)inode)->mode)) {
+ printf("%s is a link (len %u)\n",filename,
+ ((struct inode_table_entry *)inode)->size);
+ } else {
+ printf("%s is not a link\n",filename);
+ }
+#endif
+
+ while ((inode) && (S_ISLNK(((struct inode_table_entry *)inode)->mode))) {
+ oldinode = inode;
+ inode = iso_follow_link(oldinode, filename);
+ iso_iput(oldinode);
+ }
+ if (inode == NULL)
+ return -1;
+ else {
+ struct inode_table_entry * itp =
+ (struct inode_table_entry *) inode;
+ return (itp - inode_table);
+ }
+}
+
+
+void
+iso_close (int fd)
+{
+ iso_iput(&inode_table[fd].inode);
+}
+
+
+long
+iso_map (int fd, long block)
+{
+ return iso_bmap(&inode_table[fd].inode, block) * sb.s_blocksize;
+}
+
+#include <linux/stat.h>
+int
+iso_fstat (int fd, struct stat * buf)
+{
+ struct inode_table_entry * ip = inode_table + fd;
+
+ if (fd >= MAX_OPEN_FILES)
+ return -1;
+
+ memset(buf, 0, sizeof(struct stat));
+ /* fill in relevant fields */
+ buf->st_ino = ip->inumber;
+ buf->st_mode = ip->mode;
+ buf->st_nlink = ip->nlink;
+ buf->st_size = ip->size;
+ return 0;
+}
+
+/*
+ * NOTE: mixing calls to this and calls to any other function that reads from
+ * the filesystem will clobber the buffers and cause much wailing and gnashing
+ * of teeth.
+ *
+ * Sorry this function is so ugly. It was written as a way for me to
+ * learn how the ISO filesystem stuff works.
+ *
+ * Will Woods, 2/2001
+ *
+ * Uses data_block
+ */
+char *iso_readdir_i(int fd, int rewind) {
+ struct inode_table_entry *itp = &(inode_table[fd]);
+ struct iso_directory_record *dirent = 0;
+ unsigned int fraglen = 0, block, dirent_len, name_len = 0, oldoffset;
+ static unsigned int blockoffset = 0, diroffset = 0;
+
+ if (!S_ISDIR(itp->mode)) {
+ printf("Not a directory\n");
+ return NULL;
+ }
+
+ /* Initial read to this directory, get the first block */
+ if (rewind) {
+ blockoffset = diroffset = 0;
+ block = iso_bmap(&itp->inode,0);
+#ifdef DEBUG_ISO
+ printf("fd #%d, inode %d, first_extent %d, block %u\n",
+ fd,itp->inumber,itp->inode.i_first_extent,block);
+#endif
+ if (!block) return NULL;
+
+ if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
+ != sb.s_blocksize) return NULL;
+ }
+
+ /* keep doing this until we get a filename or we fail */
+ while (!name_len) {
+ /* set up our dirent pointer into the block of data we've read */
+ dirent = (struct iso_directory_record *) (data_block + blockoffset);
+ dirent_len = isonum_711(dirent->length);
+#ifdef DEBUG_ISO
+ printf("diroffset=%u, blockoffset=%u, length=%u\n",
+ diroffset,blockoffset,dirent_len);
+#endif
+
+ /* End of directory listing or end of sector */
+ if (dirent_len == 0) {
+ /* round diroffset up to the nearest blocksize */
+ diroffset = ((diroffset & ~(ISOFS_BLOCK_SIZE - 1))
+ + ISOFS_BLOCK_SIZE);
+#ifdef DEBUG_ISO
+ printf("dirent_len == 0. diroffset=%u, itp->size=%u. ",
+ diroffset,itp->size);
+#endif
+ if (diroffset >= itp->size) {
+#ifdef DEBUG_ISO
+ printf("End of directory.\n");
+#endif
+ return NULL;
+ } else {
+#ifdef DEBUG_ISO
+ printf("End of sector. Need next block.\n");
+#endif
+ /* Get the next block. */
+ block = iso_bmap(&itp->inode, diroffset>>sb.s_blocksize_bits);
+ if (!block) return NULL;
+ if (iso_dev_read(data_block, block * sb.s_blocksize, sb.s_blocksize)
+ != sb.s_blocksize) return NULL;
+
+ /* set the offsets and the pointers properly */
+ blockoffset = 0;
+ dirent = (struct iso_directory_record *) data_block;
+ dirent_len = isonum_711(dirent->length);
+#ifdef DEBUG_ISO
+ printf("diroffset=%u, blockoffset=%u, length=%u\n",
+ diroffset,blockoffset,dirent_len);
+#endif
+ }
+ }
+
+ /* update the offsets for the next read */
+ oldoffset = blockoffset;
+ blockoffset += dirent_len;
+ diroffset += dirent_len;
+
+ /*
+ * directory entry spans two blocks -
+ * get next block and glue the two halves together
+ */
+ if (blockoffset >= sb.s_blocksize) {
+ fraglen = sb.s_blocksize - oldoffset;
+#ifdef DEBUG_ISO
+ printf("fragmented block: blockoffset = %u, fraglen = %u\n",
+ blockoffset, fraglen);
+#endif
+ /* copy the fragment at end of old block to front of new buffer */
+ memcpy(big_data_block, data_block + oldoffset, fraglen);
+
+ /* read the next block into the buffer after the old fragment */
+ block = iso_bmap(&itp->inode, diroffset >> sb.s_blocksize_bits);
+ if (!block) return NULL;
+ if (iso_dev_read(big_data_block + fraglen, block * sb.s_blocksize, sb.s_blocksize)
+ != sb.s_blocksize) return NULL;
+#ifdef DEBUG_ISO
+ printf("read %u bytes from offset %u\n",
+ sb.s_blocksize, block * sb.s_blocksize);
+#endif
+
+ blockoffset = 0;
+ dirent = (struct iso_directory_record *) big_data_block;
+ }
+
+ /*
+ * Everything's cool, let's get the filename.
+ * First we need to figure out the length.
+ */
+ name_len = isonum_711(dirent->name_len);
+#ifdef DEBUG_ISO
+ if (name_len==0) printf("dirent->name_len = 0, skipping.\n");
+#endif
+
+ /* skip '.' and '..' */
+ if (name_len == 1) {
+ if (dirent->name[0] == (char)0) name_len = 0;
+ if (dirent->name[0] == (char)1) name_len = 0;
+ }
+
+
+ if (sb.s_unhide == 'n') {
+ /* sb.s_high_sierra is the offset for the position of the flags.
+ this adjusts for differences between iso9660 and high sierra.
+
+ if bit 0 (exists) or bit 2 (associated) are set, we ignore
+ this record. */
+ if (dirent->flags[-sb.s_high_sierra] & 5) name_len = 0;
+ }
+
+ /* if we have a real filename here.. */
+ if (name_len) {
+ /* should be sufficient, since get_rock_ridge_filename truncates
+ at 254 characters */
+ char rrname[256];
+ if ((name_len = get_rock_ridge_filename(dirent, rrname, &itp->inode))) {
+ rrname[name_len] = '\0';
+ /* it's okay if rrname is longer than dirent->name, because
+ we're just overwriting parts of the now-useless dirent */
+ strcpy(dirent->name, rrname);
+ } else {
+ int i;
+ char c;
+ if (sb.s_mapping == 'n') { /* downcase the name */
+ for (i = 0; i < name_len; i++) {
+ c = dirent->name[i];
+
+ /* lower case */
+ if ((c >= 'A') && (c <= 'Z')) c |= 0x20;
+
+ /* Drop trailing '.;1' */
+ if ((c == '.') && (i == name_len-3) &&
+ (dirent->name[i+1] == ';') &&
+ (dirent->name[i+2] == '1')) {
+ name_len -= 3 ; break;
+ }
+
+ /* Drop trailing ';1' */
+ if ((c == ';') && (i == name_len-2) &&
+ (dirent->name[i+1] == '1')) {
+ name_len -= 2; break;
+ }
+
+ /* convert ';' to '.' */
+ if (c == ';')
+ c = '.';
+
+ dirent->name[i] = c;
+ }
+ dirent->name[name_len] = '\0';
+ }
+ }
+ }
+ /* now that we're done using it, and it's smaller than a full block,
+ * copy big_data_block back into data_block */
+ if (fraglen) {
+ int len = sb.s_blocksize - dirent_len;
+ memcpy(data_block, big_data_block + dirent_len, len);
+#ifdef DEBUG_ISO
+ printf("copied %u bytes of data back to data_block\n", len);
+#endif
+ blockoffset = 0;
+ fraglen = 0;
+ }
+ }
+ return dirent->name;
+}
+
+/**********************************************************************
+ *
+ * Rock Ridge functions and definitions, from the Linux kernel source.
+ * linux/fs/isofs/rock.c, (c) 1992, 1993 Eric Youngdale.
+ *
+ **********************************************************************/
+
+#define SIG(A,B) ((A << 8) | B)
+
+/* This is a way of ensuring that we have something in the system
+ use fields that is compatible with Rock Ridge */
+#define CHECK_SP(FAIL) \
+ if(rr->u.SP.magic[0] != 0xbe) FAIL; \
+ if(rr->u.SP.magic[1] != 0xef) FAIL;
+
+/* We define a series of macros because each function must do exactly the
+ same thing in certain places. We use the macros to ensure that everything
+ is done correctly */
+
+#define CONTINUE_DECLS \
+ int cont_extent = 0, cont_offset = 0, cont_size = 0; \
+ void * buffer = 0
+
+#define CHECK_CE \
+ {cont_extent = isonum_733(rr->u.CE.extent); \
+ cont_offset = isonum_733(rr->u.CE.offset); \
+ cont_size = isonum_733(rr->u.CE.size);}
+
+#define SETUP_ROCK_RIDGE(DE,CHR,LEN) \
+ {LEN= sizeof(struct iso_directory_record) + DE->name_len[0]; \
+ if(LEN & 1) LEN++; \
+ CHR = ((unsigned char *) DE) + LEN; \
+ LEN = *((unsigned char *) DE) - LEN;}
+
+#define MAYBE_CONTINUE(LABEL) \
+ {if (buffer) free(buffer); \
+ if (cont_extent){ \
+ buffer = malloc(cont_size); \
+ if (!buffer) goto out; \
+ if (iso_dev_read(buffer, cont_extent * ISOFS_BLOCK_SIZE + cont_offset, cont_size) \
+ == cont_size) { \
+ chr = (unsigned char *) buffer; \
+ len = cont_size; \
+ cont_extent = cont_size = cont_offset = 0; \
+ goto LABEL; \
+ }; \
+ printf("Unable to read rock-ridge attributes\n"); \
+ }}
+
+int get_rock_ridge_filename(struct iso_directory_record * de,
+ char * retname,
+ struct iso_inode * inode)
+{
+ int len;
+ unsigned char * chr;
+ int retnamlen = 0, truncate=0;
+ int cont_extent = 0, cont_offset = 0, cont_size = 0;
+ void *buffer = 0;
+
+ /* No rock ridge? well then... */
+ if (!sb.s_rock) return 0;
+ *retname = '\0';
+
+ len = sizeof(struct iso_directory_record) + isonum_711(de->name_len);
+ if (len & 1) len++;
+ chr = ((unsigned char *) de) + len;
+ len = *((unsigned char *) de) - len;
+ {
+ struct rock_ridge * rr;
+ int sig;
+
+ repeat:
+ while (len > 1){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len == 0) break; /* Something got screwed up here */
+
+ sig = (chr[0] << 8) + chr[1];
+ chr += rr->len;
+ len -= rr->len;
+
+ switch(sig){
+ case SIG('R','R'):
+ if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
+ break;
+ case SIG('S','P'):
+ if (rr->u.SP.magic[0] != 0xbe ||
+ rr->u.SP.magic[1] != 0xef)
+ goto out;
+ break;
+ case SIG('C','E'):
+ cont_extent = isonum_733(rr->u.CE.extent);
+ cont_offset = isonum_733(rr->u.CE.offset);
+ cont_size = isonum_733(rr->u.CE.size);
+ break;
+ case SIG('N','M'):
+ if (truncate) break;
+ /*
+ * If the flags are 2 or 4, this indicates '.' or '..'.
+ * We don't want to do anything with this, because it
+ * screws up the code that calls us. We don't really
+ * care anyways, since we can just use the non-RR
+ * name.
+ */
+ if (rr->u.NM.flags & 6) {
+ break;
+ }
+
+ if (rr->u.NM.flags & ~1) {
+ printf("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
+ break;
+ };
+ if((strlen(retname) + rr->len - 5) >= 254) {
+ int i = 254-strlen(retname);
+ strncat(retname, rr->u.NM.name, i);
+ retnamlen += i;
+ truncate = 1;
+ break;
+ };
+ strncat(retname, rr->u.NM.name, rr->len - 5);
+ retnamlen += rr->len - 5;
+ break;
+ case SIG('R','E'):
+ goto out;
+ default:
+ break;
+ }
+ };
+ }
+ if (buffer) free(buffer);
+ if (cont_extent) { /* we had a continued record */
+ buffer = malloc(cont_size);
+ if (!buffer) goto out;
+ if (iso_dev_read(buffer, cont_extent * ISOFS_BLOCK_SIZE + cont_offset, cont_size)
+ != cont_size) goto out;
+ chr = buffer + cont_offset;
+ len = cont_size;
+ cont_extent = cont_size = cont_offset = 0;
+ goto repeat;
+ }
+ return retnamlen; /* If 0, this file did not have a NM field */
+ out:
+ if (buffer) free(buffer);
+ return 0;
+}
+
+static int parse_rock_ridge_inode(struct iso_directory_record * de,
+ struct iso_inode * inode){
+ int len;
+ unsigned char *chr;
+ int symlink_len = 0;
+ struct inode_table_entry *itp = (struct inode_table_entry *) inode;
+ CONTINUE_DECLS;
+
+#ifdef DEBUG_ROCK
+ printf("parse_rock_ridge_inode(%u)\n",itp->inumber);
+#endif
+
+ if (!sb.s_rock) return 0;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ int sig;
+ /* struct iso_inode * reloc; */
+ struct rock_ridge * rr;
+ int rootflag;
+
+ while (len > 1){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len == 0) goto out; /* Something got screwed up here */
+ sig = (chr[0] << 8) + chr[1];
+ chr += rr->len;
+ len -= rr->len;
+
+ switch(sig){
+ case SIG('R','R'):
+#ifdef DEBUG_ROCK
+ printf("RR ");
+#endif
+ if((rr->u.RR.flags[0] &
+ (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
+ break;
+ case SIG('S','P'):
+#ifdef DEBUG_ROCK
+ printf("SP ");
+#endif
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','E'):
+#ifdef DEBUG_ROCK
+ printf("CE ");
+#endif
+ CHECK_CE;
+ break;
+ case SIG('E','R'):
+#ifdef DEBUG_ROCK
+ printf("ISO 9660 Extensions: ");
+ { int p;
+ for(p=0;p<rr->u.ER.len_id;p++) printf("%c",rr->u.ER.data[p]);
+ };
+ printf("\n");
+#endif
+ break;
+ case SIG('P','X'):
+#ifdef DEBUG_ROCK
+ printf("PX ");
+#endif
+ itp->mode = isonum_733(rr->u.PX.mode);
+ itp->nlink = isonum_733(rr->u.PX.n_links);
+ /* Ignore uid and gid. We're only a simple bootloader, after all. */
+ break;
+ case SIG('P','N'):
+ /* Ignore device files. */
+ break;
+ case SIG('T','F'):
+ /* create/modify/access times are uninteresting to us. */
+ break;
+ case SIG('S','L'):
+#ifdef DEBUG_ROCK
+ printf("SL ");
+#endif
+ {int slen;
+ struct SL_component * slp;
+ struct SL_component * oldslp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ itp->size = symlink_len;
+ while (slen > 1){
+ rootflag = 0;
+ switch(slp->flags &~1){
+ case 0:
+ itp->size += slp->len;
+ break;
+ case 2:
+ itp->size += 1;
+ break;
+ case 4:
+ itp->size += 2;
+ break;
+ case 8:
+ rootflag = 1;
+ itp->size += 1;
+ break;
+ default:
+ printf("Symlink component flag not implemented\n");
+ };
+ slen -= slp->len + 2;
+ oldslp = slp;
+ slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+ if(slen < 2) {
+ if( ((rr->u.SL.flags & 1) != 0)
+ && ((oldslp->flags & 1) == 0) ) itp->size += 1;
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if( (!rootflag)
+ && ((oldslp->flags & 1) == 0) ) itp->size += 1;
+ }
+ }
+ symlink_len = itp->size;
+ break;
+ case SIG('R','E'):
+ printf("Attempt to read inode for relocated directory\n");
+ goto out;
+ case SIG('C','L'):
+#ifdef DEBUG_ROCK
+ printf("CL(!) ");
+#endif
+ /* I'm unsure as to the function of this signature.
+ We'll ignore it and hope that everything will be OK.
+ */
+#if 0
+#ifdef DEBUG
+ printf("RR CL (%x)\n",inode->i_ino);
+#endif
+ inode->inode.first_extent = isonum_733(rr->u.CL.location);
+ reloc = iso_iget(inode->i_sb,
+ (inode->u.isofs_i.i_first_extent <<
+ inode -> i_sb -> u.isofs_sb.s_log_zone_size));
+ if (!reloc)
+ goto out;
+ inode->mode = reloc->mode;
+ inode->nlink = reloc->nlink;
+ inode->size = reloc->size;
+ iso_iput(reloc);
+#endif /* 0 */
+ break;
+ default:
+ break;
+ }
+ };
+ }
+ MAYBE_CONTINUE(repeat);
+#ifdef DEBUG_ROCK
+ printf("\nparse_rock_ridge_inode(): ok\n");
+#endif
+ return 1;
+ out:
+ if(buffer) free(buffer);
+#ifdef DEBUG_ROCK
+ printf("\nparse_rock_ridge_inode(): failed\n");
+#endif
+ return 0;
+}
+
+/* Returns the name of the file that this inode is symlinked to. This is
+ in malloc memory, so we have to free it when we're done */
+
+static char * get_rock_ridge_symlink(struct iso_inode *inode)
+{
+ int blocksize = ISOFS_BLOCK_SIZE;
+ int blockbits = ISOFS_BLOCK_BITS;
+ char * rpnt = NULL;
+ unsigned char * pnt;
+ struct iso_directory_record * raw_inode;
+ struct inode_table_entry *itp = (struct inode_table_entry *)inode;
+ CONTINUE_DECLS;
+ int block, blockoffset;
+ int sig;
+ int rootflag;
+ int len;
+ unsigned char * chr, * buf = NULL;
+ struct rock_ridge * rr;
+
+#ifdef DEBUG_ROCK
+ printf("get_rock_ridge_symlink(%u): link is %u bytes long\n",itp->inumber, itp->size);
+#endif
+
+ if (!sb.s_rock) goto out;
+
+ block = itp->inumber >> blockbits;
+ blockoffset = itp->inumber & (blocksize - 1);
+
+ buf=malloc(blocksize);
+
+ if (iso_dev_read(buf, block << blockbits, blocksize) != blocksize)
+ goto out_noread;
+
+ pnt = ((unsigned char *) buf) + blockoffset;
+
+ raw_inode = ((struct iso_directory_record *) pnt);
+
+ /*
+ * If we go past the end of the buffer, there is some sort of error.
+ */
+ if (blockoffset + *pnt > blocksize)
+ goto out_bad_span;
+
+ /* Now test for possible Rock Ridge extensions which will override some of
+ these numbers in the inode structure. */
+
+ SETUP_ROCK_RIDGE(raw_inode, chr, len);
+
+ repeat:
+ while (len > 1){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len == 0) goto out; /* Something got screwed up here */
+ sig = (chr[0] << 8) + chr[1];
+ chr += rr->len;
+ len -= rr->len;
+
+#ifdef DEBUG_ROCK
+ printf("%c%c ",chr[0],chr[1]);
+#endif
+ switch(sig){
+ case SIG('R','R'):
+ if((rr->u.RR.flags[0] & RR_SL) == 0) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('S','L'):
+ {int slen;
+ struct SL_component * oldslp;
+ struct SL_component * slp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ while (slen > 1){
+ if (!rpnt){
+ rpnt = (char *) malloc (itp->size +1);
+ if (!rpnt) goto out;
+ *rpnt = 0;
+ };
+ rootflag = 0;
+ switch(slp->flags &~1){
+ case 0:
+ strncat(rpnt,slp->text, slp->len);
+ break;
+ case 2:
+ strcat(rpnt,".");
+ break;
+ case 4:
+ strcat(rpnt,"..");
+ break;
+ case 8:
+ rootflag = 1;
+ strcat(rpnt,"/");
+ break;
+ default:
+#ifdef DEBUG_ROCK
+ printf("Symlink component flag not implemented (%d)\n",slen);
+#endif
+ break;
+ };
+ slen -= slp->len + 2;
+ oldslp = slp;
+ slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+ if(slen < 2) {
+ /*
+ * If there is another SL record, and this component record
+ * isn't continued, then add a slash.
+ */
+ if( ((rr->u.SL.flags & 1) != 0)
+ && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if( (!rootflag)
+ && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");
+
+ };
+ break;
+ case SIG('C','E'):
+ CHECK_CE; /* This tells is if there is a continuation record */
+ break;
+ default:
+ break;
+ }
+ };
+ };
+ MAYBE_CONTINUE(repeat);
+
+ out_freebh:
+#ifdef DEBUG_ROCK
+ printf("\nget_rock_ridge_symlink() exiting\n");
+#endif
+ if (buf)
+ free(buf);
+ return rpnt;
+
+ /* error exit from macro */
+out:
+#ifdef DEBUG_ROCK
+ printf("abort");
+#endif
+ if(buffer)
+ free(buffer);
+ if(rpnt)
+ free(rpnt);
+ rpnt = NULL;
+ goto out_freebh;
+out_noread:
+ printf("unable to read block");
+ goto out_freebh;
+out_bad_span:
+ printf("symlink spans iso9660 blocks\n");
+ goto out_freebh;
+}
+
+
+
diff --git a/package/aboot/src/lib/memcpy.c b/package/aboot/src/lib/memcpy.c
new file mode 100644
index 000000000..38fd642ab
--- /dev/null
+++ b/package/aboot/src/lib/memcpy.c
@@ -0,0 +1,23 @@
+/*
+ * aboot/lib/memcpy.c
+ *
+ * Copyright (c) 1995 David Mosberger (davidm@cs.arizona.edu)
+ */
+#include <linux/types.h>
+#include <stddef.h>
+
+/*
+ * Booting is I/O bound so rather than a time-optimized, we want
+ * a space-optimized memcpy. Not that the rest of the loader
+ * were particularly small, though...
+ */
+void *__memcpy(void *dest, const void *source, size_t n)
+{
+ char *dst = dest;
+ const char *src = source;
+
+ while (n--) {
+ *dst++ = *src++;
+ }
+ return dest;
+}
diff --git a/package/aboot/src/lib/memset.c b/package/aboot/src/lib/memset.c
new file mode 100644
index 000000000..01ebe6758
--- /dev/null
+++ b/package/aboot/src/lib/memset.c
@@ -0,0 +1,28 @@
+/*
+ * aboot/lib/memset.c
+ *
+ * Copyright (c) 1995 David Mosberger (davidm@cs.arizona.edu)
+ */
+#include <linux/types.h>
+#include <stddef.h>
+
+/*
+ * Booting is I/O bound so rather than a time-optimized, we want
+ * a space-optimized memcpy. Not that the rest of the loader
+ * were particularly small, though...
+ */
+void *__memset(void *s, char c, size_t n)
+{
+ char *dst = s;
+
+ while (n--) {
+ *dst++ = c;
+ }
+ return s;
+}
+
+
+void *__constant_c_memset(void *dest, char c, size_t n)
+{
+ return __memset(dest, c, n);
+}
diff --git a/package/aboot/src/lib/string.c b/package/aboot/src/lib/string.c
new file mode 100644
index 000000000..7b6130b11
--- /dev/null
+++ b/package/aboot/src/lib/string.c
@@ -0,0 +1,252 @@
+/*
+ * aboot/lib/string.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ */
+
+#include <stddef.h>
+#include <linux/types.h>
+
+char * ___strtok = NULL;
+
+char * strcpy(char * dest,const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return tmp;
+}
+
+char * strncpy(char * dest,const char *src,size_t count)
+{
+ char *tmp = dest;
+
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */;
+
+ return tmp;
+}
+
+char * strcat(char * dest, const char * src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+
+char * strncat(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ if (count) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--count == 0)
+ break;
+ }
+ }
+
+ return tmp;
+}
+
+int strcmp(const char * cs,const char * ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ }
+
+ return __res;
+}
+
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+ register signed char __res = 0;
+
+ while (count) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ count--;
+ }
+
+ return __res;
+}
+
+char * strchr(const char * s,char c)
+{
+ for(; *s != c; ++s)
+ if (*s == '\0')
+ return NULL;
+ return (char *) s;
+}
+
+char * strrchr(const char * s,char c)
+{
+ const char * r = NULL;
+ while (*s++)
+ if (*s == c) r = s;
+ return (char *) r;
+}
+
+size_t strlen(const char * s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+
+size_t strnlen(const char * s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0' && count--; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
+
+char * strpbrk(const char * cs,const char * ct)
+{
+ const char *sc1,*sc2;
+
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
+}
+
+char * strtok(char * s,const char * ct)
+{
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}
+
+void * memset(void * s, int c, size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+
+char * bcopy(const char * src, char * dest, int count)
+{
+ char *tmp = dest;
+
+ while (count--)
+ *tmp++ = *src++;
+
+ return dest;
+}
+
+void * memcpy(void * dest,const void *src,size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+
+void * memmove(void * dest,const void *src,size_t count)
+{
+ char *tmp, *s;
+
+ if (dest <= src) {
+ tmp = (char *) dest;
+ s = (char *) src;
+ while (count--)
+ *tmp++ = *s++;
+ }
+ else {
+ tmp = (char *) dest + count;
+ s = (char *) src + count;
+ while (count--)
+ *--tmp = *--s;
+ }
+
+ return dest;
+}
+
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+ const unsigned char *su1, *su2;
+ signed char res = 0;
+
+ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
+}
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+void * memscan(void * addr, unsigned char c, size_t size)
+{
+ unsigned char * p = (unsigned char *) addr;
+
+ while (size) {
+ if (*p == c)
+ return (void *) p;
+ p++;
+ size--;
+ }
+ return (void *) p;
+}
diff --git a/package/aboot/src/lib/vsprintf.c b/package/aboot/src/lib/vsprintf.c
new file mode 100644
index 000000000..d6d256c73
--- /dev/null
+++ b/package/aboot/src/lib/vsprintf.c
@@ -0,0 +1,317 @@
+/*
+ * linux/lib/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include "string.h"
+
+/* we use this so that we can do without the ctype library */
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+#define is_xdigit(c) \
+ (is_digit(c) || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
+#define is_lower(c) (((c) >= 'a') && ((c) <= 'z'))
+#define to_upper(c) ((c) + 'a' - 'A')
+
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = 0, value;
+
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && is_xdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (is_xdigit(*cp)
+ && (value =
+ (is_digit(*cp)
+ ? *cp - '0' : ((is_lower(*cp)
+ ? to_upper(*cp) : *cp) - 'A' + 10)))
+ < base)
+ {
+ result = result*base + value;
+ cp++;
+ }
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
+
+static int skip_atoi(const char **s)
+{
+ int i=0;
+
+ while (is_digit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+#define do_div(n,base) ({ \
+int __res; \
+__res = ((unsigned long) n) % (unsigned) base; \
+n = ((unsigned long) n) / (unsigned) base; \
+__res; })
+
+static char * number(char * str, long num, int base, int size, int precision
+ ,int type)
+{
+ char c,sign,tmp[66];
+ const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
+ int i;
+
+ if (type & LARGE)
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ if (type & LEFT)
+ type &= ~ZEROPAD;
+ if (base < 2 || base > 36)
+ return 0;
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN) {
+ if (num < 0) {
+ sign = '-';
+ num = -num;
+ size--;
+ } else if (type & PLUS) {
+ sign = '+';
+ size--;
+ } else if (type & SPACE) {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL) {
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ }
+ i = 0;
+ if (num == 0)
+ tmp[i++]='0';
+ else while (num != 0)
+ tmp[i++] = digits[do_div(num,base)];
+ if (i > precision)
+ precision = i;
+ size -= precision;
+ if (!(type&(ZEROPAD+LEFT)))
+ while(size-->0)
+ *str++ = ' ';
+ if (sign)
+ *str++ = sign;
+ if (type & SPECIAL) {
+ if (base==8)
+ *str++ = '0';
+ else if (base==16) {
+ *str++ = '0';
+ *str++ = digits[33];
+ }
+ }
+ if (!(type & LEFT))
+ while (size-- > 0)
+ *str++ = c;
+ while (i < precision--)
+ *str++ = '0';
+ while (i-- > 0)
+ *str++ = tmp[i];
+ while (size-- > 0)
+ *str++ = ' ';
+ return str;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long num;
+ int base = 10;
+ int i;
+ char * str;
+ char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width = -1; /* width of output field */
+ int precision = -1; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier = -1; /* 'h', 'l', or 'L' for integer fields */
+
+ for (str=buf ; *fmt ; ++fmt) {
+ if (*fmt != '%') {
+ *str++ = *fmt;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt) {
+ case '-': flags |= LEFT; goto repeat;
+ case '+': flags |= PLUS; goto repeat;
+ case ' ': flags |= SPACE; goto repeat;
+ case '#': flags |= SPECIAL; goto repeat;
+ case '0': flags |= ZEROPAD; goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (is_digit(*fmt))
+ field_width = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0) {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.') {
+ ++fmt;
+ if (is_digit(*fmt))
+ precision = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ precision = 0;
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+ qualifier = *fmt;
+ ++fmt;
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt) {
+ case 'c':
+ if (!(flags & LEFT))
+ while (--field_width > 0)
+ *str++ = ' ';
+ *str++ = (unsigned char) va_arg(args, int);
+ while (--field_width > 0)
+ *str++ = ' ';
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ if (!s)
+ s = "<NULL>";
+
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT))
+ while (len < field_width--)
+ *str++ = ' ';
+ for (i = 0; i < len; ++i)
+ *str++ = *s++;
+ while (len < field_width--)
+ *str++ = ' ';
+ continue;
+
+ case 'p':
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str,
+ (unsigned long) va_arg(args, void *), 16,
+ field_width, precision, flags);
+ continue;
+
+
+ case 'n':
+ if (qualifier == 'l') {
+ long * ip = va_arg(args, long *);
+ *ip = (str - buf);
+ } else {
+ int * ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+
+ default:
+ if (*fmt != '%')
+ *str++ = '%';
+ if (*fmt)
+ *str++ = *fmt;
+ else
+ --fmt;
+ continue;
+ }
+ if (qualifier == 'l')
+ num = va_arg(args, unsigned long);
+ else if (qualifier == 'h')
+ if (flags & SIGN)
+ num = va_arg(args, int);
+ else
+ num = va_arg(args, unsigned int);
+ else if (flags & SIGN)
+ num = va_arg(args, int);
+ else
+ num = va_arg(args, unsigned int);
+ str = number(str, num, base, field_width, precision, flags);
+ }
+ *str = '\0';
+ return str-buf;
+}
+
+int sprintf(char * buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i=vsprintf(buf,fmt,args);
+ va_end(args);
+ return i;
+}
+