001 /* JAPI - (Yet another (hopefully) useful) Java API 002 * 003 * Copyright (C) 2004-2006 Christian Hujer 004 * 005 * This program is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU General Public License as 007 * published by the Free Software Foundation; either version 2 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program; if not, write to the Free Software 017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 * 02111-1307, USA. 019 */ 020 021 package net.sf.japi.cpp; 022 023 import java.io.BufferedReader; 024 import java.io.File; 025 import java.io.FileReader; 026 import java.io.IOException; 027 import java.io.InputStream; 028 import java.io.InputStreamReader; 029 import java.io.OutputStream; 030 import java.io.OutputStreamWriter; 031 import java.io.PrintWriter; 032 import java.io.Reader; 033 import java.io.Writer; 034 import java.util.ArrayList; 035 import static java.util.Collections.unmodifiableList; 036 import java.util.HashMap; 037 import java.util.List; 038 import java.util.Map; 039 import static net.sf.japi.cpp.State.CHAR; 040 import static net.sf.japi.cpp.State.CHAR_ESCAPE; 041 import static net.sf.japi.cpp.State.DIRECTIVE; 042 import static net.sf.japi.cpp.State.DIRECTIVE_ESCAPE; 043 import static net.sf.japi.cpp.State.EOL_COMMENT; 044 import static net.sf.japi.cpp.State.MAYBE_COMMENT; 045 import static net.sf.japi.cpp.State.MAYBE_ENDOF_COMMENT; 046 import static net.sf.japi.cpp.State.MULTILINE_COMMENT; 047 import static net.sf.japi.cpp.State.NORMAL; 048 import static net.sf.japi.cpp.State.STRING; 049 import static net.sf.japi.cpp.State.STRING_ESCAPE; 050 051 /** A C Preprocessor. 052 * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a> 053 */ 054 public class CPreProcessor { 055 056 /** Synchronization lock object. */ 057 private final Object syncLock = new Object(); 058 059 /** List with include directory paths. */ 060 private final List<File> includePaths = new ArrayList<File>(); 061 062 /** Map with included files. */ 063 private final Map<String, String> includes = new HashMap<String, String>(); 064 065 /** Map with defined macros. */ 066 private final Map<String, String> defines = new HashMap<String, String>(); 067 068 /** Setting whether to strip comments. */ 069 private boolean stripComments = true; 070 071 public CPreProcessor() { 072 } 073 074 /** Add an include path. 075 * @param file include path to add 076 * @throws IllegalArgumentException in case <var>file</var> is not a directory 077 */ 078 public void addIncludePath(final File file) { 079 if (!file.isDirectory()) { 080 throw new IllegalArgumentException("Only directories are valid for include paths."); 081 } 082 includePaths.add(file); 083 } 084 085 /** Get a list with all include paths. 086 * @return list with all include paths 087 */ 088 public List<File> getIncludePaths() { 089 return unmodifiableList(includePaths); 090 } 091 092 private String include(final String filename) throws IOException { 093 synchronized (syncLock) { 094 if (!includes.containsKey(filename)) { 095 includes.put(filename, readFile(filename)); 096 } 097 assert includes.get(filename) != null; 098 return includes.get(filename); 099 } 100 } 101 102 private static String readFile(final String filename) throws IOException { 103 final StringBuilder chain = new StringBuilder(); 104 final char[] buffer = new char[4096]; 105 Reader in = null; 106 try { 107 in = new FileReader(filename); 108 for (int bytesRead; (bytesRead = in.read(buffer)) != -1;) { 109 chain.append(buffer, 0, bytesRead); 110 } 111 } finally { 112 try { in.close(); } catch (final Exception e) { /* ignore */ } 113 } 114 return chain.toString(); 115 } 116 117 public void process(final Reader in, final Writer out) throws IOException { 118 new Processor(in, out); 119 } 120 121 private class Processor { 122 123 private final Reader in; 124 private final Writer out; 125 private int c; 126 private StringBuilder currentDirective = new StringBuilder(); 127 private StringBuilder currentDirectiveArgs = new StringBuilder(); 128 129 private Processor(final Reader inStream, final Writer outStream) throws IOException { 130 in = inStream instanceof BufferedReader ? (BufferedReader) inStream : new BufferedReader(inStream); 131 out = outStream instanceof PrintWriter ? (PrintWriter) outStream : new PrintWriter(outStream); 132 run(); 133 out.flush(); 134 } 135 136 private void run() throws IOException { 137 State state = State.NORMAL; 138 while ((c = in.read()) != -1) { 139 switch (state) { 140 case NORMAL: 141 switch (c) { 142 case '#': state = DIRECTIVE; break; 143 case '/': state = MAYBE_COMMENT; break; 144 case '"': out.write(c); state = STRING; break; 145 case '\'': out.write(c); state = CHAR; break; 146 default: out.write(c); 147 } break; 148 case DIRECTIVE: 149 switch (c) { 150 case '\\': state = DIRECTIVE_ESCAPE; break; 151 case '\n': state = NORMAL; processDirective(); 152 } break; 153 case DIRECTIVE_ESCAPE: 154 state = DIRECTIVE; break; 155 case MAYBE_COMMENT: 156 switch (c) { 157 case '*': if (!stripComments) { out.write('/'); out.write(c); } state = MULTILINE_COMMENT; break; 158 case '/': if (!stripComments) { out.write('/'); out.write(c); } state = EOL_COMMENT; break; 159 default: out.write('/'); out.write(c); 160 } break; 161 case MULTILINE_COMMENT: 162 switch (c) { 163 case '*': if (!stripComments) { out.write(c); } state = MAYBE_ENDOF_COMMENT; break; 164 default: if (!stripComments) { out.write(c); } 165 } break; 166 case EOL_COMMENT: 167 switch (c) { 168 case '\n': out.write(c); state = NORMAL; break; 169 default: if (!stripComments) { out.write(c); } 170 } break; 171 case MAYBE_ENDOF_COMMENT: 172 switch (c) { 173 case '/': if (!stripComments) { out.write(c); } state = NORMAL; break; 174 case '*': if (!stripComments) { out.write(c); } break; 175 default: if (!stripComments) { out.write(c); } state = MULTILINE_COMMENT; break; 176 } break; 177 case STRING: 178 switch (c) { 179 case '\\': state = STRING_ESCAPE; break; 180 case '"': state = NORMAL; break; 181 } break; 182 case STRING_ESCAPE: 183 state = STRING; break; 184 case CHAR: 185 switch (c) { 186 case '\\': state = CHAR_ESCAPE; break; 187 case '\'': state = NORMAL; break; 188 } break; 189 case CHAR_ESCAPE: 190 state = CHAR; break; 191 } 192 } 193 } 194 195 private void processDirective() { 196 } 197 198 } // class Processor 199 200 /** Main program. 201 * @param args command line arguments 202 */ 203 public static void main(final String... args) throws IOException { 204 new CPreProcessor().process(System.in, System.out); 205 } 206 207 public void process(final InputStream in, final OutputStream out) throws IOException { 208 process(new InputStreamReader(in), new OutputStreamWriter(out)); 209 } 210 211 } // class CPreProcessor