1    	package acl2s.lib.session;
2    	
3    	import java.io.BufferedReader;
4    	import java.io.File;
5    	import java.io.FileReader;
6    	import java.io.IOException;
7    	import java.net.URISyntaxException;
8    	import java.net.URL;
9    	import java.util.ArrayList;
10   	import java.util.Arrays;
11   	import java.util.HashSet;
12   	import java.util.Iterator;
13   	import java.util.LinkedList;
14   	import java.util.List;
15   	import java.util.Map;
16   	
17   	import org.peterd.util.Misc;
18   	
19   	import acl2s.lib.parse.obj.Sym;
20   	import acl2s.lib_driver.IAcl2sLibErrorHandler;
21   	import acl2s.lib_example.BasicInteractiveSessionConfig;
22   	
23   	public class MyBaseConfig implements IBaseConfig {
24   		private static final long serialVersionUID = 2L;
25   	
26   		public MyBaseConfig(IAcl2sLibErrorHandler err, File hooksHome, File acl2Home, File[] modeHomes) {
27   			this.err = err;
28   			this.hooksHome = hooksHome;
29   			this.acl2Home = acl2Home;
30   			updateForNewHooksHome();
31   			updateForNewAcl2Home();
32   			addModeHomes(modeHomes);
33   		}
34   	
35   		protected IAcl2sLibErrorHandler err;
36   		protected File acl2Home;
37   		protected String htmlHomeUrl;
38   		protected String[] pathsToTry;
39   		
40   		protected File hooksHome;
41   		protected String hooksAcl2Path;
42   		
43   		protected ModeDir[] modeDirs = {}; // after update, dependency-respecting sequence 
44   		protected SessionMode[] modes = { SessionMode.compatible }; // kept as sequence of all known modes
45   		protected String modeAddIncludeBooks = "";
46   		
47   		public void setHooksHome(File f) {
48   			if (!Misc.equal(f, hooksHome)) {
49   				hooksHome = f;
50   				updateForNewHooksHome();
51   			}
52   		}
53   	
54   		protected void updateForNewHooksHome() {
55   			if (hooksHome == null) {
56   				hooksAcl2Path = null;
57   			} else {
58   				hooksAcl2Path = toAcl2PathDir(hooksHome);
59   			}
60   		}
61   		
62   		public String getHooksAcl2Path() {
63   			return hooksAcl2Path;
64   		}
65   		
66   		public File getHooksHome() {
67   			return hooksHome;
68   		}
69   	
70   		
71   		public void setAcl2Home(File f) {
72   			if (!Misc.equal(f, acl2Home)) {
73   				acl2Home = f;
74   				updateForNewAcl2Home();
75   			}
76   		}
77   		
78   		protected void updateForNewAcl2Home() {
79   			htmlHomeUrl = findHtmlHome(acl2Home);
80   			pathsToTry = findPaths(acl2Home);
81   		}
82   		
83   		
84   		public void addModeHomes(File... modeHomes) {
85   			if (modeHomes == null || modeHomes.length == 0) return;
86   			
87   			LinkedList<ModeDir> phase1 = new LinkedList<ModeDir>();
88   			for (File modeHome : modeHomes) {
89   				ModeDir d = ModeDir.maybeCreate(err, modeHome);
90   				if (d != null) {
91   					phase1.add(d);
92   				}
93   			}
94   			if (phase1.size() == 0) return;
95   			phase1.addAll(Arrays.asList(modeDirs));
96   			updateModes(phase1);
97   		}
98   		
99   		public void removeModeHomes(File... removeHomes) {
100  			if (removeHomes == null || removeHomes.length == 0) return;
101  			ArrayList<ModeDir> toRemove = new ArrayList<ModeDir>();
102  			for (File home : removeHomes) {
103  				ModeDir d = lookupModeDir(home);
104  				if (d != null) {
105  					toRemove.add(d);
106  				}
107  			}
108  			removeModeDirs(toRemove);
109  		}
110  		
111  		public void removeModeDirs(ModeDir... removeDirs) {
112  			removeModeDirs(Arrays.asList(removeDirs));
113  		}
114  		
115  		public void removeModeDirs(List<ModeDir> removeDirs) {
116  			if (removeDirs == null || removeDirs.size() == 0) return;
117  			
118  			LinkedList<ModeDir> phase1 = new LinkedList<ModeDir>();
119  			phase1.addAll(Arrays.asList(modeDirs));
120  			if (!phase1.removeAll(removeDirs)) return;
121  			
122  			updateModes(phase1);
123  		}
124  		
125  		static Sym systemKeyword = Sym.createKeyword("SYSTEM");
126  		
127  		protected void updateModes(LinkedList<ModeDir> phase1) {
128  			// order to satisfy dependencies
129  			ArrayList<ModeDir> phase2 = new ArrayList<ModeDir>(phase1.size());
130  			boolean added;
131  			HashSet<Sym> dirSyms = new HashSet<Sym>(phase1.size());
132  			dirSyms.add(systemKeyword);
133  			do {
134  				added = false;
135  				Iterator<ModeDir> rem = phase1.iterator();
136  				while (rem.hasNext()) {
137  					ModeDir modeDir = rem.next();
138  					boolean sat = true;
139  					for (Sym dep : modeDir.deps) {
140  						if (!dirSyms.contains(dep) && !Misc.equal(dep, modeDir.includeBookDirSym)) {
141  							sat = false;
142  						}
143  					}
144  					if (sat) {
145  						phase2.add(modeDir);
146  						rem.remove();
147  						if (modeDir.includeBookDirSym != null) {
148  							dirSyms.add(modeDir.includeBookDirSym);
149  							added = true;;
150  						}
151  					}
152  				}
153  			} while (added);
154  			
155  			
156  			for (ModeDir bad : phase1) {
157  				err.error("Mode dir has unsatisfied dependencies: " + bad.home.getAbsolutePath(), null);
158  			}
159  	
160  			modeDirs = phase2.toArray(new ModeDir[phase2.size()]);
161  			
162  			ArrayList<SessionMode> allModes = new ArrayList<SessionMode>();
163  			allModes.add(SessionMode.compatible);
164  			StringBuilder addInclBook = new StringBuilder();
165  			for (ModeDir modeDir : modeDirs) {
166  				addInclBook.append(modeDir.getAddIncludeBookDirString());
167  				for (SessionMode mode : modeDir.modes) {
168  					allModes.add(mode);
169  				}
170  			}
171  			modes = allModes.toArray(new SessionMode[allModes.size()]);
172  			Arrays.sort(modes);  // by precedence
173  			modeAddIncludeBooks = addInclBook.toString();
174  		}
175  		
176  		
177  		public String getDocBaseURL() {
178  			return htmlHomeUrl;
179  		}
180  		
181  		public String[] getCmdLine() {
182  			String cmd = fallbackCmd;
183  			for (String path : pathsToTry) {
184  				File f = new File(path);
185  				if (f.exists()) {
186  					cmd = path;
187  					break;
188  				}
189  			}
190  			return new String[] { cmd };
191  		}
192  	
193  	
194  		public void setupEnvironment(Map<String, String> env) {
195  			// script should set ACL2_SYSTEM_BOOKS better than we can
196  			env.put("ACL2-CUSTOMIZATION", "NONE"); // we will load it if we want it
197  		}
198  	
199  		public String getACL2sDocURL() {
200  			return Versions.getAcl2sDocInternetURLString();
201  		}
202  	
203  		public SessionMode[] getModes() {
204  			return modes;
205  		}
206  	
207  		public SessionMode getDefaultMode() {
208  			return modes[0];
209  		}
210  	
211  		public ModeDir[] getModeDirs() {
212  			return modeDirs;
213  		}
214  		
215  		public String getModeAddIncludeBooks() {
216  			return modeAddIncludeBooks;
217  		}
218  		
219  		public SessionMode lookupMode(String name) {
220  			for (SessionMode m : modes) {
221  				if (m.getName().equalsIgnoreCase(name)) {
222  					return m;
223  				}
224  			}
225  			return null;
226  		}
227  	
228  		public ModeDir lookupModeDir(File home) {
229  			for (ModeDir modeDir : modeDirs) {
230  				if (modeDir.home.equals(home)) {
231  					return modeDir;
232  				}
233  			}
234  			return null;
235  		}
236  		
237  		public ModeDir lookupModeDir(Sym dir) {
238  			for (ModeDir modeDir : modeDirs) {
239  				if (modeDir.includeBookDirSym == dir) {
240  					return modeDir;
241  				}
242  			}
243  			return null;
244  		}
245  		
246  		// ********************** STATIC STUFF ********************** //
247  		
248  		public static File getACL2HomeFromEnvironment() {
249  			return acl2HomeFromEnvironment;
250  		}
251  		
252  		public static File getHooksHomeFromEnvironment() {
253  			return hooksHomeFromEnvironment;
254  		}
255  		
256  		static final File acl2HomeFromEnvironment = findAcl2HomeFromEnvironment();
257  		static final File hooksHomeFromEnvironment = findHooksHomeFromEnvironment();
258  		
259  		static File findAcl2HomeFromEnvironment() {
260  			String tmp = System.getenv("ACL2_HOME");
261  			if (tmp == null || ! new File(tmp).isDirectory()) {
262  				tmp = System.getProperty("ACL2_HOME");
263  			}
264  			if (tmp == null || ! new File(tmp).isDirectory()) {
265  				tmp = System.getProperty("acl2.home");
266  			}
267  			if (tmp == null || ! new File(tmp).isDirectory()) {
268  				return null;
269  			} else {
270  				return new File(tmp).getAbsoluteFile();
271  			}
272  		}
273  		
274  		static File findHooksHomeFromEnvironment() {
275  			String tmp;
276  			tmp = System.getenv("ACL2S_HOOKS");
277  			if (tmp != null) {
278  				File f = new File(tmp);
279  				if (f.isDirectory()) {
280  					return f;
281  				}
282  			}
283  			tmp = System.getProperty("ACL2S_HOOKS");
284  			if (tmp != null) {
285  				File f = new File(tmp);
286  				if (f.isDirectory()) {
287  					return f;
288  				}
289  			}
290  			tmp = System.getProperty("acl2s.hooks");
291  			if (tmp != null) {
292  				File f = new File(tmp);
293  				if (f.isDirectory()) {
294  					return f;
295  				}
296  			}
297  			try {
298  				URL u = BasicInteractiveSessionConfig.class.getResource("hooks/preinit.lsp");
299  				if (u != null) {
300  					File f = new File(u.toURI()).getParentFile();
301  					if (f.isDirectory()) {
302  						return f;
303  					}
304  				}
305  			} catch (IllegalArgumentException e) {
306  			} catch (URISyntaxException e) {
307  			}
308  			try {
309  				URL u = BasicInteractiveSessionConfig.class.getResource("../hooks/preinit.lsp");
310  				if (u != null) {
311  					File f = new File(u.toURI()).getParentFile();
312  					if (f.isDirectory()) {
313  						return f;
314  					}
315  				}
316  			} catch (IllegalArgumentException e) {
317  			} catch (URISyntaxException e) {
318  			}
319  			return null; // could not find
320  		}
321  		
322  		
323  		
324  		public static final String fallbackCmd = "acl2";
325  		
326  		
327  		
328  		public static String findHtmlHome(File acl2HomeOption) {
329  			String tmp = null;
330  			if (acl2HomeOption != null) {
331  				File f = new File(new File(acl2HomeOption, "doc"), "HTML");
332  				if (new File(f, "acl2-doc.html").exists()) {
333  					tmp = "file://" + toAcl2PathDir(f);
334  				}
335  			}
336  			if (tmp == null) {
337  				tmp = Versions.getAcl2DocInternetURLString();
338  			}
339  			return tmp;
340  		}
341  		
342  		//harshrc:: Acl2HomeOption can be 
343  		//1. ACL2 build path set in eclipse preferences
344  		//2. ACL2s image plugin path (bundle in eclipse terminology)
345  		//3. ACL2_HOME environment variable set in OS
346  		//4. null (in which case we try to execute 'acl2')
347  		public static String[] findPaths(File acl2HomeOption) {
348  			boolean windows = File.separatorChar == '\\';
349  			
350  			ArrayList<String> paths = new ArrayList<String>();
351  			
352  			if (acl2HomeOption != null) {
353  				String hpath = acl2HomeOption.getAbsolutePath();
354  				if (windows) {
355  					paths.add(hpath + File.separator + "run_acl2.exe");
356  					paths.add(hpath + File.separator + "saved_acl2.bat");
357  					paths.add(hpath + File.separator + "bin" + File.separator + "acl2.exe");
358  				} else {
359  					paths.add(hpath + File.separator + "run_acl2");
360  					paths.add(hpath + File.separator + "saved_acl2");
361  				}
362  			}
363  			
364  			if (windows) {
365  				String minVer = Versions.getAcl2MinVersionString();
366  				String maxVer = Versions.getAcl2MaxVersionString();
367  				
368  				paths.add("C:\\ACL2-" + maxVer + "\\bin\\acl2.exe");
369  				paths.add("C:\\Program Files\\ACL2 " + maxVer + "\\bin\\acl2.exe");
370  				if (! minVer.equals(maxVer)) {
371  					paths.add("C:\\ACL2-" + minVer + "\\bin\\acl2.exe");
372  					paths.add("C:\\Program Files\\ACL2 " + minVer + "\\bin\\acl2.exe");
373  				}
374  			}
375  			
376  			return paths.toArray(new String[paths.size()]);
377  		}
378  	
379  		/*
380  		static final Sym BOOK_DEV = Sym.createKeyword("BOOK-DEV");
381  		static final Sym TTAGS = Sym.createKeyword("TTAGS");
382  		static final Sym IMPORTS = Sym.createKeyword("IMPORTS");
383  		static final Sym PRECEDENCE = Sym.createKeyword("PRECEDENCE");
384  		
385  		public static SessionMode[] getModesFromHome(File root) {
386  			try {
387  				File[] modeFiles = root.listFiles();
388  				ArrayList<SessionMode> foundModes = new ArrayList<SessionMode>(modeFiles.length);
389  				for (File mf : modeFiles) {
390  					if (!mf.getName().toLowerCase().endsWith("mode.lsp")) continue;
391  					try {
392  						FileInputStream stream = new FileInputStream(mf);
393  						LispObject[] md = LispObject.parseAll(new StreamParser(BootstrapParseContext.instance, stream));
394  						stream.close();
395  						if (md.length < 4) throw new ParseException("Expecting at least four lisp objects in file " + mf);
396  						if (!(md[0] instanceof Str) ||
397  								!(md[1] instanceof Str) ||
398  								!(md[2] instanceof Str)) {
399  							throw new ParseException("First three objects must be strings in file " + mf);
400  						}
401  						String name = ((Str)md[0]).value;
402  						String sdesc = ((Str)md[1]).value;
403  						String ldesc = ((Str)md[2]).value;
404  	
405  						// option defaults
406  						boolean bookdev = true;
407  						boolean ttags = false;
408  						LispObject extraImports = Sym.nilInstance;
409  						int precedence = Integer.MAX_VALUE;
410  	
411  						// options given with keywords
412  						if (!(md[3].isNil())) {
413  							try {
414  								LispObject optLst = Cons.deQuote(md[3]);
415  								while (!optLst.isNil()) {
416  									Cons c = (Cons) optLst;
417  									Sym opt = (Sym) c.car;
418  									c = (Cons) c.cdr;
419  									LispObject val = c.car;
420  									optLst = c.cdr;
421  	
422  									if (opt == BOOK_DEV) {
423  										bookdev = Cons.maybeDeQuote(val).isNotNil();
424  									} else if (opt == TTAGS) {
425  										ttags = Cons.maybeDeQuote(val).isNotNil();
426  									} else if (opt == IMPORTS) {
427  										// ensure either (unquoted) nil or quoted list
428  										extraImports = Cons.maybeDeQuote(val);
429  	
430  										if (extraImports.isNotNil()) {
431  											extraImports = Cons.quote(extraImports);
432  										}
433  									} else if (opt == PRECEDENCE) {
434  										val = Cons.maybeDeQuote(val);
435  										if (val instanceof Int) {
436  											BigInteger precInt = ((Int) val).v;
437  											if (precInt.bitLength() <= 31) {
438  												precedence = precInt.intValue();
439  											}
440  										}
441  									} else {
442  										// unrecognized
443  									}
444  								}
445  							} catch (Exception e) {
446  								throw new ParseException("Malformed keyword options in fourth object of file " + mf);
447  							}
448  						}
449  						LispObject[] initData = new LispObject[md.length - 4];
450  						System.arraycopy(md, 4, initData, 0, initData.length);
451  						foundModes.add(new SessionMode(root, name, sdesc, ldesc, bookdev, ttags, extraImports, precedence, initData));
452  					} catch (ParseException pe) {
453  						System.err.println("Unable to parse mode in registry (" + mf + "): " + pe.toString());
454  					}
455  				}
456  				
457  				if (foundModes.size() > 0) {
458  					SessionMode[] modes = foundModes.toArray(new SessionMode[foundModes.size()]);
459  					Arrays.sort(modes);
460  					return modes;
461  				}
462  				// otherwise, fallback:
463  			} catch (IOException ioe) {
464  				System.err.println("While reading mode registry: " + ioe.toString());
465  			}
466  			return SessionMode.compatibleOnly;
467  		}*/
468  		
469  		public static File[] getCertifyOrder(File hooksHome) throws IOException {
Event modeled_event: Opening a resource with "new java.io.FileReader()".
Event transitive_modeled_event: Opening a resource with "new java.io.BufferedReader()".
Event alias: Assigning: r = new java.io.BufferedReader()
Also see events: [return_without_close]
470  			BufferedReader r = new BufferedReader(new FileReader(new File(hooksHome, "certify-order.txt")));
471  			
472  			ArrayList<File> order = new ArrayList<File>();
473  			for (;;) {
Event return_without_close: Returning from routine while a resource is still open.
Also see events: [modeled_event][transitive_modeled_event][alias]
At conditional (3): uncaught exception java.io.IOException thrown on line 474: Taking true branch.
474  				String line = r.readLine();
At conditional (1): (line == null): Taking false branch.
475  				if (line == null) break;
At conditional (2): (line.length() > 0): Taking false branch.
476  				if (line.length() > 0 && Character.isLetterOrDigit(line.charAt(0))) {
477  					File f = new File(hooksHome, line + ".lisp");
478  					if (f.exists()) {
479  						order.add(f);
480  					} else {
481  						System.err.println("Skipping non-existant hooks file: " + f);
482  					}
483  				}
484  			}
485  			return order.toArray(new File[order.size()]);
486  		}
487  		
488  		public static boolean isConsoleInterrupt(String line) {
489  			if (line.startsWith("************ ABORTING from raw Lisp ***********")) {
490  				return true; // ACL2's abort message, for everything but non-ansi GCL
491  			}
492  			if (line.startsWith("debugger invoked")) {
493  				return true; //harshrc// one of abort messages SBCL
494  			}
495  			if (line.startsWith("> Break: interrupt signal")) {
496  				return true; //harshrc// one of abort messages CCL
497  			}
498  			if (line.equals("Error: Console interrupt.\n")) {
499  				return true; // non-ansi GCL is special
500  			}
501  			/* These should not be needed now that we use ACL2's have the ABORTING line
502  			if (line.startsWith("Error: ")) {
503  				if (line.startsWith("Error: interrupted ")) {
504  					return true; // SBCL (older)
505  				}
506  				if (line.startsWith("Error: Interactive interrupt ")) {
507  					return true; // SBCL (newer)
508  				}
509  			}
510  			if (line.startsWith("> Break in process ")) {
511  				return true; // OpenMCL
512  			}
513  			*/
514  			return false;
515  		}
516  	
517  		public static String toAcl2PathString(String path) {
518  			if (File.separatorChar == '/') {
519  				return path;
520  			} else {
521  				return path.replace(File.separatorChar, '/');
522  			}
523  		}
524  		
525  		public static String toSystemPathString(String acl2Path) {
526  			if (File.separatorChar == '/') {
527  				return acl2Path;
528  			} else {
529  				return acl2Path.replace('/', File.separatorChar);
530  			}
531  		}
532  		
533  		public static String toAcl2PathFile(File f) {
534  			return toAcl2PathString(f.getAbsolutePath());
535  		}
536  	
537  		public static String toAcl2PathDir(File f) {
538  			String path = toAcl2PathFile(f);
539  			if (!path.endsWith("/")) {
540  				path += "/";
541  			}
542  			return path;
543  		}
544  	}