• Advertisement
Sign in to follow this  

Sorting out the Environment "Path" into a char **

This topic is 4276 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Language: C Compiler: VS 2005 I'm having a problem looping through the "Path" environment varible (ex: "Path=C:/Home/bin;C:/Foo/WTF/bin/") and storing it in a char** where each path in the "Path" is in a char * of the char **. Here is my for loop:
pCmdPath = (char **)malloc(path_amount *sizeof(char *));
	
temp = (char *)malloc(100 * sizeof(char));

for(iter = 0; iter < strlen(result); iter++) {
	if(result[iter] == ';') {
		temp[path_length] = result[iter];
		path_length++;
	}
	else {
		pCmdPath[path] = (char *)malloc(path_length * sizeof(char));
		strncpy(pCmdPath[path], temp, path_length);
		
		temp = (char *)realloc(temp, 100 * sizeof(char));

		if(NULL == temp) {
			printf("Error: couldn't realloc temp!\n");
			exit(1);
		}

		path++;
		path_length = 0;
	}
}

All the memory allocation works for pCmdPath, but the problems are in the strncpy and realloc (which isn't reallocing memory for temp). Any ideas or ideas on a different approach to this?

Share this post


Link to post
Share on other sites
Advertisement
Do you really have to use C? Unless its an absolute necessity, I would strongly suggest moving to C++. Your compiler will handle it automatically. There is no need to limit yourself to C, as C++ will make this problem much easier.

Here is my version of your program, if I understand what you are trying to do:

//------------------------------
// Includes
//------------------------------
#include <iostream>
#include <vector>
#include <string>
using namespace std;

//------------------------------
// Main
//------------------------------
int main()
{
// variables
string path = "";
vector<string> pathList;

// get the path from the user
cout << "Enter a list of paths, separated by semicolons: ";
getline( cin, path );

// loop until we find every path
while( 1 )
{
// search for a semicolon
int index = ( int )path.find( ";" );

// check if there is none
if( index == -1 )
{
// this means that there is 1 path left,
// so store it and break the loop
pathList.push_back( path );
break;
}
else
{
// there is at least two string left
// read the first one so that we can add it to the list
string temp = path.substr( 0, index );
pathList.push_back( temp );

// remove the first string from the path variable
path = path.substr( index + 1, path.size() );
}
}

// for testing, print out all the paths
cout << "\nThere are " << ( int )pathList.size() << "paths." << endl;
for( int i = 0; i < ( int )pathList.size(); i++ )
cout << pathList.at( i ) << endl;

// quit the program
return 0;
}




I just tested this and everything works perfectly. You should be able to modify it from here to accomplish whatever your goals are.

Share this post


Link to post
Share on other sites
That is a really nice solution and I really wish I could use C++, because I love the string class, along with STL. But we have to use C. = /

Share this post


Link to post
Share on other sites
You don't need to reallocate and copy so much. The tricky part is determining the number of paths in the variable.



// get the size needed for the buffer
DWORD dwChars = GetEnvironmentVariable(_T("PATH"), NULL, 0);

// allocate the buffer
LPTSTR lpBuffer = malloc(dwChars * sizeof(TCHAR));

// fill the buffer
GetEnvironmentVariable(_T("PATH"), lpBuffer, dwChars);

LPTSTR paths[64]; // assume a maximum of 64 paths

int j = 0;

paths[j++] = lpBuffer;

for (int i=0; i < _tstrlen(lpBuffer); i++){
if ( _T(";") == lpBuffer[i] ) {
paths[j++] = &lpBuffer[i+1];
lpBuffer[i] = _T("\0");
}
}

// do stuff with paths

free(lpBuffer)





That's off the top of my head (ie. it's untested). The point is that you can use the memory containing the path variable to hold the path strings - terminating them by replacing the semi-colon with a string terminator. Then you can use the paths array to simply hold pointers to the start of those strings in that buffer. If 64 isn't enough, loop through the lpBuffer counting semi-colons and then use malloc to allocate the paths array. Just remember that the paths array is an array of pointers to strings not a string itself.

Share this post


Link to post
Share on other sites
Thank you so much for the idea of pointing the the beginning of each of the paths.
I have one question about freeing up the memory used. Now say that there is a char * such as:

char *result = getenv("PATH");

Now that result holds the string of paths that I'm using the char **pCmdPath to hold pointers to the paths in result. If i do this:

free(pCmdPath);

Would this free all the memory used by result and the memory used by pCmdPath because both are pointing to the same position in memory?

Share this post


Link to post
Share on other sites
you can also use the strtok function:


void main()
{
char thestring[] = "this;is;a;semi-colon;seperated;string;";
char* token = strtok( thestring, ";" );
while( token )
{
std::cout << token << std::endl;
token = strtok( 0, ";" );
}
}

Share this post


Link to post
Share on other sites
Quote:
Original post by LostSource
Thank you so much for the idea of pointing the the beginning of each of the paths.
I have one question about freeing up the memory used. Now say that there is a char * such as:

char *result = getenv("PATH");

Now that result holds the string of paths that I'm using the char **pCmdPath to hold pointers to the paths in result. If i do this:

free(pCmdPath);

Would this free all the memory used by result and the memory used by pCmdPath because both are pointing to the same position in memory?


If pCmdPath points to the same address as result, then yes, but you'll keep your code easier to understand if you use result to free the memory. Variables are cheap, you don't need to reuse them.

If you want to use pCmdPath to delete particular paths, don't. Just set the entry in the array pCmdPath that you want to delete to NULL. For example, say you want to delete a particular path from the PATH, you need to build a completely new path string. You can do that by iterating through the pCmdPath array, if the entry is NULL, skip it, if not, strcat a semi-colon then strcat the original path. When the new path string is complete, use putenv to set it as the new PATH variable.

Another thing to be aware of is that the memory returned by getenv might be read-only - so the method I suggested wouldn't work. You'd have to copy the memory returned into another buffer that you could then manipulate.

Looking over this reference, getenv: "The returned pointer is not intended for modifying operations on the environment variable. Refer to putenv or your platform's manual for that." - and "Do not use this returned pointer to modify directly the environment variable from your program." - this suggests to me that the returned string is a copy of the environment variable and probably is safe to modify - but it could be implementation dependent. You might want to copy the buffer returned by getenv and manipulate that copy to stay on the safe side.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement