[pure-lang-svn] SF.net SVN: pure-lang:[524] pure/trunk
Status: Beta
Brought to you by:
agraef
From: <ag...@us...> - 2008-08-17 21:10:01
|
Revision: 524 http://pure-lang.svn.sourceforge.net/pure-lang/?rev=524&view=rev Author: agraef Date: 2008-08-17 21:10:10 +0000 (Sun, 17 Aug 2008) Log Message: ----------- Overhaul of the script and library search algorithms. Modified Paths: -------------- pure/trunk/ChangeLog pure/trunk/interpreter.cc pure/trunk/interpreter.hh pure/trunk/pure.cc pure/trunk/runtime.cc Modified: pure/trunk/ChangeLog =================================================================== --- pure/trunk/ChangeLog 2008-08-17 20:50:56 UTC (rev 523) +++ pure/trunk/ChangeLog 2008-08-17 21:10:10 UTC (rev 524) @@ -1,5 +1,61 @@ 2008-08-17 Albert Graef <Dr....@t-...> + * pure.cc, interpreter.cc/h, runtime.cc: Overhaul of the script + and library search algorithms. + + The prelude is now *always* searched for in PURELIB only, to + prevent code injection issues. Thus to use a custom prelude you'll + have to set the PURELIB environment variable accordingly, or + employ the '-n' option and explicitly specify the prelude on the + command line. + + Scripts specified on the command line or with the 'run' command + will *only* be searched for in the current directory. + + In addition to the PURELIB environment variable, new -I/-L command + line options and PURE_INCLUDE/PURE_LIBRARY environment variables + are now available to specify additional directories to search for + source files and dynamic libraries specified using relative + pathnames in 'using' clauses. + + For source scripts opened with a 'using' clause, the interpreter + searches the following directories in the given order: + + - the directory of the script containing the 'using' clause (or + the current working directory if the 'using' clause is read from + standard input), + + - directories specified with -I, in the order in which they are + specified on the command line, + + - directories specified in colon-separated format in the + PURE_INCLUDE variable, in the order in which they are specified, + + - the PURELIB directory. + + Similarly, dynamic libraries are searched for in: + + - the directory of the script containing the 'using' clause (or + the current working directory if the 'using' clause is read from + standard input), + + - directories specified with -L, in the order in which they are + specified on the command line, + + - directories specified in colon-separated format in the + PURE_LIBRARY variable, in the order in which they are specified, + + - the PURELIB directory, + + - other platform-specific locations searched by the dynamic + linker, such as system library directories and LD_LIBRARY_PATH on + Linux. + + Note that in either case the current working directory is *not* + searched by default (unless the 'using' clause is read from + standard input), but of course you can force this by adding '.' to + the corresponding search path. + * parser.yy, printer.cc et al: Revised list-of-tuples syntax. In order to include a tuple in a proper list value you can simply put the tuple inside parentheses now. Modified: pure/trunk/interpreter.cc =================================================================== --- pure/trunk/interpreter.cc 2008-08-17 20:50:56 UTC (rev 523) +++ pure/trunk/interpreter.cc 2008-08-17 21:10:10 UTC (rev 524) @@ -58,7 +58,7 @@ interpreter::interpreter() : verbose(0), interactive(false), ttymode(false), override(false), stats(false), temp(0), - ps("> "), lib(""), histfile("/.pure_history"), modname("pure"), + ps("> "), libdir(""), histfile("/.pure_history"), modname("pure"), nerrs(0), source_s(0), result(0), mem(0), exps(0), tmps(0), module(0), JIT(0), FPM(0), fptr(0) { @@ -434,7 +434,8 @@ #define BUFSIZE 1024 static string searchdir(const string& srcdir, const string& libdir, - const string& script) + const list<string>& include_dirs, + const string& script, bool search = true) { char cwd[BUFSIZE]; if (script.empty()) @@ -449,23 +450,23 @@ string fname; if (script[0] != '/') { // resolve relative pathname - if (srcdir.empty()) { + if (!search) { fname = workdir+script; if (chkfile(fname)) goto found; - if (!libdir.empty()) { - fname = libdir+script; - if (chkfile(fname)) goto found; - } fname = script; } else { - fname = srcdir+script; + fname = (srcdir.empty()?workdir:srcdir)+script; if (chkfile(fname)) goto found; + for (list<string>::const_iterator dir = include_dirs.begin(), + end = include_dirs.end(); dir != end; dir++) + if (!dir->empty()) { + fname = *dir+script; + if (chkfile(fname)) goto found; + } if (!libdir.empty()) { fname = libdir+script; if (chkfile(fname)) goto found; } - fname = workdir+script; - if (chkfile(fname)) goto found; fname = script; } } else @@ -505,6 +506,53 @@ return fname; } +/* Library search. */ + +static string searchlib(const string& srcdir, const string& libdir, + const list<string>& library_dirs, + const string& lib, bool search = true) +{ + char cwd[BUFSIZE]; + if (lib.empty()) + return lib; + else if (!getcwd(cwd, BUFSIZE)) { + perror("getcwd"); + return lib; + } + string workdir = cwd; + if (!workdir.empty() && workdir[workdir.size()-1] != '/') + workdir += "/"; + string fname; + if (lib[0] != '/') { + // resolve relative pathname + if (!search) { + fname = workdir+lib; + if (chkfile(fname)) goto found; + fname = lib; + } else { + fname = (srcdir.empty()?workdir:srcdir)+lib; + if (chkfile(fname)) goto found; + for (list<string>::const_iterator dir = library_dirs.begin(), + end = library_dirs.end(); dir != end; dir++) + if (!dir->empty()) { + fname = *dir+lib; + if (chkfile(fname)) goto found; + } + if (!libdir.empty()) { + fname = libdir+lib; + if (chkfile(fname)) goto found; + } + fname = lib; + } + } else + fname = lib; + found: +#if DEBUG>1 + std::cerr << "search '" << lib << "', found as '" << fname << "'\n"; +#endif + return fname; +} + // Run the interpreter on a source file, collection of source files, or on // string data. @@ -523,17 +571,19 @@ if (name.substr(name.size()-strlen(DLLEXT)) != DLLEXT) dllname += DLLEXT; // First try to open the library under the given name. - if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently(name.c_str(), &msg)) + string aname = searchlib(srcdir, libdir, librarydirs, name); + if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently(aname.c_str(), &msg)) return 0; else if (dllname == name) throw err(msg); + aname = searchlib(srcdir, libdir, librarydirs, dllname); // Now try the name with DLLEXT added. - else if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(dllname.c_str(), &msg)) + if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(aname.c_str(), &msg)) throw err(msg); return 0; } // ordinary source file - string fname = searchdir(srcdir, lib, s); + string fname = searchdir(srcdir, libdir, includedirs, s, check); if (check && sources.find(fname) != sources.end()) // already loaded, skip return 0; Modified: pure/trunk/interpreter.hh =================================================================== --- pure/trunk/interpreter.hh 2008-08-17 20:50:56 UTC (rev 523) +++ pure/trunk/interpreter.hh 2008-08-17 21:10:10 UTC (rev 524) @@ -313,10 +313,13 @@ bool stats; // stats mode (print execution times) uint8_t temp; // temporary level (purgable definitions) string ps; // prompt string - string lib; // library dir to search for source files + string libdir; // library dir to search for source files string histfile; // command history file string modname; // name of output (LLVM) module + // Additional directories to search for sources and libraries. + list<string> includedirs, librarydirs; + // Interpreter state. For internal use only. int nerrs; // current error count string errmsg; // last reported error (runstr) @@ -338,12 +341,14 @@ *************************************************************************/ /* Parse and execute the given source file (stdin if empty), or the given - list of files. If check is true (the default), only load the script if it - wasn't included before. Returns the last computed expression (if any). - (This expression is owned by the interpreter and must *not* be freed by - the caller.) This is the main interface function. If interactive is - true, readline is used to get interactive input from the user, using ps - as the prompt string. Please note that due to some global data shared by + list of files. If check is true (the default), a full search is performed + for relative pathnames (checking include directories and PURELIB to + locate the script file) and the script is only loaded if it wasn't + included before. Returns the last computed expression (if any). (This + expression is owned by the interpreter and must *not* be freed by the + caller.) This is the main interface function. If interactive is true, + readline is used to get interactive input from the user, using ps as the + prompt string. Please note that due to some global data shared by different interpreter instances, you can't run two interpreters concurrently right now. (It is possible to run them sequentially, though.) */ Modified: pure/trunk/pure.cc =================================================================== --- pure/trunk/pure.cc 2008-08-17 20:50:56 UTC (rev 523) +++ pure/trunk/pure.cc 2008-08-17 21:10:10 UTC (rev 524) @@ -36,16 +36,20 @@ pure [-h] [-i] [-n] [-q] [-v[level]] -x script [args ...]\n\ -h: Print this message and exit.\n\ -i: Force interactive mode (read commands from stdin).\n\ +-I: Add directory to search for included source files.\n\ +-L: Add directory to search for dynamic libraries.\n\ -n: Suppress automatic inclusion of the prelude.\n\ -q: Quiet startup (suppresses sign-on message).\n\ -v: Set verbosity level (useful for debugging purposes).\n\ -x: Execute script with given command line arguments.\n\ --: Stop option processing, pass remaining args in argv variable.\n\ Environment:\n\ -PURELIB: Directory to search for source scripts including the prelude.\n\ -PURE_MORE: Shell command for paging through output of the 'list' command.\n\ -PURE_PS: Command prompt to be used in the interactive command loop.\n\ -PURE_STACK: Maximum stack size in kilobytes (default: 0 = unlimited).\n" +PURELIB: Directory to search for library scripts and the prelude.\n\ +PURE_INCLUDE: Path to search for included source files.\n\ +PURE_LIBRARY: Path to search for dynamic libraries.\n\ +PURE_MORE: Shell command for paging through output of the 'list' command.\n\ +PURE_PS: Command prompt to be used in the interactive command loop.\n\ +PURE_STACK: Maximum stack size in kilobytes (default: 0 = unlimited).\n" #define LICENSE "This program is free software distributed under the GNU Public License\n(GPL V3 or later). Please see the COPYING file for details.\n" static const char *commands[] = { @@ -182,6 +186,26 @@ return !stat(s.c_str(), &st) && !S_ISDIR(st.st_mode); } +static void add_path(list<string>& dirs, const string& path) +{ + size_t pos = 0; + while (pos != string::npos) { + size_t end = path.find(':', pos); + string s; + if (end == string::npos) { + s = path.substr(pos); + pos = end; + } else { + s = path.substr(pos, end-pos); + pos = end+1; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + dirs.push_back(s); + } + } +} + int main(int argc, char *argv[]) { @@ -234,11 +258,13 @@ size_t n = strtoul(env, &end, 0); if (!*end) interpreter::stackmax = n*1024; } - if ((env = getenv("PURELIB"))) - interp.lib = string(env)+"/"; - else - interp.lib = string(PURELIB)+"/"; - string prelude = interp.lib+string("prelude.pure"); + if ((env = getenv("PURELIB"))) { + string s = env; + if (!s.empty() && s[s.size()-1] != '/') s.append("/"); + interp.libdir = s; + } else + interp.libdir = string(PURELIB)+"/"; + string prelude = interp.libdir+string("prelude.pure"); #if USE_FASTCC // This global option is needed to get tail call optimization (you'll also // need to have USE_FASTCC in interpreter.hh enabled). @@ -258,8 +284,34 @@ want_prelude = false; else if (*args == string("-q")) quiet = true; - else if (string(*args).substr(0,2) == "-v") { + else if (string(*args).substr(0,2) == "-I") { string s = string(*args).substr(2); + if (s.empty()) { + if (!*++args) { + interp.error(prog + ": -I lacks directory argument"); + return 1; + } + s = *args; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + interp.includedirs.push_back(s); + } + } else if (string(*args).substr(0,2) == "-L") { + string s = string(*args).substr(2); + if (s.empty()) { + if (!*++args) { + interp.error(prog + ": -L lacks directory argument"); + return 1; + } + s = *args; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + interp.librarydirs.push_back(s); + } + } else if (string(*args).substr(0,2) == "-v") { + string s = string(*args).substr(2); if (s.empty()) continue; char *end; strtoul(s.c_str(), &end, 0); @@ -277,16 +329,14 @@ interp.error(prog + ": invalid option " + *args); return 1; } + if ((env = getenv("PURE_INCLUDE"))) add_path(interp.includedirs, env); + if ((env = getenv("PURE_LIBRARY"))) add_path(interp.librarydirs, env); interp.init_sys_vars(PACKAGE_VERSION, HOST, myargs); if (want_prelude) { // load the prelude if we can find it - if (chkfile("prelude.pure")) { - prelude = "prelude.pure"; + if (chkfile(prelude)) { have_prelude = true; - } else if (chkfile(prelude)) // try again in the PURELIB directory - have_prelude = true; - if (have_prelude) { - interp.run(prelude); + interp.run(prelude, false); interp.compile(); } } @@ -300,7 +350,7 @@ } else if (*argv == string("-x")) { if (*++argv) { count++; interp.modname = *argv; - interp.run(*argv); + interp.run(*argv, false); } else { interp.error(prog + ": missing script name"); return 1; @@ -308,11 +358,15 @@ break; } else if (*argv == string("--")) break; - else if (**argv == '-') + else if (string(*argv).substr(0,2) == "-I" || + string(*argv).substr(0,2) == "-L") { + string s = string(*argv).substr(2); + if (s.empty()) ++argv; + } else if (**argv == '-') ; else if (**argv) { if (count++ == 0) interp.modname = *argv; - interp.run(*argv); + interp.run(*argv, false); } if (count > 0 && !force_interactive) { if (interp.verbose&verbosity::dump) interp.compile(); @@ -345,7 +399,7 @@ histfile = strdup(interp.histfile.c_str()); } interp.temp = 1; - interp.run(""); + interp.run("", false); if (interp.ttymode) cout << endl; return 0; } Modified: pure/trunk/runtime.cc =================================================================== --- pure/trunk/runtime.cc 2008-08-17 20:50:56 UTC (rev 523) +++ pure/trunk/runtime.cc 2008-08-17 21:10:10 UTC (rev 524) @@ -879,6 +879,35 @@ #include <llvm/Target/TargetOptions.h> +#include <sys/types.h> +#include <sys/stat.h> + +static inline bool chkfile(const string& s) +{ + struct stat st; + return !stat(s.c_str(), &st) && !S_ISDIR(st.st_mode); +} + +static void add_path(list<string>& dirs, const string& path) +{ + size_t pos = 0; + while (pos != string::npos) { + size_t end = path.find(':', pos); + string s; + if (end == string::npos) { + s = path.substr(pos); + pos = end; + } else { + s = path.substr(pos, end-pos); + pos = end+1; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + dirs.push_back(s); + } + } +} + extern "C" pure_interp *pure_create_interp(int argc, char *argv[]) { @@ -901,11 +930,13 @@ size_t n = strtoul(env, &end, 0); if (!*end) interpreter::stackmax = n*1024; } - if ((env = getenv("PURELIB"))) - interp.lib = string(env)+"/"; - else - interp.lib = string(PURELIB)+"/"; - string prelude = interp.lib+string("prelude.pure"); + if ((env = getenv("PURELIB"))) { + string s = env; + if (!s.empty() && s[s.size()-1] != '/') s.append("/"); + interp.libdir = s; + } else + interp.libdir = string(PURELIB)+"/"; + string prelude = interp.libdir+string("prelude.pure"); #if USE_FASTCC // This global option is needed to get tail call optimization (you'll also // need to have USE_FASTCC in interpreter.hh enabled). @@ -922,8 +953,36 @@ want_prelude = false; else if (*args == string("-q")) /* ignored */; - else if (string(*args).substr(0,2) == "-v") { + else if (string(*args).substr(0,2) == "-I") { string s = string(*args).substr(2); + if (s.empty()) { + if (!*++args) { + cerr << "pure_create_interp: -I lacks directory argument\n"; + delete _interp; + return 0; + } + s = *args; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + interp.includedirs.push_back(s); + } + } else if (string(*args).substr(0,2) == "-L") { + string s = string(*args).substr(2); + if (s.empty()) { + if (!*++args) { + cerr << "pure_create_interp: -L lacks directory argument\n"; + delete _interp; + return 0; + } + s = *args; + } + if (!s.empty()) { + if (s[s.size()-1] != '/') s.append("/"); + interp.librarydirs.push_back(s); + } + } else if (string(*args).substr(0,2) == "-v") { + string s = string(*args).substr(2); if (s.empty()) continue; char *end; strtoul(s.c_str(), &end, 0); @@ -943,19 +1002,14 @@ delete _interp; return 0; } + if ((env = getenv("PURE_INCLUDE"))) add_path(interp.includedirs, env); + if ((env = getenv("PURE_LIBRARY"))) add_path(interp.librarydirs, env); interp.init_sys_vars(PACKAGE_VERSION, HOST, myargs); if (want_prelude) { // load the prelude if we can find it - FILE *fp = fopen("prelude.pure", "r"); - if (fp) - prelude = "prelude.pure"; - else - // try again in the PURELIB directory - fp = fopen(prelude.c_str(), "r"); - if (fp) { - fclose(fp); + if (chkfile(prelude)) { have_prelude = true; - interp.run(prelude); + interp.run(prelude, false); interp.compile(); } } @@ -969,7 +1023,7 @@ } else if (*argv == string("-x")) { if (*++argv) { count++; interp.modname = *argv; - interp.run(*argv); + interp.run(*argv, false); } else { cerr << "pure_create_interp: missing script name\n"; delete _interp; @@ -978,11 +1032,15 @@ break; } else if (*argv == string("--")) break; - else if (**argv == '-') + else if (string(*argv).substr(0,2) == "-I" || + string(*argv).substr(0,2) == "-L") { + string s = string(*argv).substr(2); + if (s.empty()) ++argv; + } else if (**argv == '-') ; else if (**argv) { if (count++ == 0) interp.modname = *argv; - interp.run(*argv); + interp.run(*argv, false); } interp.symtab.init_builtins(); return (pure_interp*)_interp; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |