/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.fs.jfat; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.util.Arrays; public class FatName { // // special characters // private static final byte underscore = '_'; private static final byte space = ' '; private static final byte period = '.'; private static final byte tilde = '~'; // // shortname illegal chars // // 0x22, 0x2A, 0x2B, 0x2C, 0x2F, // 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 0x5B, 0x5C, 0x5D, // 0x7C // private static final byte[] short_illegal = { '"', '*', '+', ',', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '|'}; private final FatDirectory parent; @SuppressWarnings("unused") private final String name; private final CodePageEncoder encoder; private final CodePageDecoder decoder; private final int numberOfComponents; private final String[] components; private boolean lossy; private boolean stripped; private boolean fit; private byte[] basis; private final String shortName; private final String longName; private String shortBase; private String shortExt; private FatCase shortCase; FatName(FatDirectory parent, String name) throws IOException { CodePage cp = parent.getFatFileSystem().getCodePage(); this.parent = parent; this.name = name; this.encoder = cp.newEncoder(); this.decoder = cp.newDecoder(); this.longName = FatUtils.longName(name); this.lossy = false; this.fit = true; this.stripped = false; this.basis = new byte[11]; Arrays.fill(this.basis, 0, this.basis.length, space); basisName(); /* * log.debug ( "lossy\t[" + lossy + "]" ); log.debug ( "stripped\t[" + * stripped + "]" ); log.debug ( "fit\t\t[" + fit + "]" ); log.debug ( * "basis\t<" + decoder.decode ( basis ) + ">" ); */ if (isMangled()) { numericTail(); // log.debug ( "basisN\t<" + decoder.decode ( basis ) + ">" ); byte[] primary = new byte[8]; byte[] extension = new byte[3]; System.arraycopy(basis, 0, primary, 0, 8); System.arraycopy(basis, 8, extension, 0, 3); shortBase = decoder.decode(primary).trim(); shortExt = decoder.decode(extension).trim(); shortCase = new FatCase(); } else { int p = longName.indexOf(period); if (p == -1) { shortBase = longName; shortExt = ""; } else { shortBase = longName.substring(0, p); if (p == (longName.length() - 1)) shortExt = ""; else shortExt = longName.substring(p + 1); } shortCase = new FatCase(shortBase, shortExt); } if (shortExt.length() > 0) shortName = shortBase + "." + shortExt; else shortName = shortBase; int n = longName.length() / 13; if ((longName.length() % 13) != 0) n++; numberOfComponents = n; components = new String[n]; for (int i = 0; i < n - 1; i++) components[i] = longName.substring(i * 13, (i + 1) * 13); components[n - 1] = longName.substring((n - 1) * 13); } /* * internal utility routine */ private byte[] stripChar(byte[] name, byte ch, boolean strip) { boolean flag = !strip; ByteBuffer b = ByteBuffer.allocate(name.length); for (int i = (name.length - 1); i >= 0; i--) { if (name[i] != ch) strip = flag; if (strip) { if (name[i] != ch) b.put(name[i]); else stripped = true; } else b.put(name[i]); } b.flip(); byte[] n = new byte[b.remaining()]; b.get(n); return n; } /* * internal utility routine */ boolean collide(byte[] name) { return parent.collide(name); } /* * Basis-Name Phase1: Step1 and Step2 */ private byte[] encode() throws CharacterCodingException { byte[] n = encoder.encode(FatUtils.toUpperCase(longName), underscore); lossy = encoder.isLossy(); for (int i = 0; i < n.length; i++) { if (n[i] < 0x20) { n[i] = underscore; lossy = true; continue; } for (int j = 0; j < short_illegal.length; j++) { if ((n[i] == short_illegal[j])) { n[i] = underscore; lossy = true; break; } } } return n; } /* * Basis-Name Phase2: Step3 and Step4 */ private byte[] strip(byte[] name) { return stripChar(stripChar(name, space, false), period, true); } /* * Basis-Name Phase3: Step5, Step6(omitted) and Step7 */ private void primary(byte[] name) { int i; for (i = 0; i < name.length; i++) { if (name[i] == period) break; if (i >= 8) { fit = false; break; } basis[i] = name[i]; } for (i = (name.length - 1); i >= 0; i--) if (name[i] == period) break; if (i != -1) { if (i > 8) fit = false; int j; int l = i + 1; for (j = l; j < name.length; j++) { if ((j - l) >= 3) break; basis[8 + (j - l)] = name[j]; } if (j != name.length) fit = false; } } private void basisName() throws CharacterCodingException { primary(strip(encode())); } private void numericTail() throws IOException { int i, j, l, p; byte[] nbasis = new byte[11]; StringBuilder btail = new StringBuilder(7); for (p = 7; p >= 0; p--) if (basis[p] != space) break; for (i = 1; i <= 999999; i++) { System.arraycopy(basis, 0, nbasis, 0, 11); btail.setLength(0); btail.append(i); byte[] tail = encoder.encode(btail.toString()); if (tail.length > 6) throw new IOException("tail too long: " + tail.length); if ((p + tail.length) < 8) l = p + 1; else l = 7 - tail.length; nbasis[l] = tilde; for (j = l; j < (l + tail.length); j++) nbasis[j + 1] = tail[j - l]; if (!collide(nbasis)) { basis = nbasis; return; } } throw new IOException("tail too large"); } public byte[] getName() { return basis; } public String getShortName() { return shortName; } public String getLongName() { return longName; } public boolean isMangled() { return (lossy || stripped || !fit); } public String getShortBase() { return shortBase; } public String getShortExt() { return shortExt; } public FatCase getShortCase() { return shortCase; } public int getNumberOfComponents() { return numberOfComponents; } public String[] getComponents() { return components; } public String getComponent(int i) { return components[i]; } public String toString() { StrWriter out = new StrWriter(); out.println("*******************************************"); out.println("FatName"); out.println("*******************************************"); out.println("LongName\t" + getLongName()); out.println("isMangled\t" + isMangled()); out.println("N.Components\t" + getNumberOfComponents()); out.println("ShortBase\t" + getShortBase()); out.println("ShortExt\t" + getShortExt()); out.println("ShortCase\t" + getShortCase()); out.println("ShortName\t" + getShortName()); out.print("*******************************************"); return out.toString(); } }