Esempio n. 1
0
        /*
         * Rename columns
         *
         * ALTER TABLE table RENAME war TO peace
         */
        internal override void AlterTableRenameCol(String tableName, String oldColname, String newColname)
        //throws tinySQLException
        {
            String fullpath = dataDir + java.io.File.separator + tableName + DBFFileTable.dbfExtension;

            try
            {
                java.io.RandomAccessFile ftbl = new java.io.RandomAccessFile(fullpath, "rw");

                DBFHeader dbfHeader = new DBFHeader(ftbl); // read the first 32 bytes ...

                int locn = 0;                              // offset of the current column
                for (int iCol = 1; iCol <= dbfHeader.numFields; iCol++)
                {
                    TsColumn coldef = readColdef(ftbl, tableName, iCol, locn);
                    if (coldef.name.equals(oldColname))
                    {
                        Utils.log("Replacing column name '" + oldColname + "' with '" + newColname + "'");
                        ftbl.seek((iCol - 1) * 32 + 32);
                        ftbl.write(Utils.forceToSize(newColname,
                                                     DBFFileTable.FIELD_TYPE_INDEX - DBFFileTable.FIELD_NAME_INDEX,
                                                     (byte)0));
                        ftbl.close();
                        return;
                    }
                }
                ftbl.close();
                throw new TinySQLException("Renaming of column name '" + oldColname + "' to '" + newColname + "' failed, no column '" + oldColname + "' found");
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 2
0
        /**
         * Retrieve a record (=row)
         * @param dbfHeader dBase meta info
         * @param recordNumber starts with 1
         * @return the String with the complete record
         *         or null if the record is marked as deleted
         * @see tinySQLTable#GetCol
         */
        public String GetRecord(java.io.RandomAccessFile ff, DBFHeader dbfHeader, int recordNumber) //throws tinySQLException
        {
            if (recordNumber < 1)
            {
                throw new TinySQLException("Internal error - current record number < 1");
            }

            try
            {
                // seek the starting offset of the current record,
                // as indicated by recordNumber
                ff.seek(dbfHeader.headerLength + (recordNumber - 1) * dbfHeader.recordLength);

                // fully read a byte array out to the length of
                // the record.
                byte[] b = new byte[dbfHeader.recordLength];
                ff.readFully(b);

                // make it into a String
                String record = new java.lang.StringJ(b, Utils.encode);

                // remove deleted records
                if (DBFFileTable.isDeleted(record))
                {
                    return(null);
                }

                return(record);
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 3
0
        internal const int RESERVED_INDEX         = 12; // 12-31


        /**
         * Constructs a DBFHeader, read the data from file <br>
         * You need to supply an open file handle to read from
         * @param ff open file handle for read access
         */
        internal DBFHeader(java.io.RandomAccessFile ff) //throws tinySQLException
        {
            try
            {
                ff.seek(FLAG_INDEX);
                file_type = Utils.fixByte(ff.readByte());

                // get the last update date
                file_update_year  = Utils.fixByte(ff.readByte());
                file_update_month = Utils.fixByte(ff.readByte());
                file_update_day   = Utils.fixByte(ff.readByte());

                // a byte array to hold little-endian long data
                //
                byte[] b = new byte[4];

                // read that baby in...
                //
                ff.readFully(b);

                // convert the byte array into a long (really a double)
                // 4-7 number of records
                numRecords = (int)Utils.vax_to_long(b);

                // a byte array to hold little-endian short data
                //
                b = new byte[2];

                // get the data position (where it starts in the file)
                // 8-9 Length of header
                ff.readFully(b);
                headerLength = Utils.vax_to_short(b);

                // find out the length of the data portion
                // 10-11 Length of Record
                ff.readFully(b);
                recordLength = Utils.vax_to_short(b);

                // calculate the number of fields
                //
                numFields = (int)(headerLength - 33) / 32;

                // skip the next 20 bytes - looks like this is not needed...
                //ff.skipBytes(20);
                // 12-31 reserved

                Utils.log("HEADER=" + this.toString());
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 4
0
 /**
  * Update the header (index 10-11) with the length of one record
  * @param recordLength Length of one data record (row)
  */
 public void setRecordLength(java.io.RandomAccessFile ff, int recordLength) //throws tinySQLException
 {
     this.recordLength = recordLength;
     try
     {
         ff.seek(DBFHeader.LENGTH_OF_REC_INDEX);
         ff.write(Utils.shortToLittleEndian((short)recordLength));
     }
     catch (Exception e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 5
0
 /**
  * Update the header (index 4-7) with the new number of records <br>
  * This is the static variant (use it if you don't want to obtain
  * a DBFHeader instance
  * @param New number of records
  */
 public static void writeNumRecords(java.io.RandomAccessFile ff, int numRecords) //throws tinySQLException
 {
     try
     {
         byte[] b = Utils.intToLittleEndian(numRecords);
         ff.seek(NUMBER_OF_REC_INDEX);
         ff.write(b);
     }
     catch (Exception e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 6
0
 /**
  *
  * Position the record pointer at the top of the table.
  *
  * @see tinySQLTable#GoTop
  *
  */
 public override void GoTop()
 {//throws tinySQLException {
     try
     {
         ftbl.seek(0);
         record_number = 0;
     }
     catch (java.io.IOException e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 7
0
        /*
         * Insert a row. If c or v == null, insert a blank row
         *
         * @param c Ordered Vector of column names
         * @param v Ordered Vector (must match order of c) of values
         * @see tinySQLTable#InsertRow()
         *
         */
        public override void InsertRow(java.util.Vector <Object> c, java.util.Vector <Object> v) //throws TinySQLException
        {
            try
            {
                /*
                 *       Go to the end of the file, then write out the not deleted indicator
                 */
                ftbl.seek(ftbl.length());
                ftbl.write(RECORD_IS_NOT_DELETED);

                /*
                 *       Write out a blank record
                 */
                for (int i = 1; i < dbfHeader.recordLength; i++)
                {
                    ftbl.write(' ');
                }
                int numRec = (int)dbfHeader.numRecords + 1;
                currentRecordNumber = numRec;
                dbfHeader.setNumRecords(ftbl, numRec);
            }
            catch (Exception e)
            {
                if (TinySQLGlobals.DEBUG)
                {
                    java.lang.SystemJ.err.println(e.toString());                      // e.printStackTrace();
                }
                throw new TinySQLException(e.getMessage());
            }
            if (c != null && v != null)
            {
                UpdateCurrentRow(c, v);
            }
            else
            {
                dbfHeader.setTimestamp(ftbl);
            }
        }
Esempio n. 8
0
 /**
  * Update the header (index 8-9) with the new number of records
  * @param numFields number of columns (used to calculate header length)
  */
 public void setHeaderLength(java.io.RandomAccessFile ff, int numFields)// throws tinySQLException
 {
     this.numFields = numFields;
     try
     {
         int headerLength = (DBFHeader.BULK_SIZE + 1) + numFields * DBFHeader.BULK_SIZE;
         ff.seek(DBFHeader.LENGTH_OF_HEADER_INDEX);
         ff.write(Utils.shortToLittleEndian((short)headerLength));
     }
     catch (Exception e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 9
0
 /**
  * Update the header (index 10-11) with the length of one record
  * @param recordLength Length of one data record (row)
  */
 public void setReserved(java.io.RandomAccessFile ff) //throws tinySQLException
 {
     try
     {
         ff.seek(DBFHeader.RESERVED_INDEX);
         byte[] reserved = Utils.forceToSize(null,
                                             DBFHeader.BULK_SIZE - DBFHeader.RESERVED_INDEX,
                                             (byte)0);
         ff.write(reserved);  // padding with \0!
     }
     catch (Exception e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 10
0
 public override int read(byte[] b, int off, int len)  //throws IOException {
 {
     lock (mSharedRaf) {
         mSharedRaf.seek(mOffset);
         if (len > mLength - mOffset)
         {
             len = (int)(mLength - mOffset);
         }
         int count = mSharedRaf.read(b, off, len);
         if (count > 0)
         {
             mOffset += count;
             return(count);
         }
         else
         {
             return(-1);
         }
     }
 }
Esempio n. 11
0
 public void setTimestamp(java.io.RandomAccessFile ff) //throws tinySQLException
 {
     try
     {
         java.util.Calendar cal = java.util.Calendar.getInstance();
         cal.setTime(new java.util.Date());
         int dd = cal.get(java.util.Calendar.DAY_OF_MONTH);
         int mm = cal.get(java.util.Calendar.MONTH) + 1;
         int yy = cal.get(java.util.Calendar.YEAR);
         yy = yy % 100;          // Y2K problem: only 2 digits
         ff.seek(DATE_INDEX);
         ff.write(yy);
         ff.write(mm);
         ff.write(dd);
     }
     catch (Exception e)
     {
         throw new TinySQLException(e.getMessage());
     }
 }
Esempio n. 12
0
        /**
         * Searches for the &quot;End of central dir record&quot;, parses
         * it and positions the stream at the first central directory
         * record.
         */
        private void positionAtCentralDirectory()
        //throws IOException
        {
            bool found         = false;
            long off           = archive.length() - MIN_EOCD_SIZE;
            long stopSearching = java.lang.Math.max(0L, archive.length() - MAX_EOCD_SIZE);

            if (off >= 0)
            {
                archive.seek(off);
                byte[] sig  = ZipArchiveOutputStream.EOCD_SIG;
                int    curr = archive.read();
                while (off >= stopSearching && curr != -1)
                {
                    if (curr == sig[POS_0])
                    {
                        curr = archive.read();
                        if (curr == sig[POS_1])
                        {
                            curr = archive.read();
                            if (curr == sig[POS_2])
                            {
                                curr = archive.read();
                                if (curr == sig[POS_3])
                                {
                                    found = true;
                                    break;
                                }
                            }
                        }
                    }
                    archive.seek(--off);
                    curr = archive.read();
                }
            }
            if (!found)
            {
                throw new java.util.zip.ZipException("archive is not a ZIP archive");
            }
            archive.seek(off + CFD_LOCATOR_OFFSET);
            byte[] cfdOffset = new byte[WORD];
            archive.readFully(cfdOffset);
            archive.seek(ZipLong.getValue(cfdOffset));
        }
Esempio n. 13
0
        /*
         * Retrieve a column's string value from the given row and given colName
         * @param ff the file handle
         * @param colName the column name
         * @param the wanted record (starts with 1)
         * @see tinySQLTable#GetCol
         *
         * @author Thomas Morgner <*****@*****.**> This function retrieves a
         * row, perhaps the name should changed to reflect the new function.
         */
        public static String _GetCol(java.io.RandomAccessFile ff, DBFHeader dbfHeader,
                                     int currentRow) //throws TinySQLException
        {
            try
            {
                /*
                 *       Seek the starting offset of the current record,
                 *       as indicated by currentRow
                 */
                ff.seek(dbfHeader.headerLength + (currentRow - 1) * dbfHeader.recordLength);

                /*
                 *       Fully read a byte array out to the length of the record and convert
                 *       it into a String.
                 */
                byte[] b = new byte[dbfHeader.recordLength];
                ff.readFully(b);
                return(new java.lang.StringJ(b, Utils.encode)); // "Cp437"
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 14
0
        /**
         * write the first 32 bytes to file
         */
        public void write(java.io.RandomAccessFile ff) //throws tinySQLException
        {
            try
            {
                //-----------------------------
                // write out the primary header
                ff.seek(FLAG_INDEX);
                ff.writeByte((byte)0x03);

                setTimestamp(ff); // set current date YY MM DD (dBase is not Y2K save)

                setNumRecords(ff, 0);

                setHeaderLength(ff, numFields);

                setRecordLength(ff, recordLength);

                setReserved(ff);
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 15
0
        /**
         * Reading a column definition from file<br>
         * @param ff file handle (correctly positioned)
         * @param iCol index starts with 1
         * @param locn offset to the current column
         * @return struct with column info
         */
        internal static TsColumn readColdef(java.io.RandomAccessFile ff, String tableName, int iCol, int locn) //throws tinySQLException
        {
            try
            {
                // seek the position of the field definition data.
                // This information appears after the first 32 byte
                // table information, and lives in 32 byte chunks.
                //
                ff.seek((iCol - 1) * 32 + 32);

                // get the column name into a byte array
                //
                byte[] b = new byte[11];
                ff.readFully(b);

                // convert the byte array to a String
                // Seek first 0x00 occurence and strip array after that
                //
                // some C-implementations do not set the remaining bytes
                // after the name to 0x00, so we have to correct this.
                //bool clear = false;
                int i = 0;
                while ((i < 11) && (b[i] != 0))
                {
                    i++;
                }
                while (i < 11)
                {
                    b[i] = 0;
                    i++;
                }
                String colName = (new java.lang.StringJ(b, Utils.encode)).trim();
                // read in the column type which follows the 11 byte column name
                //
                byte[] c = new byte[1];
                c[0] = ff.readByte();
                String ftyp = new java.lang.StringJ(c, Utils.encode);

                // skip four bytes
                //
                ff.skipBytes(4);

                // get field length and precision which are in the two bytes following
                // the column type.
                //
                short flen = Utils.fixByte(ff.readByte()); // 16
                short fdec = Utils.fixByte(ff.readByte()); // 17
                if (ftyp.equals("N") & fdec == 0)
                {
                    ftyp = "I";
                }

                // bytes 18 - 31 are reserved

                // create a new tsColumn object and assign it the
                // attributes of the current field
                //
                if (TinySQLGlobals.DEBUG)
                {
                    java.lang.SystemJ.outJ.println("Try and create tsColumn for " + colName);
                }
                TsColumn column = new TsColumn(colName);

                /*
                 *    The column type is now given as java.sql.Types constant
                 */
                column.type          = typeToSQLType(ftyp);
                column.size          = flen;
                column.decimalPlaces = fdec;
                column.position      = locn + 1; // set the field position to the current
                column.tableName     = tableName;
                return(column);
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 16
0
        /**
         * Writes all necessary data for this entry.
         * @throws IOException on error
         */
        public override void closeArchiveEntry() //throws IOException
        {
            if (finished)
            {
                throw new java.io.IOException("Stream has already been finished");
            }

            if (entry == null)
            {
                throw new java.io.IOException("No current entry to close");
            }

            long realCrc = crc.getValue();

            crc.reset();

            if (entry.getMethod() == DEFLATED)
            {
                def.finish();
                while (!def.finished())
                {
                    deflate();
                }

                entry.setSize(ZipUtil.adjustToLong(def.getTotalIn()));
                entry.setCompressedSize(ZipUtil.adjustToLong(def.getTotalOut()));
                entry.setCrc(realCrc);

                def.reset();

                written += entry.getCompressedSize();
            }
            else if (raf == null)
            {
                if (entry.getCrc() != realCrc)
                {
                    throw new java.util.zip.ZipException("bad CRC checksum for entry "
                                                         + entry.getName() + ": "
                                                         + java.lang.Long.toHexString(entry.getCrc())
                                                         + " instead of "
                                                         + java.lang.Long.toHexString(realCrc));
                }

                if (entry.getSize() != written - dataStart)
                {
                    throw new java.util.zip.ZipException("bad size for entry "
                                                         + entry.getName() + ": "
                                                         + entry.getSize()
                                                         + " instead of "
                                                         + (written - dataStart));
                }
            }
            else     /* method is STORED and we used RandomAccessFile */
            {
                long size = written - dataStart;

                entry.setSize(size);
                entry.setCompressedSize(size);
                entry.setCrc(realCrc);
            }

            // If random access output, write the local file header containing
            // the correct CRC and compressed/uncompressed sizes
            if (raf != null)
            {
                long save = raf.getFilePointer();

                raf.seek(localDataStart);
                writeOut(ZipLong.getBytes(entry.getCrc()));
                writeOut(ZipLong.getBytes(entry.getCompressedSize()));
                writeOut(ZipLong.getBytes(entry.getSize()));
                raf.seek(save);
            }

            writeDataDescriptor(entry);
            entry = null;
        }
Esempio n. 17
0
        /**
         *
         * Deletes Columns from tableName, given a vector of
         * column definition (tsColumn) arrays.<br>
         *
         * ALTER TABLE table DROP [ COLUMN ] column { RESTRICT | CASCADE }
         *
         * @param tableName the name of the table
         * @param v a Vector containing arrays of column definitions.
         * @see tinySQL#AlterTableDropCol
         *
         */
        internal override void AlterTableDropCol(String tableName, java.util.Vector <Object> v)
        {//throws IOException, tinySQLException {
            // rename the file ...
            String fullpath = dataDir + java.io.File.separator + tableName + DBFFileTable.dbfExtension;
            String tmppath  = dataDir + java.io.File.separator + tableName + "-tmp" + DBFFileTable.dbfExtension;

            if (Utils.renameFile(fullpath, tmppath) == false)
            {
                throw new TinySQLException("ALTER TABLE DROP COL error in renaming " + fullpath);
            }

            try
            {
                // open the old file ...
                java.io.RandomAccessFile ftbl_tmp = new java.io.RandomAccessFile(tmppath, "r");

                // read the first 32 bytes ...
                DBFHeader dbfHeader_tmp = new DBFHeader(ftbl_tmp);

                // read the column info ...
                java.util.Vector <Object> coldef_list = new java.util.Vector <Object>(dbfHeader_tmp.numFields - v.size());
                int locn = 0; // offset of the current column

                nextCol : for (int i = 1; i <= dbfHeader_tmp.numFields; i++)
                {
                    TsColumn coldef = readColdef(ftbl_tmp, tableName, i, locn);

                    // remove the DROP columns from the existing cols ...
                    for (int jj = 0; jj < v.size(); jj++)
                    {
                        String colName = (String)v.elementAt(jj);
                        if (coldef.name.equals(colName))
                        {
                            Utils.log("Dropping " + colName);
                            goto nextCol;
                        }
                    }

                    locn += coldef.size; // increment locn by the length of this field.
                    // Utils.log("Recycling " + coldef.name);
                    coldef_list.addElement(coldef);
                }

                // create the new table ...
                CreateTable(tableName, coldef_list);

                // copy the data from old to new

                // opening new created dBase file ...
                java.io.RandomAccessFile ftbl = new java.io.RandomAccessFile(fullpath, "rw");
                ftbl.seek(ftbl.length()); // go to end of file

                int numRec = 0;
                for (int iRec = 1; iRec <= dbfHeader_tmp.numRecords; iRec++)
                {
                    if (DBFFileTable.isDeleted(ftbl_tmp, dbfHeader_tmp, iRec) == true)
                    {
                        continue;
                    }

                    numRec++;

                    ftbl.write(DBFFileTable.RECORD_IS_NOT_DELETED);  // write flag

                    // Read the whole column into the table's cache
                    String column = DBFFileTable._GetCol(ftbl_tmp, dbfHeader_tmp, iRec);

                    for (int iCol = 0; iCol < coldef_list.size(); iCol++) // write columns
                    {
                        TsColumn coldef = (TsColumn)coldef_list.elementAt(iCol);

                        // Extract column values from cache
                        String value = DBFFileTable.getColumn(coldef, column);
                        java.lang.SystemJ.outJ.println("From cache column value" + value);

                        value = Utils.forceToSize(value, coldef.size, " "); // enforce the correct column length

                        byte[] b = value.getBytes(Utils.encode);            // transform to byte and write to file
                        ftbl.write(b);
                    }
                }

                ftbl_tmp.close();

                // remove temp file
                java.io.File f = new java.io.File(tmppath);
                if (f.exists())
                {
                    f.delete();
                }

                DBFHeader.writeNumRecords(ftbl, numRec);
                ftbl.close();
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 18
0
        /**
         * Creates new Columns in tableName, given a vector of
         * column definition (tsColumn) arrays.<br>
         * It is necessary to copy the whole file to do this task.
         *
         * ALTER TABLE table [ * ] ADD [ COLUMN ] column type
         *
         * @param tableName the name of the table
         * @param v a Vector containing arrays of column definitions.
         * @see tinySQL#AlterTableAddCol
         */
        internal override void AlterTableAddCol(String tableName, java.util.Vector <Object> v)
        {//throws IOException, tinySQLException {
            // rename the file ...
            String fullpath = dataDir + java.io.File.separator + tableName + DBFFileTable.dbfExtension;
            String tmppath  = dataDir + java.io.File.separator + tableName + "_tmp_tmp" + DBFFileTable.dbfExtension;

            if (Utils.renameFile(fullpath, tmppath) == false)
            {
                throw new TinySQLException("ALTER TABLE ADD COL error in renaming " + fullpath);
            }

            try
            {
                // open the old file ...
                java.io.RandomAccessFile ftbl_tmp = new java.io.RandomAccessFile(tmppath, "r");

                // read the first 32 bytes ...
                DBFHeader dbfHeader_tmp = new DBFHeader(ftbl_tmp);

                // read the column info ...
                java.util.Vector <Object> coldef_list = new java.util.Vector <Object>(dbfHeader_tmp.numFields + v.size());
                int locn = 0; // offset of the current column
                for (int i = 1; i <= dbfHeader_tmp.numFields; i++)
                {
                    TsColumn coldef = readColdef(ftbl_tmp, tableName, i, locn);
                    locn += coldef.size; // increment locn by the length of this field.
                    coldef_list.addElement(coldef);
                }

                // add the new column definitions to the existing ...
                for (int jj = 0; jj < v.size(); jj++)
                {
                    coldef_list.addElement(v.elementAt(jj));
                }

                // create the new table ...
                CreateTable(tableName, coldef_list);

                // copy the data from old to new

                // opening new created dBase file ...
                java.io.RandomAccessFile ftbl = new java.io.RandomAccessFile(fullpath, "rw");
                ftbl.seek(ftbl.length()); // go to end of file

                int numRec = 0;
                for (int iRec = 1; iRec <= dbfHeader_tmp.numRecords; iRec++)
                {
                    String str = GetRecord(ftbl_tmp, dbfHeader_tmp, iRec);

                    // Utils.log("Copy of record#" + iRec + " str='" + str + "' ...");

                    if (str == null)
                    {
                        continue;                           // record was marked as deleted, ignore it
                    }
                    ftbl.write(str.getBytes(Utils.encode)); // write original record
                    numRec++;

                    for (int iCol = 0; iCol < v.size(); iCol++) // write added columns
                    {
                        TsColumn coldef = (TsColumn)v.elementAt(iCol);

                        // enforce the correct column length
                        String value = Utils.forceToSize(coldef.defaultVal, coldef.size, " ");

                        // transform to byte and write to file
                        byte[] b = value.getBytes(Utils.encode);
                        ftbl.write(b);
                    }
                }

                ftbl_tmp.close();

                DBFHeader.writeNumRecords(ftbl, numRec);
                ftbl.close();

                Utils.delFile(tmppath);
            }
            catch (Exception e)
            {
                throw new TinySQLException(e.getMessage());
            }
        }
Esempio n. 19
0
        /*
         * Find the central directory and read the contents.
         *
         * <p>The central directory can be followed by a variable-length comment
         * field, so we have to scan through it backwards.  The comment is at
         * most 64K, plus we have 18 bytes for the end-of-central-dir stuff
         * itself, plus apparently sometimes people throw random junk on the end
         * just for the fun of it.</p>
         *
         * <p>This is all a little wobbly.  If the wrong value ends up in the EOCD
         * area, we're hosed. This appears to be the way that everybody handles
         * it though, so we're in good company if this fails.</p>
         */
        private void readCentralDir()  //throws IOException {

        /*
         * Scan back, looking for the End Of Central Directory field.  If
         * the archive doesn't have a comment, we'll hit it on the first
         * try.
         *
         * No need to synchronize mRaf here -- we only do this when we
         * first open the Zip file.
         */
        {
            long scanOffset = mRaf.length() - ENDHDR;

            if (scanOffset < 0)
            {
                throw new ZipException("too short to be Zip");
            }

            long stopOffset = scanOffset - 65536;

            if (stopOffset < 0)
            {
                stopOffset = 0;
            }

            while (true)
            {
                mRaf.seek(scanOffset);
                if (ZipEntry.readIntLE(mRaf) == 101010256L)
                {
                    break;
                }

                scanOffset--;
                if (scanOffset < stopOffset)
                {
                    throw new ZipException("EOCD not found; not a Zip archive?");
                }
            }

            /*
             * Found it, read the EOCD.
             *
             * For performance we want to use buffered I/O when reading the
             * file.  We wrap a buffered stream around the random-access file
             * object.  If we just read from the RandomAccessFile we'll be
             * doing a read() system call every time.
             */
            RAFStream rafs = new RAFStream(mRaf, mRaf.getFilePointer());

            java.io.BufferedInputStream bin = new java.io.BufferedInputStream(rafs, ENDHDR);

            int diskNumber         = ler.readShortLE(bin);
            int diskWithCentralDir = ler.readShortLE(bin);
            int numEntries         = ler.readShortLE(bin);
            int totalNumEntries    = ler.readShortLE(bin);

            /*centralDirSize =*/ ler.readIntLE(bin);
            long centralDirOffset = ler.readIntLE(bin);

            /*commentLen =*/ ler.readShortLE(bin);

            if (numEntries != totalNumEntries ||
                diskNumber != 0 ||
                diskWithCentralDir != 0)
            {
                throw new ZipException("spanned archives not supported");
            }

            /*
             * Seek to the first CDE and read all entries.
             * However, when Z_SYNC_FLUSH is used the offset may not point directly
             * to the CDE so skip over until we find it.
             * At most it will be 6 bytes away (one or two bytes for empty block, 4 bytes for
             * empty block signature).
             */
            scanOffset = centralDirOffset;
            stopOffset = scanOffset + 6;

            while (true)
            {
                mRaf.seek(scanOffset);
                if (ZipEntry.readIntLE(mRaf) == CENSIG)
                {
                    break;
                }

                scanOffset++;
                if (scanOffset > stopOffset)
                {
                    throw new ZipException("Central Directory Entry not found");
                }
            }

            // If CDE is found then go and read all the entries
            rafs = new RAFStream(mRaf, scanOffset);
            bin  = new java.io.BufferedInputStream(rafs, 4096);
            for (int i = 0; i < numEntries; i++)
            {
                ZipEntry newEntry = new ZipEntry(ler, bin);
                mEntries.put(newEntry.getName(), newEntry);
            }
        }