OSDN Git Service

bc40a5dedfb2e9fd88a90a196df5996e6859cbdb
[pf3gnuchains/gcc-fork.git] / libjava / gnu / gcj / tools / gcj_dbtool / Main.java
1 /* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009  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 2009 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 }