1 /*
   2  * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package build.tools.stripproperties;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.BufferedWriter;
  30 import java.io.FileInputStream;
  31 import java.io.FileNotFoundException;
  32 import java.io.FileOutputStream;
  33 import java.io.OutputStream;
  34 import java.io.OutputStreamWriter;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.util.ArrayList;
  38 import java.util.Enumeration;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Properties;
  42 
  43 /**
  44  * Reads a properties file from standard input and writes an equivalent
  45  * properties file without comments to standard output.
  46  */
  47 public class StripPropertiesCorba {
  48 
  49     private static void error(String msg, Exception e) {
  50         System.err.println("ERROR: stripproperties: " + msg);
  51         if ( e != null ) {
  52             System.err.println("EXCEPTION: " + e.toString());
  53             e.printStackTrace();
  54         }
  55     }
  56 
  57     private static List<String> infiles = new ArrayList<String>();
  58     private static List<String> outfiles = new ArrayList<String>();
  59 
  60     private static boolean parseOptions(String args[]) {
  61         boolean ok = true;
  62 
  63         for ( int i = 0; i < args.length ; i++ ) {
  64             if ( "-clean".equals(args[i]) && i+2 < args.length ) {
  65                 infiles.add(args[++i]);
  66                 outfiles.add(args[++i]);
  67             } else if ( args[i].charAt(0)=='@') {
  68                 String filename = args[i].substring(1);
  69                 FileInputStream finput = null;
  70                 byte contents[] = null;
  71                 try {
  72                     finput = new FileInputStream(filename);
  73                     int byteCount = finput.available();
  74                     if ( byteCount <= 0 ) {
  75                         error("The @file is empty", null);
  76                         ok = false;
  77                     } else {
  78                         contents = new byte[byteCount];
  79                         int bytesRead = finput.read(contents);
  80                         if ( byteCount != bytesRead ) {
  81                             error("Cannot read all of @file", null);
  82                             ok = false;
  83                         }
  84                     }
  85                 } catch ( IOException e ) {
  86                     error("cannot open " + filename, e);
  87                     ok = false;
  88                 }
  89                 if ( finput != null ) {
  90                     try {
  91                         finput.close();
  92                     } catch ( IOException e ) {
  93                         ok = false;
  94                         error("cannot close " + filename, e);
  95                     }
  96                 }
  97                 if ( ok && contents != null ) {
  98                     String tokens[] = (new String(contents)).split("\\s+");
  99                     if ( tokens.length > 0 ) {
 100                         ok = parseOptions(tokens);
 101                     }
 102                 }
 103                 if ( !ok ) {
 104                     break;
 105                 }
 106             } else {
 107                 infiles.add(args[i]);
 108                 outfiles.add(args[i]);
 109             }
 110         }
 111         return ok;
 112     }
 113 
 114     private static boolean stripFiles(List<String> infiles, List<String> outfiles) {
 115         boolean ok = true;
 116         Iterator<String> inIter  = infiles.iterator();
 117         Iterator<String> outIter = outfiles.iterator();
 118 
 119         for (; inIter.hasNext(); ) {
 120             String infile = inIter.next();
 121             String outfile = outIter.next();
 122 
 123             Properties prop = new Properties();
 124             InputStream in = null;
 125             try {
 126                 in = new BufferedInputStream(new FileInputStream(infile));
 127                 prop.load(in);
 128             } catch ( FileNotFoundException e ) {
 129                 error("Cannot access file " + infile, e);
 130                 ok = false;
 131             } catch ( IOException e ) {
 132                 error("IO exception processing file " + infile, e);
 133                 ok = false;
 134             }
 135             if ( in != null ) {
 136                 try {
 137                     in.close();
 138                 } catch ( IOException e ) {
 139                     error("IO exception closing file " + infile, e);
 140                     ok = false;
 141                 }
 142             }
 143             if ( !ok ) {
 144                 break;
 145             }
 146 
 147             OutputStream out = null;
 148             try {
 149                 out = new FileOutputStream(outfile);
 150                 storeProperties(prop, out);
 151                 out.flush();
 152             } catch ( IOException e ) {
 153                 error("IO exception processing file " + outfile, e);
 154                 ok = false;
 155             }
 156             if ( out != null ) {
 157                 try {
 158                     out.close();
 159                 } catch ( IOException e ) {
 160                     error("IO exception closing file " + outfile, e);
 161                     ok = false;
 162                 }
 163             }
 164             if ( !ok ) {
 165                 break;
 166             }
 167 
 168         }
 169         return ok;
 170     }
 171 
 172     /**
 173      * Strip the properties filenames supplied, replacing their contents.
 174      * @param args Names of properties files to process and replace contents
 175      */
 176     public static void main(String args[]) {
 177         boolean ok = parseOptions(args);
 178         if ( !ok || !stripFiles(infiles, outfiles) ) {
 179             System.exit(1);
 180         }
 181     }
 182 
 183     // --- code below here is adapted from java.util.Properties ---
 184 
 185     private static final String specialSaveChars = "=: \t\r\n\f#!";
 186 
 187     /*
 188      * Converts unicodes to encoded &#92;uxxxx
 189      * and writes out any of the characters in specialSaveChars
 190      * with a preceding slash
 191      */
 192     private static String saveConvert(String theString, boolean escapeSpace) {
 193         int len = theString.length();
 194         StringBuffer outBuffer = new StringBuffer(len*2);
 195 
 196         for(int x=0; x<len; x++) {
 197             char aChar = theString.charAt(x);
 198             switch(aChar) {
 199                 case ' ':
 200                     if (x == 0 || escapeSpace) {
 201                         outBuffer.append('\\');
 202                     }
 203                     outBuffer.append(' ');
 204                     break;
 205                 case '\\':
 206                     outBuffer.append('\\');
 207                     outBuffer.append('\\');
 208                     break;
 209                 case '\t':
 210                     outBuffer.append('\\');
 211                     outBuffer.append('t');
 212                     break;
 213                 case '\n':
 214                     outBuffer.append('\\');
 215                     outBuffer.append('n');
 216                     break;
 217                 case '\r':
 218                     outBuffer.append('\\');
 219                     outBuffer.append('r');
 220                     break;
 221                 case '\f':
 222                     outBuffer.append('\\');
 223                     outBuffer.append('f');
 224                     break;
 225                 default:
 226                     if ((aChar < 0x0020) || (aChar == 0x007e) || (aChar > 0x00ff)) {
 227                         outBuffer.append('\\');
 228                         outBuffer.append('u');
 229                         outBuffer.append(toHex((aChar >> 12) & 0xF));
 230                         outBuffer.append(toHex((aChar >>  8) & 0xF));
 231                         outBuffer.append(toHex((aChar >>  4) & 0xF));
 232                         outBuffer.append(toHex( aChar        & 0xF));
 233                     } else {
 234                         if (specialSaveChars.indexOf(aChar) != -1) {
 235                             outBuffer.append('\\');
 236                         }
 237                         outBuffer.append(aChar);
 238                     }
 239             }
 240         }
 241         return outBuffer.toString();
 242     }
 243 
 244     /**
 245      * Writes the content of <code>properties</code> to <code>out</code>.
 246      * The format is that of Properties.store with the following modifications:
 247      * <ul>
 248      * <li>No header or date is written
 249      * <li>Latin-1 characters are written as single bytes, not escape sequences
 250      * <li>Line breaks are indicated by a single \n independent of platform
 251      * <ul>
 252      */
 253     private static void storeProperties(Properties properties, OutputStream out)
 254     throws IOException {
 255         BufferedWriter awriter;
 256         awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
 257         for (Enumeration<Object> e = properties.keys(); e.hasMoreElements();) {
 258             String key = (String)e.nextElement();
 259             String val = (String)properties.get(key);
 260             key = saveConvert(key, true);
 261 
 262             /* No need to escape embedded and trailing spaces for value, hence
 263              * pass false to flag.
 264              */
 265             val = saveConvert(val, false);
 266             writeln(awriter, key + "=" + val);
 267         }
 268         awriter.flush();
 269     }
 270 
 271     private static void writeln(BufferedWriter bw, String s) throws IOException {
 272         bw.write(s);
 273         bw.write("\n");
 274     }
 275 
 276     /**
 277      * Convert a nibble to a hex character
 278      * @param   nibble  the nibble to convert.
 279      */
 280     private static char toHex(int nibble) {
 281         return hexDigit[(nibble & 0xF)];
 282     }
 283 
 284     /** A table of hex digits */
 285     private static final char[] hexDigit = {
 286         '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
 287     };
 288 }