001 /* JAPI - (Yet anothr (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:Christian.Hujer@itcqis.com">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 */
077 public void addIncludePath(final File file) {
078 if (!file.isDirectory()) {
079 throw new IllegalArgumentException("Only directories are valid for include paths.");
080 }
081 includePaths.add(file);
082 }
083
084 /** Get a list with all include paths.
085 * @return list with all include paths
086 */
087 public List<File> getIncludePaths() {
088 return unmodifiableList(includePaths);
089 }
090
091 private String include(final String filename) throws IOException {
092 synchronized (syncLock) {
093 if (!includes.containsKey(filename)) {
094 includes.put(filename, readFile(filename));
095 }
096 assert includes.get(filename) != null;
097 return includes.get(filename);
098 }
099 }
100
101 private static String readFile(final String filename) throws IOException {
102 final StringBuilder chain = new StringBuilder();
103 final char[] buffer = new char[4096];
104 Reader in = null;
105 try {
106 in = new FileReader(filename);
107 for (int bytesRead; (bytesRead = in.read(buffer)) != -1;) {
108 chain.append(buffer, 0, bytesRead);
109 }
110 } finally {
111 try { in.close(); } catch (final Exception e) { /* ignore */ }
112 }
113 return chain.toString();
114 }
115
116 public void process(final Reader in, final Writer out) throws IOException {
117 new Processor(in, out);
118 }
119
120 private class Processor {
121
122 private final Reader in;
123 private final Writer out;
124 private int c;
125 private StringBuilder currentDirective = new StringBuilder();
126 private StringBuilder currentDirectiveArgs = new StringBuilder();
127
128 private Processor(final Reader inStream, final Writer outStream) throws IOException {
129 in = inStream instanceof BufferedReader ? (BufferedReader) inStream : new BufferedReader(inStream);
130 out = outStream instanceof PrintWriter ? (PrintWriter) outStream : new PrintWriter(outStream);
131 run();
132 out.flush();
133 }
134
135 private void run() throws IOException {
136 State state = State.NORMAL;
137 while ((c = in.read()) != -1) {
138 switch (state) {
139 case NORMAL:
140 switch (c) {
141 case '#': state = DIRECTIVE; break;
142 case '/': state = MAYBE_COMMENT; break;
143 case '"': out.write(c); state = STRING; break;
144 case '\'': out.write(c); state = CHAR; break;
145 default: out.write(c);
146 } break;
147 case DIRECTIVE:
148 switch (c) {
149 case '\\': state = DIRECTIVE_ESCAPE; break;
150 case '\n': state = NORMAL; processDirective();
151 } break;
152 case DIRECTIVE_ESCAPE:
153 state = DIRECTIVE; break;
154 case MAYBE_COMMENT:
155 switch (c) {
156 case '*': if (!stripComments) { out.write('/'); out.write(c); } state = MULTILINE_COMMENT; break;
157 case '/': if (!stripComments) { out.write('/'); out.write(c); } state = EOL_COMMENT; break;
158 default: out.write('/'); out.write(c);
159 } break;
160 case MULTILINE_COMMENT:
161 switch (c) {
162 case '*': if (!stripComments) { out.write(c); } state = MAYBE_ENDOF_COMMENT; break;
163 default: if (!stripComments) { out.write(c); }
164 } break;
165 case EOL_COMMENT:
166 switch (c) {
167 case '\n': out.write(c); state = NORMAL; break;
168 default: if (!stripComments) { out.write(c); }
169 } break;
170 case MAYBE_ENDOF_COMMENT:
171 switch (c) {
172 case '/': if (!stripComments) { out.write(c); } state = NORMAL; break;
173 case '*': if (!stripComments) { out.write(c); } break;
174 default: if (!stripComments) { out.write(c); } state = MULTILINE_COMMENT; break;
175 } break;
176 case STRING:
177 switch (c) {
178 case '\\': state = STRING_ESCAPE; break;
179 case '"': state = NORMAL; break;
180 } break;
181 case STRING_ESCAPE:
182 state = STRING; break;
183 case CHAR:
184 switch (c) {
185 case '\\': state = CHAR_ESCAPE; break;
186 case '\'': state = NORMAL; break;
187 } break;
188 case CHAR_ESCAPE:
189 state = CHAR; break;
190 }
191 }
192 }
193
194 private void processDirective() {
195 }
196
197 } // class Processor
198
199 /** Main program.
200 * @param args command line arguments
201 */
202 public static void main(final String... args) throws IOException {
203 new CPreProcessor().process(System.in, System.out);
204 }
205
206 public void process(final InputStream in, final OutputStream out) throws IOException {
207 process(new InputStreamReader(in), new OutputStreamWriter(out));
208 }
209
210 } // class CPreProcessor