Source for gnu.gcj.tools.gcj_dbtool.Main

   1: /* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
   2:    Free Software Foundation
   3: 
   4:    This file is part of libgcj.
   5: 
   6: This software is copyrighted work licensed under the terms of the
   7: Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
   8: details.  */
   9: 
  10: package gnu.gcj.tools.gcj_dbtool;
  11: 
  12: 
  13: import gnu.gcj.runtime.PersistentByteMap;
  14: import java.io.*;
  15: import java.nio.channels.*;
  16: import java.util.*;
  17: import java.util.jar.*;
  18: import java.security.MessageDigest;
  19: 
  20: public class Main
  21: {
  22:   static private boolean verbose = false;
  23: 
  24:   public static void main (String[] s)
  25:   {
  26:     boolean fileListFromStdin = false;
  27:     char filenameSeparator = ' ';
  28: 
  29:     insist (s.length >= 1);
  30: 
  31:     if (s[0].equals("-") ||
  32:     s[0].equals("-0"))
  33:       {
  34:     if (s[0].equals("-0"))
  35:       filenameSeparator = (char)0;
  36:     fileListFromStdin = true;
  37:     String[] newArgs = new String[s.length - 1];
  38:     System.arraycopy(s, 1, newArgs, 0, s.length - 1);
  39:     s = newArgs;
  40:       }
  41: 
  42:     if (s[0].equals("-v") || s[0].equals("--version"))
  43:       {
  44:     insist (s.length == 1);
  45:     System.out.println("gcj-dbtool ("
  46:                + System.getProperty("java.vm.name")
  47:                + ") "
  48:                + System.getProperty("java.vm.version"));
  49:     System.out.println();
  50:     System.out.println("Copyright 2011 Free Software Foundation, Inc.");
  51:     System.out.println("This is free software; see the source for copying conditions.  There is NO");
  52:     System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
  53:     return;
  54:       }
  55:     if (s[0].equals("--help"))
  56:       {
  57:     usage(System.out);
  58:     return;
  59:       }
  60: 
  61:     if (s[0].equals("-n"))
  62:       {
  63:     // Create a new database.
  64:     insist (s.length >= 2 && s.length <= 3);
  65: 
  66:     int capacity = 32749;
  67: 
  68:     if (s.length == 3)
  69:       {        
  70:         capacity = Integer.parseInt(s[2]);
  71: 
  72:         if (capacity <= 2)
  73:           {
  74:         usage(System.err);
  75:         System.exit(1);
  76:           }
  77:       }
  78:         
  79:     try
  80:       {
  81:         PersistentByteMap b 
  82:           = PersistentByteMap.emptyPersistentByteMap(new File(s[1]), 
  83:                              capacity, capacity*32);
  84:       }
  85:     catch (Exception e)
  86:       {
  87:         System.err.println ("error: could not create " 
  88:                 + s[1] + ": " + e.toString());
  89:         System.exit(2);
  90:       }
  91:     return;
  92:       }
  93: 
  94:     if (s[0].equals("-a") || s[0].equals("-f"))
  95:       {
  96:     // Add a jar file to a database, creating it if necessary.
  97:     // Copies the database, adds the jar file to the copy, and
  98:     // then renames the new database over the old.
  99:     try
 100:       {
 101:         insist (s.length == 4);
 102:         File database = new File(s[1]);
 103:         database = database.getAbsoluteFile();
 104:         File jar = new File(s[2]);    
 105:         PersistentByteMap map; 
 106:         if (database.isFile())
 107:           map = new PersistentByteMap(database, 
 108:                       PersistentByteMap.AccessMode.READ_ONLY);
 109:         else
 110:           map = PersistentByteMap.emptyPersistentByteMap(database, 
 111:                                  100, 100*32);
 112:         File soFile = new File(s[3]);
 113:         if (! s[0].equals("-f") && ! soFile.isFile())
 114:           throw new IllegalArgumentException(s[3] + " is not a file");
 115:          map = addJar(jar, map, soFile);
 116:       }
 117:     catch (Exception e)
 118:       {
 119:         System.err.println ("error: could not update " + s[1] 
 120:                 + ": " + e.toString());
 121:         System.exit(2);
 122:       }
 123:     return;
 124:       }
 125: 
 126:     if (s[0].equals("-t"))
 127:       {
 128:     // Test
 129:     try
 130:       {
 131:         insist (s.length == 2);
 132:         PersistentByteMap b 
 133:           = new PersistentByteMap(new File(s[1]),
 134:                       PersistentByteMap.AccessMode.READ_ONLY);
 135:         Iterator iterator = b.iterator(PersistentByteMap.ENTRIES);
 136:     
 137:         while (iterator.hasNext())
 138:           {
 139:         PersistentByteMap.MapEntry entry 
 140:           = (PersistentByteMap.MapEntry)iterator.next();
 141:         byte[] key = (byte[])entry.getKey();
 142:         byte[] value = (byte[])b.get(key);
 143:         if (! Arrays.equals (value, (byte[])entry.getValue()))
 144:           {
 145:             String err 
 146:               = ("Key " + bytesToString(key) + " at bucket " 
 147:              + entry.getBucket());
 148:           
 149:             throw new RuntimeException(err);
 150:           }
 151:           }
 152:       }
 153:     catch (Exception e)
 154:       {
 155:         e.printStackTrace();
 156:         System.exit(3);
 157:       }
 158:     return;
 159:       }
 160:      
 161:     if (s[0].equals("-m"))
 162:       {
 163:     // Merge databases.
 164:     insist (s.length >= 3
 165:         || fileListFromStdin && s.length == 2);
 166:     try
 167:       {
 168:         File database = new File(s[1]);
 169:         database = database.getAbsoluteFile();
 170:         File temp = File.createTempFile(database.getName(), "", 
 171:                         database.getParentFile());
 172:             
 173:         int newSize = 0;
 174:         int newStringTableSize = 0;
 175:         Fileset files = getFiles(s, 2, fileListFromStdin, 
 176:                      filenameSeparator);
 177:         PersistentByteMap[] sourceMaps 
 178:           = new PersistentByteMap[files.size()];
 179: 
 180:         // Scan all the input files, calculating worst case string
 181:         // table and hash table use.
 182:         {
 183:           Iterator it = files.iterator();
 184:           int i = 0;
 185:           while (it.hasNext())
 186:         {
 187:           PersistentByteMap b 
 188:             = new PersistentByteMap((File)it.next(),
 189:                         PersistentByteMap.AccessMode.READ_ONLY);
 190:           newSize += b.size();
 191:           newStringTableSize += b.stringTableSize();
 192:           sourceMaps[i++] = b;
 193:         }
 194:         }
 195:         
 196:         newSize *= 1.5; // Scaling the new size by 1.5 results in
 197:                 // fewer collisions.
 198:         PersistentByteMap map 
 199:           = PersistentByteMap.emptyPersistentByteMap
 200:           (temp, newSize, newStringTableSize);
 201: 
 202:         for (int i = 0; i < sourceMaps.length; i++)
 203:           {
 204:         if (verbose)
 205:           System.err.println("adding " + sourceMaps[i].size() 
 206:                      + " elements from "
 207:                      + sourceMaps[i].getFile());
 208:         map.putAll(sourceMaps[i]);
 209:           }
 210:         map.close();
 211:         temp.renameTo(database);
 212:       }
 213:     catch (Exception e)
 214:       {
 215:         e.printStackTrace();
 216:         System.exit(3);
 217:       }
 218:     return;
 219:       }
 220: 
 221:     if (s[0].equals("-l"))
 222:       {
 223:     // List a database.
 224:     insist (s.length == 2);
 225:     try
 226:       {
 227:         PersistentByteMap b 
 228:           = new PersistentByteMap(new File(s[1]),
 229:                       PersistentByteMap.AccessMode.READ_ONLY);
 230: 
 231:         System.out.println ("Capacity: " + b.capacity());
 232:         System.out.println ("Size: " + b.size());
 233:         System.out.println ();
 234: 
 235:         System.out.println ("Elements: ");
 236:         Iterator iterator = b.iterator(PersistentByteMap.ENTRIES);
 237:     
 238:         while (iterator.hasNext())
 239:           {
 240:         PersistentByteMap.MapEntry entry 
 241:           = (PersistentByteMap.MapEntry)iterator.next();
 242:         byte[] digest = (byte[])entry.getKey();
 243:         System.out.print ("[" + entry.getBucket() + "] " 
 244:                   + bytesToString(digest)
 245:                   + " -> ");
 246:         System.out.println (new String((byte[])entry.getValue()));
 247:           }
 248:       }
 249:     catch (Exception e)
 250:       {
 251:         System.err.println ("error: could not list " 
 252:                 + s[1] + ": " + e.toString());
 253:         System.exit(2);
 254:       }
 255:     return;
 256:       }
 257: 
 258:     if (s[0].equals("-d"))
 259:       {
 260:     // For testing only: fill the byte map with random data.
 261:     insist (s.length == 2);
 262:     try
 263:       {    
 264:         MessageDigest md = MessageDigest.getInstance("MD5");
 265:         PersistentByteMap b 
 266:           = new PersistentByteMap(new File(s[1]), 
 267:                       PersistentByteMap.AccessMode.READ_WRITE);
 268:         int N = b.capacity();
 269:         byte[] bytes = new byte[1];
 270:         byte digest[] = md.digest(bytes);
 271:         for (int i = 0; i < N; i++)
 272:           {
 273:         digest = md.digest(digest);
 274:         b.put(digest, digest);
 275:           }
 276:       }
 277:     catch (Exception e)
 278:       {
 279:         e.printStackTrace();
 280:         System.exit(3);
 281:       }        
 282:     return;
 283:       }
 284: 
 285:     if (s[0].equals("-p"))
 286:       {
 287:     insist (s.length == 1 || s.length == 2);
 288:     String result;
 289:     
 290:     if (s.length == 1)
 291:       result = System.getProperty("gnu.gcj.precompiled.db.path", "");
 292:     else 
 293:       result = (s[1] 
 294:             + (s[1].endsWith(File.separator) ? "" : File.separator)
 295:             + getDbPathTail ());
 296: 
 297:     System.out.println (result);
 298:     return;
 299:       }
 300: 
 301:     usage(System.err);
 302:     System.exit(1);        
 303:   }
 304: 
 305:   private static native String getDbPathTail ();
 306:     
 307:   private static void insist(boolean ok)
 308:   {
 309:     if (! ok)
 310:       {
 311:     usage(System.err);
 312:     System.exit(1);
 313:       }        
 314:   }
 315: 
 316:   private static void usage(PrintStream out)
 317:   {
 318:     out.println
 319:       ("gcj-dbtool: Manipulate gcj map database files\n"
 320:        + "\n"
 321:        + "  Usage: \n"
 322:        + "    gcj-dbtool -n file.gcjdb [size]     - Create a new gcj map database\n"
 323:        + "    gcj-dbtool -a file.gcjdb file.jar file.so\n"
 324:        + "            - Add the contents of file.jar to a gcj map database\n"
 325:        + "    gcj-dbtool -f file.gcjdb file.jar file.so\n"
 326:        + "            - Add the contents of file.jar to a gcj map database\n"
 327:        + "    gcj-dbtool -t file.gcjdb            - Test a gcj map database\n"
 328:        + "    gcj-dbtool -l file.gcjdb            - List a gcj map database\n"
 329:        + "    gcj-dbtool [-][-0] -m dest.gcjdb [source.gcjdb]...\n"
 330:        + "            - Merge gcj map databases into dest\n"
 331:        + "              Replaces dest\n"
 332:        + "              To add to dest, include dest in the list of sources\n"
 333:        + "              If the first arg is -, read the list from stdin\n"
 334:        + "              If the first arg is -0, filenames separated by nul\n"
 335:        + "    gcj-dbtool -p [LIBDIR]              - Print default database name"
 336:        );
 337:   }
 338: 
 339:   // Add a jar to a map.  This copies the map first and returns a
 340:   // different map that contains the data.  The original map is
 341:   // closed.
 342: 
 343:   private static PersistentByteMap 
 344:   addJar(File f, PersistentByteMap b, File soFile)
 345:     throws Exception
 346:   {
 347:     MessageDigest md = MessageDigest.getInstance("MD5");
 348: 
 349:     JarFile jar = new JarFile (f);
 350: 
 351:     int count = 0;
 352:     {
 353:       Enumeration entries = jar.entries();      
 354:       while (entries.hasMoreElements())
 355:     {
 356:       JarEntry classfile = (JarEntry)entries.nextElement();
 357:       if (classfile.getName().endsWith(".class"))
 358:         count++;
 359:     }
 360:     }
 361: 
 362:     if (verbose)
 363:       System.err.println("adding " + count + " elements from "
 364:              + f + " to " + b.getFile());
 365:     
 366:     // Maybe resize the destination map.  We're allowing plenty of
 367:     // extra space by using a loadFactor of 2.  
 368:     b = resizeMap(b, (b.size() + count) * 2, true);
 369: 
 370:     Enumeration entries = jar.entries();
 371: 
 372:     byte[] soFileName = soFile.getCanonicalPath().getBytes("UTF-8");
 373:     while (entries.hasMoreElements())
 374:       {
 375:     JarEntry classfile = (JarEntry)entries.nextElement();
 376:     if (classfile.getName().endsWith(".class"))
 377:       {
 378:         InputStream str = jar.getInputStream(classfile);
 379:         int length = (int) classfile.getSize();
 380:         if (length == -1)
 381:           throw new EOFException();
 382: 
 383:         byte[] data = new byte[length];
 384:         int pos = 0;
 385:         while (length - pos > 0)
 386:           {
 387:         int len = str.read(data, pos, length - pos);
 388:         if (len == -1)
 389:           throw new EOFException("Not enough data reading from: "
 390:                      + classfile.getName());
 391:         pos += len;
 392:           }
 393:         b.put(md.digest(data), soFileName);
 394:       }
 395:       }
 396:     return b;
 397:   }    
 398: 
 399:   // Resize a map by creating a new one with the same data and
 400:   // renaming it.  If close is true, close the original map.
 401: 
 402:   static PersistentByteMap resizeMap(PersistentByteMap m, int newCapacity, boolean close)
 403:     throws IOException, IllegalAccessException
 404:   {
 405:     newCapacity = Math.max(m.capacity(), newCapacity);
 406:     File name = m.getFile();
 407:     File copy = File.createTempFile(name.getName(), "", name.getParentFile());
 408:     try
 409:       {
 410:     PersistentByteMap dest 
 411:       = PersistentByteMap.emptyPersistentByteMap
 412:       (copy, newCapacity, newCapacity*32);
 413:     dest.putAll(m);
 414:     dest.force();
 415:     if (close)
 416:       m.close();
 417:     copy.renameTo(name);
 418:     return dest;
 419:       }
 420:     catch (Exception e)
 421:       {
 422:     copy.delete();
 423:       }
 424:     return null;
 425:   }
 426:     
 427:      
 428:   static String bytesToString(byte[] b)
 429:   {
 430:     StringBuffer hexBytes = new StringBuffer();
 431:     int length = b.length;
 432:     for (int i = 0; i < length; ++i)
 433:       {
 434:     int v = b[i] & 0xff;
 435:     if (v < 16)
 436:       hexBytes.append('0');
 437:     hexBytes.append(Integer.toHexString(v));
 438:       }
 439:     return hexBytes.toString();
 440:   }
 441: 
 442: 
 443:   // Return a Fileset, either from a String array or from System.in,
 444:   // depending on fileListFromStdin.
 445:   private static final Fileset getFiles(String[] s, int startPos,
 446:                     boolean fileListFromStdin,
 447:                     char separator)
 448:   {
 449:     if (fileListFromStdin)
 450:       return new Fileset(System.in, separator);
 451:     else
 452:       return new Fileset(s, startPos, s.length);
 453:   }
 454: }
 455: 
 456: // Parse a stream into tokens.  The separator can be any char, and
 457: // space is equivalent to any whitepace character.
 458: class Tokenizer
 459: {
 460:   final Reader r;
 461:   final char separator;
 462: 
 463:   Tokenizer(Reader r, char separator)
 464:   {
 465:     this.r = r;
 466:     this.separator = separator;
 467:   }
 468: 
 469:   boolean isSeparator(int c)
 470:   {
 471:     if (Character.isWhitespace(separator))
 472:       return Character.isWhitespace((char)c);
 473:     else
 474:       return c == separator;
 475:   }
 476: 
 477:   // Parse a token from the input stream.  Return the empty string
 478:   // when the stream is exhausted.
 479:   String nextToken ()
 480:   {
 481:     StringBuffer buf = new StringBuffer();
 482:     int c;
 483:     try
 484:       {
 485:     while ((c = r.read()) != -1)
 486:       {
 487:         if (! isSeparator(c))
 488:           {
 489:         buf.append((char)c);
 490:         break;
 491:           }
 492:       }
 493:     while ((c = r.read()) != -1)
 494:       {
 495:         if (isSeparator(c))
 496:           break;
 497:         else
 498:           buf.append((char)c);
 499:       }
 500:       }
 501:     catch (java.io.IOException e)
 502:       {
 503:       }
 504:     return buf.toString();
 505:   }
 506: }
 507: 
 508: // A Fileset is a container for a set of files; it can be created
 509: // either from a string array or from an input stream, given a
 510: // separator character.
 511: class Fileset
 512: {
 513:   LinkedHashSet files = new LinkedHashSet();
 514:   
 515:   Fileset (String[] s, int start, int end)
 516:   {
 517:     for (int i = start; i < end; i++)
 518:       {
 519:     files.add(new File(s[i]));
 520:       }
 521:   }
 522: 
 523:   Fileset (InputStream is, char separator)
 524:   {
 525:     Reader r = new BufferedReader(new InputStreamReader(is));
 526:     Tokenizer st = new Tokenizer(r, separator);
 527:     String name;
 528:     while (! "".equals(name = st.nextToken()))
 529:       files.add(new File(name));
 530:   }
 531: 
 532:   Iterator iterator()
 533:   {
 534:     return files.iterator();
 535:   }
 536: 
 537:   int size()
 538:   {
 539:     return files.size();
 540:   }
 541: }