Source for gnu.gcj.tools.gcj_dbtool.Main

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