Diff for /FM2SQL/Attic/Convert.java between versions 1.65 and 1.72

version 1.65, 2004/08/02 11:39:23 version 1.72, 2004/08/12 09:54:38
Line 29  import java.sql.PreparedStatement; Line 29  import java.sql.PreparedStatement;
 import java.sql.SQLException;  import java.sql.SQLException;
 import java.sql.Statement;  import java.sql.Statement;
 import java.sql.Types;  import java.sql.Types;
   import java.text.ParseException;
 import java.util.ArrayList;  import java.util.ArrayList;
   import java.util.Hashtable;
 import java.util.Iterator;  import java.util.Iterator;
 import java.util.List;  import java.util.List;
 import java.util.Properties;  
 import java.util.StringTokenizer;  import java.util.StringTokenizer;
 import java.util.TreeSet;  import java.util.TreeSet;
 import java.util.Vector;  import java.util.Vector;
Line 42  import com.exploringxml.xml.Xparse; Line 43  import com.exploringxml.xml.Xparse;
   
 class Convert  class Convert
 {  {
     /**Helper class for index creation
      * @author rogo
      *
      */
     public static class IndexList extends Vector
     {
       public String toString()
       {
         StringBuffer buff = new StringBuffer(1000);
         int count = 0;
         for (Iterator iter = this.iterator(); iter.hasNext();)
         {
           String element = (String) iter.next();
           if (count < elementCount - 1)
           {
             buff.append(element).append(", ");
             count++;
           } else
             buff.append(element);
   
         }
   
         return buff.toString();
       }
     }
   static DBBean bean = new DBBean();    static DBBean bean = new DBBean();
   static DBBean beanDest = new DBBean();    static DBBean beanDest = new DBBean();
   
Line 1126  class Convert Line 1152  class Convert
     Vector selects = new Vector();      Vector selects = new Vector();
     Vector creates = new Vector();      Vector creates = new Vector();
     Vector ids = new Vector();      Vector ids = new Vector();
       Vector indexListVec = new Vector();
     String delimiter = "|";      String delimiter = "|";
     int mode = -1;      int mode = -1;
   
Line 1145  class Convert Line 1172  class Convert
         selects = new Vector();          selects = new Vector();
         creates = new Vector();          creates = new Vector();
         ids = new Vector();          ids = new Vector();
           indexListVec = new Vector();
         // parse dataBase          // parse dataBase
         Node node = root.find("convert/source/database/url", new int[] { 1, 1, i, 1 });          Node node = root.find("convert/source/database/url", new int[] { 1, 1, i, 1 });
         Node node1 = root.find("convert/source/database/user", new int[] { 1, 1, i, 1, 1 });          Node node1 = root.find("convert/source/database/user", new int[] { 1, 1, i, 1, 1 });
Line 1219  class Convert Line 1247  class Convert
           String name = (String) node4.attributes.get("name");            String name = (String) node4.attributes.get("name");
           String layout = (String) node4.attributes.get("layout");            String layout = (String) node4.attributes.get("layout");
           String id = (String) node4.attributes.get("id");            String id = (String) node4.attributes.get("id");
             String indexList = (String) node4.attributes.get("indexList");
   
           System.out.println("id was " + id);            System.out.println("id was " + id);
           if (name == null)            if (name == null)
             throw new Error("parse error required table tag attribute name missing");              throw new Error("parse error required table tag attribute name missing");
Line 1226  class Convert Line 1256  class Convert
             layout = "";              layout = "";
           if (id == null)            if (id == null)
             id = "";              id = "";
             if (indexList == null)
               indexList = "";
           if (name.equals(""))            if (name.equals(""))
             throw new Error("parse error table tag attribute must not be empty");              throw new Error("parse error table tag attribute must not be empty");
           tables.add(name);            tables.add(name);
           layouts.add(layout);            layouts.add(layout);
   
           ids.add(id);            ids.add(id);
             indexListVec.add(indexList);
           String query = (node5 == null) ? "" : node5.getCharacters();            String query = (node5 == null) ? "" : node5.getCharacters();
           if (query.equals(""))            if (query.equals(""))
             System.err.println("Warning empty select tag or  select tag missing !!");              System.err.println("Warning empty select tag or  select tag missing !!");
Line 1244  class Convert Line 1278  class Convert
         }          }
         DataBase dataBase = new DataBase(database, tables, layouts, selects, creates, ids, mode);          DataBase dataBase = new DataBase(database, tables, layouts, selects, creates, ids, mode);
         dataBase.delimiter = delimiter;          dataBase.delimiter = delimiter;
           dataBase.buildIndexTable(indexListVec);
         databases.add(dataBase);          databases.add(dataBase);
       }        }
       DBBean database = new DBBean();        DBBean database = new DBBean();
Line 1272  class Convert Line 1307  class Convert
           userDest = database.user;            userDest = database.user;
           passwdDest = database.passwd;            passwdDest = database.passwd;
   
           synchronize(db.bean.url, database.url, db.tables, db.layouts, db.selects, db.creates, db.ids, mode);            synchronize(db.bean.url, database.url, db.tables, db.layouts, db.selects, db.creates, db.ids, mode, db.delimiter, new Vector(db.htIndex.values()));
         }          }
       }        }
       // printContents(node3);        // printContents(node3);
Line 1574  class Convert Line 1609  class Convert
     Vector tables;      Vector tables;
     Vector ids;      Vector ids;
     String delimiter = "//";      String delimiter = "//";
       /**
        * maps table name to index fields
        */
       Hashtable htIndex = new Hashtable();
     boolean useNormanToUnicodeMapper = false;      boolean useNormanToUnicodeMapper = false;
   
     final static int CONVERT_MODE = 1;      final static int CONVERT_MODE = 1;
Line 1596  class Convert Line 1635  class Convert
       this.bean.setIDVector(ids);        this.bean.setIDVector(ids);
     }      }
     /**      /**
        * @param indexListVec
        */
       public void buildIndexTable(Vector indexListVec)
       {
         for (int i = 0; i < tables.size(); i++)
         {
           fillIndexList((String) tables.get(i), (String) indexListVec.get(i));
         }
       }
       /**
      * writes the data contained in this object to the buffered writer       * writes the data contained in this object to the buffered writer
      * * @param buffr       * * @param buffr
      * @throws Exception       * @throws Exception
Line 1629  class Convert Line 1678  class Convert
         String select = (String) selects.get(index);          String select = (String) selects.get(index);
         String create = (String) creates.get(index);          String create = (String) creates.get(index);
         String id = (String) ids.get(index);          String id = (String) ids.get(index);
           IndexList indexList = (IndexList) htIndex.get(table);
         buffr.write("      <table name = \"" + table + "\" layout = \"" + layout + "\" id = \"" + id + "\" >\n");          if (indexList == null)
             indexList = new IndexList();
           buffr.write("      <table name = \"" + table + "\" layout = \"" + layout + "\" id = \"" + id + "\" indexList =\"" + indexList + "\">\n");
         buffr.write("         <select>" + convertToEntities(select) + "</select>\n");          buffr.write("         <select>" + convertToEntities(select) + "</select>\n");
         if (!create.equals(""))          if (!create.equals(""))
           buffr.write("         <create>" + create + "         </create>\n");            buffr.write("         <create>" + create + "         </create>\n");
Line 1639  class Convert Line 1690  class Convert
       }        }
       buffr.write("    </database>\n");        buffr.write("    </database>\n");
     }      }
   
       public void fillIndexList(String table, String list)
       {
         IndexList indexList = new IndexList();
         StringTokenizer tokenizer = new StringTokenizer(list, ",");
         while (tokenizer.hasMoreTokens())
         {
           indexList.add(tokenizer.nextToken());
         }
         System.out.println(indexList);
   
         htIndex.put(table, indexList);
       }
     public String toString()      public String toString()
     {      {
       return bean.url + " " + tables;        return bean.url + " " + tables;
Line 1896  class Convert Line 1960  class Convert
    */     */
   // TODO implement append,update and delete in one method    // TODO implement append,update and delete in one method
   // TODO using id based algorithm    // TODO using id based algorithm
   public static void synchronize(String source, String destination, Vector names, Vector layouts, Vector selects, Vector creates, Vector ids, int mode) throws Exception    public static void synchronize(String source, String destination, Vector names, Vector layouts, Vector selects, Vector creates, Vector ids, int mode, String delimiter, Vector indexList)
       throws Exception
   {    {
     System.out.println(" bin in synchronize!!!");      System.out.println(" bin in synchronize!!!");
     FM2SQL.ProgressDialog dialog = null;      FM2SQL.ProgressDialog dialog = null;
Line 2000  class Convert Line 2065  class Convert
         {          {
           long startTime = System.currentTimeMillis();            long startTime = System.currentTimeMillis();
           int counter = -1;            int counter = -1;
             TreeSet linesToDelete = null;
             PreparedStatement delPSt = null;
           while (true)            while (true)
           {            {
             ++counter;              ++counter;
Line 2007  class Convert Line 2074  class Convert
               dialog.title.setText("Check if data  is available");                dialog.title.setText("Check if data  is available");
             else if (dialog != null)              else if (dialog != null)
               dialog.title.setText("Check if more  data  is available");                dialog.title.setText("Check if more  data  is available");
             myIds = bean.getIDVector(ids.get(tbIndex).toString(), (String) names.get(tbIndex), tempQuery, numHits);              myIds = bean.getIDVector(ids.get(tbIndex).toString(), (String) names.get(tbIndex), tempQuery, 0);
             myIdsDest = beanDest.getIDVector(convertText(idField), destTableName, tempQueryDest, numHits);              myIdsDest = beanDest.getIDVector(convertText(idField), destTableName, tempQueryDest, 0);
               //System.out.println("status of remove  "+myIds.remove("b015892"));
             if (myIds.isEmpty())              if (myIds.isEmpty())
               break;                break;
             vec = new Vector(myIds);              vec = new Vector(myIds);
Line 2016  class Convert Line 2084  class Convert
             rowCount = vec.size();              rowCount = vec.size();
             // Deletion will work this way              // Deletion will work this way
             Vector deleted = new Vector(vec);              Vector deleted = new Vector(vec);
             TreeSet linesToDelete = new TreeSet(vecDest);  
   
             TreeSet linesToAppend = new TreeSet(vec);              TreeSet linesToAppend = new TreeSet(vec);
               linesToAppend.addAll(vec);
               linesToDelete = new TreeSet(vecDest);
             // remove all lines that are already in dest database              // remove all lines that are already in dest database
             linesToAppend.removeAll(vecDest);              linesToAppend.removeAll(vecDest);
             // remove all lines that should not be deleted              // remove all lines that should not be deleted
             linesToDelete.removeAll(deleted);              linesToDelete.removeAll(deleted);
             System.out.println("linesToAppend " + linesToAppend + " " + vecDest + " " + destTableName);              System.out.println("linesToAppend " + linesToAppend + " " + destTableName);
             System.out.println("linesToDelete " + linesToDelete + " " + vecDest + " " + destTableName);              System.out.println("linesToDelete " + linesToDelete + " " + destTableName);
             // prepare new query for next chunk              System.out.println("ID LIST SIZE " + Math.round((double) myIds.size() / (double) numIntervalls) + " " + myIds.size());
             if (query.indexOf("where") > 0)              deltaID = (int) Math.round((double) myIds.size() / (double) numIntervalls);
               tempQuery = query + " and " + tempID + ">='" + vec.firstElement() + "' and " + tempID + "<='" + vec.lastElement() + "' order by " + tempID;  
             else  
               tempQuery = query + " where " + tempID + ">='" + vec.firstElement() + "' and " + tempID + "<='" + vec.lastElement() + "'"; // order by "+tempID;  
             System.out.println("tempQuery is now " + tempQuery + " order by " + tempID);  
             //  bean.makeQuery(tempQuery,vec.size());  
   
             ConversionProperties prop = getFieldNamesAndDestTableName(creates.get(tbIndex).toString(), query, names.get(tbIndex).toString());              ConversionProperties prop = getFieldNamesAndDestTableName(creates.get(tbIndex).toString(), query, names.get(tbIndex).toString());
             StringBuffer insCommand = createInsertCommand(prop.destTableName, prop.fieldNames);              StringBuffer insCommand = createInsertCommand(prop.destTableName, prop.fieldNames);
             StringBuffer updCommand  = createUpdateCommand(prop.destTableName, prop.fieldNames, tempID);              StringBuffer updCommand = createUpdateCommand(prop.destTableName, prop.fieldNames, tempIDdest);
             StringBuffer delCommand  = createDeleteCommand(destTableName,tempID);              StringBuffer delCommand = createDeleteCommand(destTableName, tempIDdest);
             PreparedStatement insPst = beanDest.getConnection().prepareStatement(insCommand.toString());              PreparedStatement insPst = beanDest.getConnection().prepareStatement(insCommand.toString());
             PreparedStatement updPst = beanDest.getConnection().prepareStatement(updCommand.toString());              PreparedStatement updPst = beanDest.getConnection().prepareStatement(updCommand.toString());
             PreparedStatement delPSt = beanDest.getConnection().prepareStatement(delCommand.toString());              delPSt = beanDest.getConnection().prepareStatement(delCommand.toString());
             Vector[] vectors = bean.getQueryData(tempQuery, vec.size());              //    delPSt.setString(1,"b015892");
             int idIndex = vectors[1].indexOf(idField);              //   delPSt.execute();
             // todo arraylist code has to be added              //  if (true)
             for (Iterator iter = vectors[0].iterator(); iter.hasNext();)              //  return;
             {              if (vec.size() <= numIntervalls)
               Vector line = (Vector) iter.next();  
               Object lineIDIndex = line.get(idIndex);  
               System.out.println("line " + linesToDelete.contains(line.get(idIndex))+" "+lineIDIndex);  
               if (linesToAppend.contains(lineIDIndex))  
               {                {
                 for (int l = 0; l < line.size(); ++l)                endIndex = 0;
                 deltaID = vec.size();
               }
               for (int k = 0; k < vec.size() - deltaID; k = k + deltaID)
                 {                  {
                   Object obj = line.get(l);                System.out.println(vec.get(k) + " " + vec.get(k + deltaID) + " " + vec.lastElement());
                   if (obj != null)                if (query.indexOf("where") > 0)
                     insPst.setString(l + 1, obj.toString());                  tempQuery = query + " and " + tempID + ">='" + vec.get(k) + "' and " + tempID + "<='" + vec.get(k + deltaID) + "'";
                   else                    else
                     insPst.setNull(l + 1, Types.NULL);                  tempQuery = query + " where " + tempID + ">='" + vec.get(k) + "' and " + tempID + "<='" + vec.get(k + deltaID) + "'";
                 }                System.out.println(tempQuery);
                 insPst.execute();                if (dialog != null)
                   dialog.title.setText("Reading table data ...");
                               
               }                   // bean.makeQuery(tempQuery, deltaID);
                 if (dialog != null)
                   dialog.title.setText("Writing table data ...");
                               
               else // update                performSynchronize(idField, vec, tempQuery, linesToDelete, linesToAppend, insPst, updPst, delPSt, deltaID, delimiter);
                 {                // System.out.println("ID LIST SIZE " + Math.round((double) myIds.size() / (double) numIntervalls) + " " + myIdsDest.size());
                   for (int l = 0; l < line.size(); ++l)                endIndex = k + deltaID;
               }
               System.out.println(endIndex);
               //all data written ? if not write last chunk of data
               if (endIndex == vec.size() - 1)
                 System.out.println("fits");
               else
                         {                          {
                           Object obj = line.get(l);                System.out.println(" last intervall from " + vec.get(endIndex) + " " + vec.lastElement());
                           if (obj != null)  
                             updPst.setString(l + 1, obj.toString());                if (query.indexOf("where") > 0)
                   tempQuery = query + " and " + tempID + ">='" + vec.get(endIndex) + "' and " + tempID + "<='" + vec.lastElement() + "'";
                           else                            else
                             updPst.setNull(l + 1, Types.NULL);                  tempQuery = query + " where " + tempID + ">='" + vec.get(endIndex) + "' and " + tempID + "<='" + vec.lastElement() + "'";
                 System.out.println(tempQuery);
                 if (dialog != null)
                   dialog.title.setText("Reading table data ...");
                 //    bean.makeQuery(tempQuery, 0);
                 if (dialog != null)
                   dialog.title.setText("Writing table data ...");
                 performSynchronize(idField, vec, tempQuery, linesToDelete, linesToAppend, insPst, updPst, delPSt, deltaID, delimiter);
                 // System.out.println("ID LIST SIZE " + Math.round((double) myIds.size() / (double) numIntervalls) + " " + myIdsDest.size());
                         }                          }
                         updPst.setString(line.size()+1,line.get(idIndex).toString());              // prepare new query for next chunk
                         updPst.execute();              if (query.indexOf("where") > 0)
                 tempQuery = query + " and " + tempID + ">'" + vec.lastElement() + "'";
               else
                 tempQuery = query + " where " + tempID + ">'" + vec.lastElement() + "'";
               
                 }                  }
             }            String tableName = names.get(tbIndex).toString();
                        IndexList idList = (IndexList) indexList.get(0);
           //  delPSt.setString(1,"6");            System.out.println("found list " + idList);
           //  delPSt.setString(1,"7");  
           //  delPSt.setString(1,"8");  
             
             //delPSt.execute();  
             System.out.println();  
             // System.out.println("ID LIST SIZE " + Math.round((double) myIds.size() / (double) numIntervalls) + " " + myIdsDest.size());  
             /// @TODO complete delete task remove query show lines to be deleted let user choose if he wants that  
             System.out.println("number of lines to  be deleted " + linesToDelete.size());  
             deltaID = (int) Math.round((double) myIds.size() / (double) numIntervalls);  
             System.out.println("deltaID " + deltaID);  
             //System.out.println();  
             
             if (true)  
               return;  
   
             beanDest.setConnection(destination);  
   
             Statement stm = beanDest.getConnection().createStatement();              Statement stm = beanDest.getConnection().createStatement();
             Vector destTables = beanDest.getTableNames();
             beanDest.makeQuery(tempQueryDest,0);
             for (Iterator iter = idList.iterator(); iter.hasNext();)
             {
               String indexField = (String) iter.next();
               indexField = convertText(indexField);
               String indexName = destTableName + "_" + indexField;
               if (destTables.contains(indexName))
               {
                 stm.execute("DROP  INDEX "+destTableName+"_"+indexField);
               //  continue;
               }
   
             Vector tables = beanDest.getTableNames();              String type = beanDest.getColumnType(indexField).toLowerCase();
             // Collections.sort(tables);             // System.out.println(indexField+" "+type+" "+(type.indexOf("text") >= 0 || type.indexOf("varchar") >= 0 || type.indexOf("char") >= 0));
             System.out.println(names.get(tbIndex) + " " + tables.indexOf(convertText((String) names.get(tbIndex)))); // "//beanDest.getTypeNames());               if (type.indexOf("text") >= 0 || type.indexOf("varchar") >= 0 || type.indexOf("char") >= 0)
             tables = beanDest.getTableNames();               stm.execute("CREATE  INDEX " + indexName + " ON " + destTableName + "(LOWER(" + indexField + "))");
             // System.out.println(beanDest.getTableNames(beanDest.getCatalogs().get(2).toString()));              else
             stm = beanDest.getConnection().createStatement();                stm.execute("CREATE  INDEX " + destTableName + "_" + indexField + " ON " + destTableName + "(" + indexField + ")");
   
             if (dialog != null)  
               dialog.title.setText(" Deleting table data ...");  
   
             int j = -1;  
   
             Vector row = null;               // stm.execute("DROP  INDEX "+destTableName+"_"+indexField);
             command = new StringBuffer();  
   
             command.append("DELETE FROM");            }
             command.append(beanDest.getQC());            //  CREATE UNIQUE INDEX title_idx ON films (title);  
             command.append(destTableName);            for (Iterator iter = linesToDelete.iterator(); iter.hasNext();)
             //command.append(convertText((String) names.get(tbIndex)));  
             command.append(beanDest.getQC());  
             int size = bean.getColumnNames().size();  
             command.append("WHERE " + convertText(ids.get(tbIndex).toString()) + " =  ?");  
             PreparedStatement pstm = beanDest.getConnection().prepareStatement(command.toString());  
             System.out.println(command + " " + tbIndex);  
             //int rowCount = bean.getRowCount(query);  
             //        int idIndex = bean.getColumnNames().indexOf(ids.get(tbIndex));  
 /*            while (true)  
             {              {
               String id = (String) iter.next();
               delPSt.setString(1, id);
               delPSt.execute();
   
               ++j;  
               if (j == linesToDelete.size())  
                 break;  
               //print rows  
               pstm.setString(1, linesToDelete.get(j).toString());  
               System.out.println(pstm.toString());  
               pstm.execute();  
               if (dialog != null)  
                 dialog.progress.setValue((int) (((double) (j + 1) / (double) rowCount) * 100.0));  
               command = null;  
             }              }
             // prepare new query for next chunk  
             if (query.indexOf("where") > 0)            long endTime = System.currentTimeMillis();
               tempQuery = query + " and " + tempID + ">'" + vec.lastElement() + "'";            System.out.println("Time for incremental synchronize  elapsed " + (endTime - startTime));
             else  
               tempQuery = query + " where " + tempID + ">'" + vec.lastElement() + "'";  
 */  
           } //to outer while  
         } // to idfield if            } // to idfield if  
       } // table loop        } // table loop
   
     } catch (Exception e)      } catch (Exception e)
     {      {
       System.out.println("Error while connecting to database " + e);        System.out.println("Error while connecting to database " + e);
         e.printStackTrace();
       if (dialog != null)        if (dialog != null)
       {        {
         dialog.setVisible(false);          dialog.setVisible(false);
Line 2171  class Convert Line 2224  class Convert
   
       dialog.setVisible(false);        dialog.setVisible(false);
     }      }
     }
     private static void performSynchronize(
       String idField,
       Vector vec,
       String tempQuery,
       TreeSet linesToDelete,
       TreeSet linesToAppend,
       PreparedStatement insPst,
       PreparedStatement updPst,
       PreparedStatement delPSt,
       int deltaID,
       String delimiter)
       throws SQLException, ParseException
     {
       Vector[] vectors = bean.getQueryData(tempQuery, deltaID);
       int idIndex = vectors[1].indexOf(idField);
       // todo arraylist code has to be added
       for (Iterator iter = vectors[0].iterator(); iter.hasNext();)
       {
         Vector line = (Vector) iter.next();
         Object lineIDIndex = line.get(idIndex);
         if (linesToAppend.contains(lineIDIndex))
           System.out.println("line " + linesToAppend.contains(line.get(idIndex)) + " " + lineIDIndex);
         if (linesToAppend.contains(lineIDIndex))
         {
           for (int l = 0; l < line.size(); ++l)
           {
             Object obj = line.get(l);
             if (obj instanceof ArrayList)
               obj = formatFileMakerArray((List) obj, delimiter);
             if (obj != null)
               insPst.setString(l + 1, obj.toString());
             else
               insPst.setNull(l + 1, Types.NULL);
           }
           insPst.execute();
   
         } else // update
           {
           for (int l = 0; l < line.size(); ++l)
           {
             Object obj = line.get(l);
             if (obj instanceof ArrayList)
               obj = formatFileMakerArray((List) obj, delimiter);
             if (obj != null)
               updPst.setString(l + 1, obj.toString());
             else
               updPst.setNull(l + 1, Types.NULL);
           }
           updPst.setString(line.size() + 1, line.get(idIndex).toString());
           //updPst.addBatch();
           updPst.execute();
         }
       }
       //updPst.executeBatch();
   } // to method    } // to method
   
   /**    /**
Line 3692  class Convert Line 3800  class Convert
         while (tokenizer.hasMoreTokens())          while (tokenizer.hasMoreTokens())
         {          {
           String fieldName = tokenizer.nextToken().trim();            String fieldName = tokenizer.nextToken().trim();
           fieldNames[fieldIndex] = convertText(fieldName);            fieldNames[fieldIndex] =beanDest.getQC() + convertText(fieldName)+beanDest.getQC();
           // System.out.println("field "+ fieldNames[fieldIndex]);            // System.out.println("field "+ fieldNames[fieldIndex]);
           fieldIndex++;            fieldIndex++;
         }          }
Line 3705  class Convert Line 3813  class Convert
         for (Iterator iter = fieldNamesVec.iterator(); iter.hasNext();)          for (Iterator iter = fieldNamesVec.iterator(); iter.hasNext();)
         {          {
           String element = (String) iter.next();            String element = (String) iter.next();
           fieldNames[++fieldIndex] = bean.getQC() + convertText(element) + bean.getQC();            fieldNames[++fieldIndex] = beanDest.getQC() + convertText(element) + beanDest.getQC();
           // System.out.println("field " + fieldNames[fieldIndex]);            // System.out.println("field " + fieldNames[fieldIndex]);
         }          }
       }        }

Removed from v.1.65  
changed lines
  Added in v.1.72


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>