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