• 11
• 9
• 10
• 9
• 10

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

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

## 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 on other sites
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 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 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 bufferDWORD dwChars = GetEnvironmentVariable(_T("PATH"), NULL, 0); // allocate the bufferLPTSTR lpBuffer = malloc(dwChars * sizeof(TCHAR));// fill the bufferGetEnvironmentVariable(_T("PATH"), lpBuffer, dwChars); LPTSTR paths[64]; // assume a maximum of 64 pathsint j = 0;paths[j++] = lpBuffer;for (int i=0; i < _tstrlen(lpBuffer); i++){   if ( _T(";") == lpBuffer ) {      paths[j++] = &lpBuffer[i+1];      lpBuffer = _T("\0");   }}// do stuff with pathsfree(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 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 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 on other sites
Quote:
 Original post by LostSourceThank 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.