1 /*
2 * Copyright (c) 2020, 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 #include "kludge_c++11.h"
27
28 #include <fstream>
29 #include <algorithm>
30 #include "CfgFile.h"
31 #include "Log.h"
32 #include "Toolbox.h"
33 #include "FileUtils.h"
34 #include "ErrorHandling.h"
35
36
getProperties(const SectionName & sectionName) const37 const CfgFile::Properties& CfgFile::getProperties(
38 const SectionName& sectionName) const {
39 const PropertyMap::const_iterator entry = data.find(sectionName);
40 if (entry != data.end()) {
41 return entry->second;
42 }
43 return empty;
44 }
45
46
setPropertyValue(const SectionName & sectionName,const PropertyName & name,const tstring_array & value)47 CfgFile& CfgFile::setPropertyValue(const SectionName& sectionName,
48 const PropertyName& name, const tstring_array& value) {
49 PropertyMap::iterator entry = data.find(sectionName);
50 if (entry != data.end()) {
51 entry->second[name] = value;
52 } else {
53 Properties props;
54 props[name] = value;
55 data[sectionName] = props;
56 }
57 return *this;
58 }
59
60
61 namespace {
62
expandMacros(const tstring & str,const CfgFile::Macros & macros)63 tstring expandMacros(const tstring& str, const CfgFile::Macros& macros) {
64 tstring reply = str;
65 CfgFile::Macros::const_iterator it = macros.begin();
66 const CfgFile::Macros::const_iterator end = macros.end();
67 for (; it != end; ++it) {
68 reply = tstrings::replace(reply, it->first, it->second);
69 }
70 return reply;
71 }
72
73 } // namespace
74
expandMacros(const Macros & macros) const75 CfgFile CfgFile::expandMacros(const Macros& macros) const {
76 CfgFile copyCfgFile = *this;
77
78 PropertyMap::iterator mapIt = copyCfgFile.data.begin();
79 const PropertyMap::iterator mapEnd = copyCfgFile.data.end();
80 for (; mapIt != mapEnd; ++mapIt) {
81 Properties::iterator propertyIt = mapIt->second.begin();
82 const Properties::iterator propertyEnd = mapIt->second.end();
83 for (; propertyIt != propertyEnd; ++propertyIt) {
84 tstring_array::iterator strIt = propertyIt->second.begin();
85 const tstring_array::iterator strEnd = propertyIt->second.end();
86 for (; strIt != strEnd; ++strIt) {
87 tstring newValue;
88 while ((newValue = ::expandMacros(*strIt, macros)) != *strIt) {
89 strIt->swap(newValue);
90 }
91 }
92 }
93 }
94
95 return copyCfgFile;
96 }
97
98
99 namespace {
100
101 const CfgFile::SectionName* getSectionName(const tstring& str);
102 const CfgFile::PropertyName* getPropertyName(const tstring& str);
103
104 const CfgFile::SectionName UnknownSection = CfgFile::SectionName(_T(""));
105
106
107 class PurgeSection {
108 public:
PurgeSection(CfgFile::SectionName & sectionName,CfgFile::Properties & sectionData,CfgFile::PropertyMap & cfgFileData)109 PurgeSection(CfgFile::SectionName& sectionName,
110 CfgFile::Properties& sectionData,
111 CfgFile::PropertyMap& cfgFileData):
112 sectionName(sectionName), sectionData(sectionData),
113 cfgFileData(cfgFileData) {
114 }
115
operator ()()116 void operator ()() {
117 if (sectionName != UnknownSection && !sectionData.empty()) {
118 std::swap(cfgFileData[sectionName], sectionData);
119 sectionName = UnknownSection;
120 sectionData.clear();
121 }
122 }
123
124 private:
125 CfgFile::SectionName& sectionName;
126 CfgFile::Properties& sectionData;
127 CfgFile::PropertyMap& cfgFileData;
128 };
129
130 class AddProperty {
131 public:
AddProperty(const CfgFile::SectionName & sectionName,CfgFile::Properties & sectionData)132 AddProperty(const CfgFile::SectionName& sectionName,
133 CfgFile::Properties& sectionData): sectionName(sectionName),
134 sectionData(sectionData) {
135 }
136
operator ()(const tstring & name,const tstring & value)137 void operator ()(const tstring& name, const tstring& value) {
138 if (sectionName != UnknownSection) {
139 const CfgFile::PropertyName *known = getPropertyName(name);
140 if (known) {
141 sectionData[*known].push_back(value);
142 }
143 }
144 }
145
146 private:
147 const CfgFile::SectionName& sectionName;
148 CfgFile::Properties& sectionData;
149 };
150
151 } // namepsace
152
load(const tstring & path)153 CfgFile CfgFile::load(const tstring& path) {
154 std::ifstream input(path.c_str());
155 if (!input.good()) {
156 JP_THROW(tstrings::any() << "Error opening \"" << path << "\" file: "
157 << lastCRTError());
158 }
159
160 CfgFile cfgFile;
161
162 SectionName sectionName = UnknownSection;
163 Properties sectionData;
164
165 PurgeSection purgeSection(sectionName, sectionData, cfgFile.data);
166
167 AddProperty addProperty(sectionName, sectionData);
168
169 std::string utf8line;
170 int lineno = 0;
171 while (std::getline(input, utf8line)) {
172 ++lineno;
173 const tstring line = tstrings::any(utf8line).tstr();
174
175 if (line.empty() || _T(';') == *line.begin()) {
176 // Empty line or comment, ignore.
177 continue;
178 }
179
180 if (_T('[') == *line.begin()) {
181 const size_t endIdx = line.find_last_of(_T(']'));
182 if (endIdx == tstring::npos) {
183 JP_THROW(tstrings::any() << "Error parsing [" << path
184 << "] file at " << lineno << ": Missing ']' character");
185 }
186
187 purgeSection();
188
189 // Section begin.
190 const SectionName *knownName = getSectionName(line.substr(1, endIdx - 1));
191 if (knownName) {
192 sectionName = *knownName;
193 } else {
194 sectionName = UnknownSection;
195 }
196 continue;
197 }
198
199 size_t sepIdx = 0;
200 do {
201 sepIdx = line.find_first_of(_T('='), sepIdx);
202 if (sepIdx == tstring::npos) {
203 addProperty(line, tstring());
204 break;
205 }
206
207 if (sepIdx != 0 && line[sepIdx - 1] == '\\') {
208 sepIdx++;
209 continue;
210 }
211
212 addProperty(line.substr(0, sepIdx), line.substr(sepIdx + 1));
213 break;
214 } while (true);
215 }
216
217 if (!input.eof()) {
218 // Failed to process file up to the end.
219 JP_THROW(tstrings::any() << "Failed to read \"" << path
220 << "\" file up to the end: " << lastCRTError());
221 }
222
223 purgeSection();
224
225 return cfgFile;
226 }
227
228
join(const tstring_array & values,const tstring::value_type delimiter)229 tstring join(const tstring_array& values, const tstring::value_type delimiter) {
230 return tstrings::join(values.begin(), values.end(), tstring(1, delimiter));
231 }
232
asString(Properties::const_reference property)233 tstring CfgFile::asString(Properties::const_reference property) {
234 return *property.second.rbegin();
235 }
236
asPathList(Properties::const_reference property)237 tstring CfgFile::asPathList(Properties::const_reference property) {
238 return join(property.second, FileUtils::pathSeparator);
239 }
240
241
242 #define JP_ALL_SECTIONS \
243 JP_SECTION(Application); \
244 JP_SECTION(JavaOptions); \
245 JP_SECTION(AppCDSJavaOptions); \
246 JP_SECTION(AppCDSGenerateCacheJavaOptions); \
247 JP_SECTION(ArgOptions);
248
249 namespace SectionName {
250 #define JP_SECTION(name) const CfgFile::SectionName name(_T(#name))
251 JP_ALL_SECTIONS
252 #undef JP_SECTION
253 } // namespace SectionName
254
255 namespace {
getSectionName(const tstring & str)256 const CfgFile::SectionName* getSectionName(const tstring& str) {
257 #define JP_SECTION(name) while (str == _T(#name)) { return &SectionName::name; }
258 JP_ALL_SECTIONS
259 #undef JP_SECTION
260 return 0;
261 }
262 }
263
264 #undef JP_ALL_SECTIONS
265
266
267 #define JP_ALL_PROPERTIES \
268 JP_PROPERTY(version, "app.version"); \
269 JP_PROPERTY(mainjar, "app.mainjar"); \
270 JP_PROPERTY(mainmodule, "app.mainmodule"); \
271 JP_PROPERTY(mainclass, "app.mainclass"); \
272 JP_PROPERTY(classpath, "app.classpath"); \
273 JP_PROPERTY(modulepath, "app.modulepath"); \
274 JP_PROPERTY(runtime, "app.runtime"); \
275 JP_PROPERTY(splash, "app.splash"); \
276 JP_PROPERTY(memory, "app.memory"); \
277 JP_PROPERTY(arguments, "arguments"); \
278 JP_PROPERTY(javaOptions, "java-options"); \
279
280 namespace PropertyName {
281 #define JP_PROPERTY(varName, name) const CfgFile::PropertyName varName(_T(name))
282 JP_ALL_PROPERTIES
283 #undef JP_PROPERTY
284 } // namespace PropertyName
285
286 namespace {
getPropertyName(const tstring & str)287 const CfgFile::PropertyName* getPropertyName(const tstring& str) {
288 #define JP_PROPERTY(varName, name) while (str == _T(name)) { return &PropertyName::varName; }
289 JP_ALL_PROPERTIES
290 #undef JP_PROPERTY
291 return 0;
292 }
293 } // namespace
294
295 #undef JP_ALL_PROPERTIES
296