001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.commons.compress.archivers.tar;
020    
021    /**
022     * This class provides static utility methods to work with byte streams.
023     *
024     * @Immutable
025     */
026    // CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
027    public class TarUtils {
028    
029        private static final int BYTE_MASK = 255;
030    
031        /** Private constructor to prevent instantiation of this utility class. */
032        private TarUtils(){    
033        }
034    
035        /**
036         * Parse an octal string from a buffer.
037         * Leading spaces are ignored.
038         * Parsing stops when a NUL is found, or a trailing space,
039         * or the buffer length is reached.
040         *
041         * Behaviour with non-octal input is currently undefined.
042         * 
043         * @param buffer The buffer from which to parse.
044         * @param offset The offset into the buffer from which to parse.
045         * @param length The maximum number of bytes to parse.
046         * @return The long value of the octal string.
047         */
048        public static long parseOctal(byte[] buffer, final int offset, final int length) {
049            long    result = 0;
050            boolean stillPadding = true;
051            int     end = offset + length;
052    
053            for (int i = offset; i < end; ++i) {
054                final byte currentByte = buffer[i];
055                if (currentByte == 0) { // Found trailing null
056                    break;
057                }
058    
059                // Ignore leading spaces ('0' can be ignored anyway)
060                if (currentByte == (byte) ' ' || currentByte == '0') {
061                    if (stillPadding) {
062                        continue;
063                    }
064    
065                    if (currentByte == (byte) ' ') { // Found trailing space
066                        break;
067                    }
068                }
069    
070                stillPadding = false;
071                // CheckStyle:MagicNumber OFF
072                if (currentByte < '0' || currentByte > '7'){
073                    throw new IllegalArgumentException(
074                            "Invalid octal digit at position "+i+" in '"+new String(buffer, offset, length)+"'");
075                }
076                result = (result << 3) + (currentByte - '0');// TODO needs to reject invalid bytes
077                // CheckStyle:MagicNumber ON
078            }
079    
080            return result;
081        }
082    
083        /**
084         * Parse an entry name from a buffer.
085         * Parsing stops when a NUL is found
086         * or the buffer length is reached.
087         *
088         * @param buffer The buffer from which to parse.
089         * @param offset The offset into the buffer from which to parse.
090         * @param length The maximum number of bytes to parse.
091         * @return The entry name.
092         */
093        public static String parseName(byte[] buffer, final int offset, final int length) {
094            StringBuffer result = new StringBuffer(length);
095            int          end = offset + length;
096    
097            for (int i = offset; i < end; ++i) {
098                if (buffer[i] == 0) { // Trailing null
099                    break;
100                }
101    
102                result.append((char) buffer[i]);
103            }
104    
105            return result.toString();
106        }
107    
108        /**
109         * Copy a name (StringBuffer) into a buffer.
110         * Copies characters from the name into the buffer
111         * starting at the specified offset. 
112         * If the buffer is longer than the name, the buffer
113         * is filled with trailing NULs.
114         * If the name is longer than the buffer,
115         * the output is truncated.
116         *
117         * @param name The header name from which to copy the characters.
118         * @param buf The buffer where the name is to be stored.
119         * @param offset The starting offset into the buffer
120         * @param length The maximum number of header bytes to copy.
121         * @return The updated offset, i.e. offset + length
122         */
123        public static int formatNameBytes(String name, byte[] buf, final int offset, final int length) {
124            int i;
125    
126            // copy until end of input or output is reached.
127            for (i = 0; i < length && i < name.length(); ++i) {
128                buf[offset + i] = (byte) name.charAt(i);
129            }
130    
131            // Pad any remaining output bytes with NUL
132            for (; i < length; ++i) {
133                buf[offset + i] = 0;
134            }
135    
136            return offset + length;
137        }
138    
139        /**
140         * Fill buffer with unsigned octal number, padded with leading zeroes.
141         * 
142         * @param value number to convert to octal - treated as unsigned
143         * @param buffer destination buffer
144         * @param offset starting offset in buffer
145         * @param length length of buffer to fill
146         * @throws IllegalArgumentException if the value will not fit in the buffer
147         */
148        public static void formatUnsignedOctalString(final long value, byte[] buffer,
149                final int offset, final int length) {
150            int remaining = length;
151            remaining--;
152            if (value == 0) {
153                buffer[offset + remaining--] = (byte) '0';
154            } else {
155                long val = value;
156                for (; remaining >= 0 && val != 0; --remaining) {
157                    // CheckStyle:MagicNumber OFF
158                    buffer[offset + remaining] = (byte) ((byte) '0' + (byte) (val & 7));
159                    val = val >>> 3;
160                    // CheckStyle:MagicNumber ON
161                }
162                if (val != 0){
163                    throw new IllegalArgumentException
164                    (value+"="+Long.toOctalString(value)+ " will not fit in octal number buffer of length "+length);
165                }
166            }
167    
168            for (; remaining >= 0; --remaining) { // leading zeros
169                buffer[offset + remaining] = (byte) '0';
170            }
171        }
172    
173        /**
174         * Write an octal integer into a buffer.
175         *
176         * Uses {@link #formatUnsignedOctalString} to format
177         * the value as an octal string with leading zeros.
178         * The converted number is followed by space and NUL
179         * 
180         * @param value The value to write
181         * @param buf The buffer to receive the output
182         * @param offset The starting offset into the buffer
183         * @param length The size of the output buffer
184         * @return The updated offset, i.e offset+length
185         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
186         */
187        public static int formatOctalBytes(final long value, byte[] buf, final int offset, final int length) {
188    
189            int idx=length-2; // For space and trailing null
190            formatUnsignedOctalString(value, buf, offset, idx);
191    
192            buf[offset + idx++] = (byte) ' '; // Trailing space
193            buf[offset + idx]   = 0; // Trailing null
194    
195            return offset + length;
196        }
197    
198        /**
199         * Write an octal long integer into a buffer.
200         * 
201         * Uses {@link #formatUnsignedOctalString} to format
202         * the value as an octal string with leading zeros.
203         * The converted number is followed by a space.
204         * 
205         * @param value The value to write as octal
206         * @param buf The destinationbuffer.
207         * @param offset The starting offset into the buffer.
208         * @param length The length of the buffer
209         * @return The updated offset
210         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
211         */
212        public static int formatLongOctalBytes(final long value, byte[] buf, final int offset, final int length) {
213    
214            int idx=length-1; // For space
215            
216            formatUnsignedOctalString(value, buf, offset, idx);
217            buf[offset + idx] = (byte) ' '; // Trailing space
218    
219            return offset + length;
220        }
221    
222        /**
223         * Writes an octal value into a buffer.
224         * 
225         * Uses {@link #formatUnsignedOctalString} to format
226         * the value as an octal string with leading zeros.
227         * The converted number is followed by NUL and then space.
228         *
229         * @param value The value to convert
230         * @param buf The destination buffer
231         * @param offset The starting offset into the buffer.
232         * @param length The size of the buffer.
233         * @return The updated value of offset, i.e. offset+length
234         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
235         */
236        public static int formatCheckSumOctalBytes(final long value, byte[] buf, final int offset, final int length) {
237    
238            int idx=length-2; // for NUL and space
239            formatUnsignedOctalString(value, buf, offset, idx);
240    
241            buf[offset + idx++]   = 0; // Trailing null
242            buf[offset + idx]     = (byte) ' '; // Trailing space
243    
244            return offset + length;
245        }
246    
247        /**
248         * Compute the checksum of a tar entry header.
249         *
250         * @param buf The tar entry's header buffer.
251         * @return The computed checksum.
252         */
253        public static long computeCheckSum(final byte[] buf) {
254            long sum = 0;
255    
256            for (int i = 0; i < buf.length; ++i) {
257                sum += BYTE_MASK & buf[i];
258            }
259    
260            return sum;
261        }
262    }