We've come to the conclusion that we need to modify the PATH environment
variable, at least when running on MacOSX or on Windows.

Attached is a little program that defines 
        void setEnvPath(string const & name, vector<string> const & env);
analogous to the
        vector<string> const getEnvPath(string const & name);
that I added the other day.

$ g++ -Iboost -o path path.C

Are people happy for this to go into the repository? I think that expanding
the PATH string to a vector and then collapsing back from a vector are the
safest things to do, given the different syntaxes for paths and for
separators on the different OSes.

The alternative is something like:
 void prepend_to_path(string const & path_addition);
but I think that getting that right is non-trivial unfortunately.

Thoughts?

-- 
Angus
#include <boost/assert.hpp>
#include <boost/tokenizer.hpp>

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#define HAVE_PUTENV 1
/* #undef HAVE_SETENV */

using std::string;
using std::vector;

string const trim(string const & a, char const * p)
{
        BOOST_ASSERT(p);

        if (a.empty() || !*p)
                return a;

        string::size_type r = a.find_last_not_of(p);
        string::size_type l = a.find_first_not_of(p);

        // Is this the minimal test? (lgb)
        if (r == string::npos && l == string::npos)
                return string();

        return a.substr(l, r - l + 1);
}


string const subst(string const & a, char oldchar, char newchar)
{
        string tmp(a);
        string::iterator lit = tmp.begin();
        string::iterator end = tmp.end();
        for (; lit != end; ++lit)
                if ((*lit) == oldchar)
                        (*lit) = newchar;
        return tmp;
}


string const subst(string const & a,
                   string const & oldstr, string const & newstr)
{
        string lstr(a);
        string::size_type i = 0;
        string::size_type const olen = oldstr.length();
        while ((i = lstr.find(oldstr, i)) != string::npos) {
                lstr.replace(i, olen, newstr);
                i += newstr.length(); // We need to be sure that we dont
                // use the same i over and over again.
        }
        return lstr;
}


namespace os {

string external_path(string const & p)
{	
	string dos_path;

#if defined(__CYGWIN__) || defined(__CYGWIN32__)
	// Translate from cygwin path syntax to dos path syntax
	if (is_absolute_path(p)) {
		char dp[PATH_MAX+1];
		cygwin_conv_to_full_win32_path(p.c_str(), dp);
		dos_path = !dp ? string() : dp;
	}

	else return p;
#else // regular Win32
	dos_path = p;
#endif
	
	// No backslashes in LaTeX files
	dos_path = subst(dos_path,'\\','/');
	return dos_path;
}


string internal_path(string const & p)
{
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
	char posix_path[PATH_MAX+1];
	posix_path[0] = '\0';
	cygwin_conv_to_posix_path(p.c_str(), posix_path);
	return posix_path;
#else
	return subst(p,"\\","/");
#endif
}

} // namespace os


string const getEnv(string const & envname)
{
	// f.ex. what about error checking?
	char const * const ch = getenv(envname.c_str());
	return !ch ? string() : ch;
}


vector<string> const getEnvPath(string const & name)
{
	typedef boost::char_separator<char> Separator;
	typedef boost::tokenizer<Separator> Tokenizer;

#if defined (__EMX__) || defined (_WIN32)
	Separator const separator(";");
#else
	Separator const separator(":");
#endif

	string const env_var = getEnv(name);
	Tokenizer const tokens(env_var, separator);
	Tokenizer::const_iterator it = tokens.begin();
	Tokenizer::const_iterator const end = tokens.end();

	std::vector<string> vars;
	for (; it != end; ++it) {
		string const path = os::internal_path(*it);
		vars.push_back(trim(path, "\""));
	}

	return vars;
}


bool PutEnv(string const & envstr)
{
	// CHECK Look at and fix this.
	// f.ex. what about error checking?

#if HAVE_PUTENV
	// this leaks, but what can we do about it?
	//   Is doing a getenv() and a free() of the older value
	//   a good idea? (JMarc)
	// Actually we don't have to leak...calling putenv like this
	// should be enough: ... and this is obviously not enough if putenv
	// does not make a copy of the string. It is also not very wise to
	// put a string on the free store. If we have to leak we should do it
	// like this:
	char * leaker = new char[envstr.length() + 1];
	envstr.copy(leaker, envstr.length());
	leaker[envstr.length()] = '\0';
	int const retval = ::putenv(leaker);

	// If putenv does not make a copy of the char const * this
	// is very dangerous. OTOH if it does take a copy this is the
	// best solution.
	// The  only implementation of putenv that I have seen does not
	// allocate memory. _And_ after testing the putenv in glibc it
	// seems that we need to make a copy of the string contents.
	// I will enable the above.
	//int retval = lyx::putenv(envstr.c_str());
#else
#ifdef HAVE_SETENV
	string varname;
	string const str = envstr.split(varname,'=');
	int const retval = ::setenv(varname.c_str(), str.c_str(), true);
#else
	// No environment setting function. Can this happen?
	int const retval = 1; //return an error condition.
#endif
#endif
	return retval == 0;
}


void setEnvPath(string const & name, vector<string> const & env)
{
#if defined (__EMX__) || defined (_WIN32)
	char const separator(';');
#else
	char const separator(':');
#endif

	std::ostringstream ss;
	vector<string>::const_iterator it = env.begin();
	vector<string>::const_iterator const end = env.end();
	for (; it != end; ++it) {
		if (ss.tellp() > 0)
			ss << separator;
		ss << os::external_path(*it);
	}
	PutEnv(name + "=" + ss.str());
}


void print(std::ostream & os, vector<string> vec)
{
	vector<string>::const_iterator it = vec.begin();
	vector<string>::const_iterator const end = vec.end();
	for (; it != end; ++it)
		os << *it << '\n';
}


int main()
{
	string const path_str = getEnv("PATH");
	std::cout << path_str << "\n\n";

	vector<string> path = getEnvPath("PATH");
	print(std::cout, path);
	std::cout << std::endl;

	path.insert(path.begin(), "C:/Angus");
	print(std::cout, path);
	std::cout << std::endl;

	setEnvPath("PATH", path);
	string const path_str2 = getEnv("PATH");
	std::cout << path_str2 << "\n\n";


	return 0;
}

Reply via email to