1 // Copyright (c) 2009 Giovanni Lagorio (lagorio@disi.unige.it)
2 //
3 // This file is part of the source of CoCoALib, the CoCoA Library.
4 //
5 // CoCoALib is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // CoCoALib is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with CoCoALib. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "CoCoA/error.H"
19 #include "CoCoA/GlobalManager.H"
20 //#include "CoCoA/library.H" // already included by AST->Parser->Interpreter
21
22
23 #include "globals.H"
24 #include "Interpreter.H"
25 #include "CompilationDate.H"
26 #include "Banner.H"
27 #include "OnlineHelp.H"
28
29 #include <boost/filesystem.hpp>
30 #include <boost/scope_exit.hpp>
31 #include <cstdlib>
32 #include <fstream>
33 #include <iostream>
34 #include <sys/types.h>
35 #include <dirent.h>
36
37 using namespace std;
38 using namespace boost;
39 using namespace CoCoA;
40 using namespace CoCoA::LexerNS;
41 using namespace CoCoA::ParserNS;
42 using namespace CoCoA::InterpreterNS;
43 using namespace CoCoA::AST;
44
45
46 namespace {
47
banner(const string & message)48 void banner(const string &message) {
49 cout <<
50 "\n\n"
51 "##########################################################################\n"
52 << message << '\n' <<
53 "##########################################################################\n"
54 << endl;
55 }
56
printUsage(const char * const progname)57 void printUsage(const char * const progname) {
58 cout << "Usage:\n" << progname << " [<option> ... <option>] [<filename> ... <filename>]\n"
59 "Options:\n"
60 "-w0, -w1, -w2, -w3 Sets the warning level: -w0 none, -w1 low, -w2 normal (the default), -w3 pedantic\n"
61 "-dwc5 Don't warn about features that may be removed in the final CoCoA 5\n"
62 "-help Prints this help\n"
63 "--fullCoCoALibError Enables a verbose error reporting for problems involving CoCoALib\n"
64 "--do-nothing exit immediately (may not be used with other options)\n"
65 "--interactive Starts an interactive session after the input files have been processed\n"
66 "--no-preamble No initial banner and loading info\n"
67 "--no-readline Do not use readline (and its history mechanism)\n"
68 "--packageDir <dir> Sets the package directory (default: \"packages\")\n"
69 "--prompt <prompt> Sets the prompt suffix (default: \"#\")\n"
70 "--enable-system-cmd Enable use of the \"system\" command to run arbitrary shell commands\n"
71 "--stacksize N Sets the stacksize to N (must be >= 100)\n"
72 "--status-log FILE Output status log messages to FILE\n"
73 "\nIf no input filenames are given, then an interactive session is started\n";
74 }
75
76 const string cocoaExtension(".cpkg5");
77 const string::size_type cocoaExtLen = cocoaExtension.length();
78
findPackages(const string & pkgDir,const int RootLen,vector<string> & packageList,std::ostream & StrOut)79 int findPackages(const string &pkgDir, const int RootLen, vector<string> &packageList, std::ostream& StrOut) {
80 DIR *dir = opendir(pkgDir.c_str());
81 if (!dir) {
82 perror(pkgDir.c_str());
83 return EXIT_FAILURE;
84 }
85 while (struct dirent *entry = readdir(dir))
86 {
87 if (*entry->d_name=='.')
88 continue;
89 const string filename(entry->d_name);
90 const string::size_type fLen = filename.length();
91 const string fullname(pkgDir+"/"+filename);
92 struct stat buf;
93 if (stat(fullname.c_str(), &buf))
94 {
95 perror(fullname.c_str());
96 return EXIT_FAILURE;
97 }
98 if (S_ISDIR(buf.st_mode))
99 {
100 if (filename == "CVS") continue;
101 if (findPackages(fullname, RootLen, packageList, StrOut))
102 return EXIT_FAILURE;
103 continue;
104 }
105 if (fLen<=cocoaExtLen || filename.substr(fLen-cocoaExtLen, cocoaExtLen)!=cocoaExtension)
106 {
107 if (//printPreamble &&
108 fullname!=string(OnlineHelp::CoCoAManFileName()) &&
109 fullname.substr(RootLen, fullname.length()-1) != "init.cocoa5")
110 StrOut << "Ignoring file : "
111 << fullname.substr(RootLen, fullname.length()-1) << endl;
112 continue;
113 }
114 packageList.push_back(fullname);
115 }
116 closedir(dir);
117 return EXIT_SUCCESS;
118 }
119
findInitFile(const string & pkgDir,vector<string> & packageList)120 int findInitFile(const string &pkgDir, vector<string> &packageList) {
121 DIR *dir = opendir(pkgDir.c_str());
122 if (!dir) {
123 perror(pkgDir.c_str());
124 return EXIT_FAILURE;
125 }
126 const string fullname(pkgDir+"/init.cocoa5");
127 struct stat buf;
128 if (stat(fullname.c_str(), &buf)) {
129 perror(fullname.c_str());
130 return EXIT_FAILURE;
131 }
132 packageList.push_back(fullname);
133 closedir(dir);
134 return EXIT_SUCCESS;
135 }
136
137
138 intrusive_ptr<Interpreter> interpreter;
139
140 // #if !defined(_WINDOWS)
141 // extern "C"
142 // #endif
143 // void signalHandler(int)
144 // {
145 // interpreter->controlC = true;
146 // }
147
148 }
149
150
151 const string DefaultPackageDir1 = "./packages";
152 const string DefaultPackageDir2 = "../packages";
153 string packageDir = DefaultPackageDir1;
154
155 const string INIT_NOT_FOUND("!!!!! WARNING: CoCoA init file not found !!!!!");
156 const string PACKAGES_NOT_FOUND("!!!!! WARNING: Standard CoCoA packages not found !!!!!");
157 const string PACKAGE_AUTOLOAD_LOADING("Loading package: ");
158 const string PACKAGE_AUTOLOAD_FAILURE_MESSAGE("\n==========================================\n>>> Package auto-loading has failed!!! <<<\n>>> The system may respond erratically <<<\n==========================================\n\n");
159 const string PACKAGE_AUTOLOAD_SKIPPING_PKG_DUE_TO_FAILURE("Due to failure, skipping auto-load of package: ");
160
main(int argc,char ** argv)161 int main(int argc, char **argv)
162 {
163 // Call below to sync_with_stdio allows automatic prompt suppression if we compile with g++ [disabled because actual behaviour is platform dependent]
164 // cin.sync_with_stdio(false); // see GetlineLineProvider::doReadNextLine in LineProviders.C
165 GlobalManager CoCoAFoundations;
166 CoCoA::SignalWatcher MonitorSIGINT(SIGINT);
167 CoCoA::SignalWatcher MonitorSIGABRT(SIGABRT);
168 CoCoA::SignalWatcher MonitorSIGTERM(SIGTERM);
169 long MaxStackSize = 5000; // default max stack size
170 bool warnAboutCocoa5 = true;
171 bool interactive = false;
172 bool UseReadline = true; // ignored if CoCoA_WITH_READLINE is not set
173 bool fullCoCoALibError = false;
174 bool printPreamble = true;
175 WarningSeverity warningLevel = WS_NORMAL;
176 int paramsIndex = 1;
177 bool IsDefaultPackageDir = true;
178 ostringstream PkgOut; // loaded pkg at start: what should we do with this???
179 while (paramsIndex<argc) {
180 string arg(argv[paramsIndex]);
181 if (arg[0]!='-')
182 break;
183 ++paramsIndex;
184 if (arg=="--do-nothing")
185 {
186 if (argc==2 && paramsIndex==2) return EXIT_SUCCESS;
187 cout << "ERROR: --do-nothing must be sole option\n";
188 return EXIT_FAILURE;
189 }
190 if (arg=="--packageDir") {
191 IsDefaultPackageDir =false;
192 if (paramsIndex==argc) {
193 cout << "Missing --packageDir argument\n";
194 return EXIT_FAILURE;
195 }
196 packageDir = argv[paramsIndex++];
197 continue;
198 }
199 if (arg=="--enable-system-cmd")
200 {
201 SystemCommandPermit::EnableCommand();
202 continue;
203 }
204 if (arg=="--stacksize") // "Quick fix" soln to the problem of limited stack size
205 {
206 // OBSOLESCENT -- because did not work!!
207 if (paramsIndex==argc) {
208 cout << "Missing --stacksize argument (size in kilobytes)\n";
209 return EXIT_FAILURE;
210 }
211 MaxStackSize = atol(argv[paramsIndex++]); // better to use istringstream???
212 if (MaxStackSize < 100) MaxStackSize = 100;
213 continue;
214 }
215 if (arg=="--status-log") // waiting/running status for GUI
216 {
217 if (GlobalStatusLogStream.is_open()) { cout << "--status-log may be specified only once\n"; return EXIT_FAILURE; }
218 if (paramsIndex==argc)
219 { cout << "Missing --status-log argument (full path of status file)\n"; return EXIT_FAILURE; }
220 const string StatusFile = argv[paramsIndex++];
221 GlobalStatusLogStream.open(StatusFile);
222 if (!GlobalStatusLogStream) { cout << "status log file not writable\n"; return EXIT_FAILURE; }
223 continue;
224 }
225 if (arg=="--prompt") {
226 if (paramsIndex==argc) {
227 cout << "Missing --prompt argument\n";
228 return EXIT_FAILURE;
229 }
230 InteractiveLineProvider::promptSuffix = argv[paramsIndex++];
231 continue;
232 }
233 if (arg=="--interactive") {
234 interactive = true;
235 continue;
236 }
237 if (arg=="--no-readline") {
238 UseReadline = false;
239 continue;
240 }
241 if (arg=="--fullCoCoALibError") {
242 fullCoCoALibError = true;
243 continue;
244 }
245 if (arg=="--no-preamble") {
246 printPreamble = false;
247 continue;
248 }
249 if (arg=="-w0") {
250 warningLevel = WS_NONE;
251 continue;
252 }
253 if (arg=="-w1") {
254 warningLevel = WS_LOW;
255 continue;
256 }
257 if (arg=="-w2") {
258 warningLevel = WS_NORMAL;
259 continue;
260 }
261 if (arg=="-w3") {
262 warningLevel = WS_PEDANTIC;
263 continue;
264 }
265 if (arg=="-dwc5") {
266 warnAboutCocoa5 = false;
267 continue;
268 }
269 printUsage(*argv);
270 if (arg=="-help" || arg=="--help" || arg=="-?" || arg=="--?")
271 return EXIT_SUCCESS;
272 cout << "\nUnknown option: " << arg << endl;
273 return EXIT_FAILURE;
274 }
275
276 // Load the (standard) CoCoA packages...
277 vector<string> packageList;
278
279 using boost::filesystem::path;
280 using boost::filesystem::exists;
281 if (IsDefaultPackageDir)
282 {
283 if (!is_directory(path(DefaultPackageDir1)))
284 packageDir = DefaultPackageDir2;
285 else
286 packageDir = DefaultPackageDir1;
287 }
288
289 if (IsDefaultPackageDir && !(exists(path(packageDir)) && is_directory(path(packageDir))))
290 {
291 string LINE; LINE.resize(PACKAGES_NOT_FOUND.size(), '!');
292 cout << "*** Cannot access default package directory: " << path(packageDir) << " ***" << endl
293 << endl
294 << LINE << endl
295 << PACKAGES_NOT_FOUND << endl
296 << LINE << endl
297 << endl;
298 }
299 else
300 {
301 if (printPreamble) cout << "\nLoading CoCoA packages from directory:";
302 if (printPreamble) cout << "\n " << packageDir << endl;
303 ///// provare anche ..
304 if (findPackages(packageDir, packageDir.length()+1, packageList, PkgOut))
305 return EXIT_FAILURE;
306 findInitFile(packageDir, packageList); // no error if file is missing
307 // if (printPreamble) cout << "...done" << endl;
308 }
309 #ifdef C5IDE
310 (void)(interactive); // to avoid compiler warning about unused parameter
311 return IDE::launchTheIDE(warningLevel, warnAboutCocoa5, packageList, fullCoCoALibError);
312 #else // #ifdef C5IDE
313 try {
314 intrusive_ptr<CppOSTREAM> output(new CppOSTREAM(cout));
315 intrusive_ptr<CppISTREAM> input(new CppISTREAM(cin));
316 intrusive_ptr<ErrorReporter> errorReporter(new DefaultErrorReporter(warningLevel, output));
317 #ifdef CoCoA_WITH_READLINE
318 intrusive_ptr<LineProvider> lineProvider;
319 if (UseReadline)
320 lineProvider = new ReadlineLineProvider();
321 else
322 lineProvider = new GetlineLineProvider();
323 #else
324 (void)(UseReadline); // to suppress compiler warning about unused variable
325 intrusive_ptr<LineProvider> lineProvider(new GetlineLineProvider());
326 #endif
327 interpreter = new Interpreter(warnAboutCocoa5, lineProvider, errorReporter, output, /*input,*/ fullCoCoALibError, MaxStackSize);
328 BOOST_SCOPE_EXIT( (&interpreter) ) // needs at least 1 arg!!
329 {
330 interpreter = nullptr;
331 } BOOST_SCOPE_EXIT_END
332 bool loadOk = true;
333 for (const string &fullname: packageList)
334 {
335 if (loadOk)
336 {
337 // cout << PACKAGE_AUTOLOAD_LOADING << fullname << endl;
338 // if (printPreamble)
339 PkgOut << PACKAGE_AUTOLOAD_LOADING << fullname.substr(packageDir.length()+1, fullname.length()-1) << endl;
340 interpreter->errorReporter->resetErrorCounts();
341 interpreter->readAndExecute(fullname, true, true);
342 if (interpreter->errorReporter->getErrorCount())
343 {
344 loadOk = false;
345 // JAA 2015-07-29 cout << PACKAGE_AUTOLOAD_FAILURE_MESSAGE << endl;
346 }
347 }
348 // JAA 2015-07-29 else
349 // JAA 2015-07-29 cout << PACKAGE_AUTOLOAD_SKIPPING_PKG_DUE_TO_FAILURE << fullname << endl;
350 }
351 // if (InitFileNotFound)
352 // cout << INIT_NOT_FOUND << endl;
353 if (paramsIndex==argc) {
354 // No filenames given ==> starting interactive session
355 PkgOut << "Starting interactive session\n";
356 if (printPreamble) cout << CoCoA5Banner() << endl;
357 if (!loadOk) cout << PACKAGE_AUTOLOAD_FAILURE_MESSAGE;
358 return interpreter->run();
359 }
360 while (paramsIndex<argc) {
361 const string filename(argv[paramsIndex++]);
362 if (printPreamble)
363 banner("Executing file " + filename);
364 interpreter->readAndExecute(filename, true, true);
365 if (errorReporter->getErrorCount())
366 return EXIT_FAILURE;
367 }
368 if (interactive)
369 return interpreter->run();
370 } catch (const Ciao &) {
371 /* nop */
372 } catch (const InterruptException &) {
373 return EXIT_FAILURE;
374 } catch (const BaseException &e) {
375 cout << e.reason << '\n';
376 return EXIT_FAILURE;
377 } catch (const ErrorInfo& err) {
378 cout << "***ERROR*** UNCAUGHT CoCoA error";
379 ANNOUNCE(cout, err);
380 return EXIT_FAILURE;
381 } catch (const std::exception &e) {
382 cout << "Fatal error: " << e.what() << '\n';
383 return EXIT_FAILURE;
384 } catch(...) {
385 cout << "***ERROR*** UNCAUGHT UNKNOWN EXCEPTION" << endl;
386 return EXIT_FAILURE;
387 }
388 return EXIT_SUCCESS;
389 #endif // #ifdef C5IDE
390 }
391
392