1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <IceUtil/Options.h>
6 #include <IceUtil/CtrlCHandler.h>
7 #include <IceUtil/Mutex.h>
8 #include <IceUtil/MutexPtrLock.h>
9 #include <IceUtil/ConsoleUtil.h>
10 #include <Slice/Preprocessor.h>
11 #include <Slice/FileTracker.h>
12 #include <Slice/Util.h>
13 #include <Gen.h>
14 #include <stdlib.h>
15 
16 using namespace std;
17 using namespace Slice;
18 using namespace IceUtilInternal;
19 
20 namespace
21 {
22 
23 IceUtil::Mutex* globalMutex = 0;
24 bool interrupted = false;
25 
26 class Init
27 {
28 public:
29 
30     Init()
31     {
32         globalMutex = new IceUtil::Mutex;
33     }
34 
35     ~Init()
36     {
37         delete globalMutex;
38         globalMutex = 0;
39     }
40 };
41 
42 Init init;
43 
44 }
45 
46 void
47 interruptedCallback(int /*signal*/)
48 {
49     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
50 
51     interrupted = true;
52 }
53 
54 void
55 usage(const string& n)
56 {
57     consoleErr << "Usage: " << n << " [options] slice-files...\n";
58     consoleErr <<
59         "Options:\n"
60         "-h, --help           Show this message.\n"
61         "-v, --version        Display the Ice version.\n"
62         "-DNAME               Define NAME as 1.\n"
63         "-DNAME=DEF           Define NAME as DEF.\n"
64         "-UNAME               Remove any definition for NAME.\n"
65         "-IDIR                Put DIR in the include file search path.\n"
66         "-E                   Print preprocessor output on stdout.\n"
67         "--output-dir DIR     Create files in the directory DIR.\n"
68         "-d, --debug          Print debug messages.\n"
69         "--hdr FILE           Use the contents of FILE as the header.\n"
70         "--ftr FILe           Use the contents of FILE as the footer.\n"
71         "--indexhdr FILE      Use the contents of FILE as the header of the index/toc page (default=--hdr).\n"
72         "--indexftr FILE      Use the contents of FILE as the footer of the index/toc page (default=--ftr).\n"
73         "--image-dir DIR      Directory containing images for style sheets.\n"
74         "--logo-url URL       Link to URL from logo image (requires --image-dir).\n"
75         "--search ACTION      Generate search box with specified ACTION.\n"
76         "--index NUM          Generate subindex if it has at least NUM entries (0 for no index, default=1).\n"
77         "--summary NUM        Print a warning if a summary sentence exceeds NUM characters.\n"
78         "--ice                Allow reserved Ice prefix in Slice identifiers\n"
79         "                     deprecated: use instead [[\"ice-prefix\"]] metadata.\n"
80         "--underscore         Allow underscores in Slice identifiers\n"
81         "                     deprecated: use instead [[\"underscore\"]] metadata.\n"
82         ;
83 }
84 
85 int
86 compile(const vector<string>& argv)
87 {
88     IceUtilInternal::Options opts;
89     opts.addOpt("h", "help");
90     opts.addOpt("v", "version");
91     opts.addOpt("", "validate");
92     opts.addOpt("D", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
93     opts.addOpt("U", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
94     opts.addOpt("I", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
95     opts.addOpt("E");
96     opts.addOpt("", "output-dir", IceUtilInternal::Options::NeedArg, ".");
97     opts.addOpt("", "hdr", IceUtilInternal::Options::NeedArg);
98     opts.addOpt("", "ftr", IceUtilInternal::Options::NeedArg);
99     opts.addOpt("", "indexhdr", IceUtilInternal::Options::NeedArg);
100     opts.addOpt("", "indexftr", IceUtilInternal::Options::NeedArg);
101     opts.addOpt("", "index", IceUtilInternal::Options::NeedArg, "1");
102     opts.addOpt("", "image-dir", IceUtilInternal::Options::NeedArg);
103     opts.addOpt("", "logo-url", IceUtilInternal::Options::NeedArg);
104     opts.addOpt("", "search", IceUtilInternal::Options::NeedArg);
105     opts.addOpt("", "summary", IceUtilInternal::Options::NeedArg, "0");
106     opts.addOpt("d", "debug");
107     opts.addOpt("", "ice");
108     opts.addOpt("", "underscore");
109 
110     bool validate = find(argv.begin(), argv.end(), "--validate") != argv.end();
111     vector<string> args;
112     try
113     {
114         args = opts.parse(argv);
115     }
116     catch(const IceUtilInternal::BadOptException& e)
117     {
118         consoleErr << argv[0] << ": error: " << e.reason << endl;
119         if(!validate)
120         {
121             usage(argv[0]);
122         }
123         return EXIT_FAILURE;
124     }
125 
126     if(opts.isSet("help"))
127     {
128         usage(argv[0]);
129         return EXIT_SUCCESS;
130     }
131 
132     if(opts.isSet("version"))
133     {
134         consoleErr << ICE_STRING_VERSION << endl;
135         return EXIT_SUCCESS;
136     }
137 
138     vector<string> cppArgs;
139     vector<string> optargs = opts.argVec("D");
140     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
141     {
142         cppArgs.push_back("-D" + *i);
143     }
144 
145     optargs = opts.argVec("U");
146     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
147     {
148         cppArgs.push_back("-U" + *i);
149     }
150 
151     optargs = opts.argVec("I");
152     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
153     {
154         cppArgs.push_back("-I" + Preprocessor::normalizeIncludePath(*i));
155     }
156 
157     bool preprocess = opts.isSet("E");
158 
159     string output = opts.optArg("output-dir");
160 
161     string header = opts.optArg("hdr");
162 
163     string footer = opts.optArg("ftr");
164 
165     string indexHeader = opts.optArg("indexhdr");
166 
167     string indexFooter = opts.optArg("indexftr");
168 
169     string ind = opts.optArg("index");
170     unsigned indexCount = 0;
171     if(!ind.empty())
172     {
173         istringstream s(ind);
174         s >>  indexCount;
175         if(!s)
176         {
177             consoleErr << argv[0] << ": error: the --index operation requires a positive integer argument"
178                        << endl;
179             if(!validate)
180             {
181                 usage(argv[0]);
182             }
183             return EXIT_FAILURE;
184         }
185     }
186 
187     string imageDir = opts.optArg("image-dir");
188 
189     string logoURL = opts.optArg("logo-url");
190 
191     string searchAction = opts.optArg("search");
192 
193     string warnSummary = opts.optArg("summary");
194     unsigned summaryCount = 0;
195     if(!warnSummary.empty())
196     {
197         istringstream s(warnSummary);
198         s >>  summaryCount;
199         if(!s)
200         {
201             consoleErr << argv[0] << ": error: the --summary operation requires a positive integer argument"
202                        << endl;
203             if(!validate)
204             {
205                 usage(argv[0]);
206             }
207             return EXIT_FAILURE;
208         }
209     }
210 
211     bool debug = opts.isSet("debug");
212 
213     bool ice = opts.isSet("ice");
214 
215     bool underscore = opts.isSet("underscore");
216 
217     if(args.empty())
218     {
219         consoleErr << argv[0] << ": error: no input file" << endl;
220         if(!validate)
221         {
222             usage(argv[0]);
223         }
224         return EXIT_FAILURE;
225     }
226 
227     if(validate)
228     {
229         return EXIT_SUCCESS;
230     }
231 
232     UnitPtr p = Unit::createUnit(true, false, ice, underscore);
233 
234     int status = EXIT_SUCCESS;
235 
236     IceUtil::CtrlCHandler ctrlCHandler;
237     ctrlCHandler.setCallback(interruptedCallback);
238 
239     for(vector<string>::size_type idx = 0; idx < args.size(); ++idx)
240     {
241         PreprocessorPtr icecpp = Preprocessor::create(argv[0], args[idx], cppArgs);
242         FILE* cppHandle = icecpp->preprocess(true, "-D__SLICE2HTML__");
243 
244         if(cppHandle == 0)
245         {
246             p->destroy();
247             return EXIT_FAILURE;
248         }
249         if(preprocess)
250         {
251             char buf[4096];
252             while(fgets(buf, static_cast<int>(sizeof(buf)), cppHandle) != ICE_NULLPTR)
253             {
254                 if(fputs(buf, stdout) == EOF)
255                 {
256                     p->destroy();
257                     return EXIT_FAILURE;
258                 }
259             }
260         }
261         else
262         {
263             status = p->parse(args[idx], cppHandle, debug);
264         }
265 
266         if(!icecpp->close())
267         {
268             p->destroy();
269             return EXIT_FAILURE;
270         }
271 
272         {
273             IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
274 
275             if(interrupted)
276             {
277                 return EXIT_FAILURE;
278             }
279         }
280     }
281 
282     if(status == EXIT_SUCCESS && !preprocess)
283     {
284         try
285         {
286             Slice::generate(p, output, header, footer, indexHeader, indexFooter, imageDir, logoURL,
287                             searchAction, indexCount, summaryCount);
288         }
289         catch(const Slice::FileException& ex)
290         {
291             // If a file could not be created, then cleanup any
292             // created files.
293             FileTracker::instance()->cleanup();
294             p->destroy();
295             consoleErr << argv[0] << ": error: " << ex.reason() << endl;
296             return EXIT_FAILURE;
297         }
298         catch(...)
299         {
300             FileTracker::instance()->cleanup();
301             consoleErr << args[0] << ": error:" << "unknown exception" << endl;
302             return EXIT_FAILURE;
303         }
304     }
305 
306     p->destroy();
307 
308     {
309         IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
310 
311         if(interrupted)
312         {
313             FileTracker::instance()->cleanup();
314             return EXIT_FAILURE;
315         }
316     }
317 
318     return status;
319 }
320 
321 #ifdef _WIN32
322 int wmain(int argc, wchar_t* argv[])
323 #else
324 int main(int argc, char* argv[])
325 #endif
326 {
327     vector<string> args = argvToArgs(argc, argv);
328     try
329     {
330         return compile(args);
331     }
332     catch(const std::exception& ex)
333     {
334         consoleErr << args[0] << ": error:" << ex.what() << endl;
335         return EXIT_FAILURE;
336     }
337     catch(...)
338     {
339         consoleErr << args[0] << ": error:" << "unknown exception" << endl;
340         return EXIT_FAILURE;
341     }
342 }
343