The user wants to be able to invoke ROBIN with various command-line switches to control its initial state. The user also wants to be able to dictate how ROBIN generally responds, and where it creates and maintains its files. Today you will learn
Many operating systems, such as DOS and UNIX, enable the user to pass parameters to your program when the program starts. These are called command-line arguments, and typically are separated by spaces on the command line, as in the following example:
SomeProgram Param1 Param2 Param3
These parameters are not passed to main() directly. Instead, every program's main() function is passed three parameters. The first parameter is an integer count of the number of arguments on the command line. The program name itself is counted, so every program has at least one parameter. The example command line shown here has four parameters. (The name SomeProgram plus the three parameters makes a total of four command-line arguments.)
The second parameter passed to main() is an array of pointers to character strings. Because an array name is a constant pointer to the first element of the array, you can declare this argument to be a pointer to a pointer to char, or a pointer to an array of char.
The third argument also is an array of pointers to char, but this contains the system's environment. In DOS and UNIX, the environment typically includes the PATH statement and a variety of other defined values. Every environment variable, however, is a string. If you write SET MYVAR = 5 in your AUTOEXEC.BAT (in DOS), you define a variable named MYVAR and assign to it the string "5".
Typically, the first argument is called argc (argument count), but you can call it anything you want. The second argument often is called argv (argument vector) but, again, this is just a convention. The third argument often is called env.
It is legal to define main() to take any number of arguments from zero through three. If you declare main() to take no arguments, of course, none of these values will be legal. As you declare each of these arguments, their values become available, but they must include all the arguments to the left. The legal declarations of main's arguments follow:
int main() int main(int argc) int main(int argc, char** argv) int main(int argc, char** argv, char** env)
You cannot try to declare the char** without also declaring the int, for example.
It is common to test argc to ensure that you have received the expected number of arguments, and to use argv to access the arguments themselves. Note that argv[0] is the name of the program, and argv[1] is the first parameter to the program, represented as a string. If your program takes two numbers as arguments, you will need to translate these numbers to strings. Listing 6.1 illustrates how to use the command-line arguments.
Listing 6.1 Using Command-Line Arguments
1: #include <iostream.h> 2: void main(int argc, char **argv, char ** env) 3: { 4: cout << "Received " << argc << " arguments...\n"; 5: for (int i=0; i<argc; i++) 6: cout << "argument " << i << ": " << argv[i] << endl; 7: for (int j = 0; env[j]!=0; j++) 8: cout << "env var: " << env[j] << endl; 9: }
Output:
d:\bc4\book2\exe>0601 Teach Yourself More C++ In 21 Days Received 8 arguments... argument 0: D:0601.EXE argument 1: Teach argument 2: Yourself argument 3: More argument 4: C++ argument 5: In argument 6: 21 argument 7: Days env var: CONFIG=Normal env var: PROMPT=$p$g env var: TEMP=c:\temp env var: TMP=c:\temp
Analysis:
The function main() declares three arguments: argc is an integer that contains the count of command-line arguments; argv is a pointer to the array of strings; and env is also a pointer to an array of strings, each of which holds an environmental variable.
Each string in the array pointed to by argv is a command-line argument. Note that argv just as easily could have been declared as char *argv[]. It is a matter of programming style how you declare argv and env; even though this program declared it as a pointer to a pointer, array offsets still were used to access the individual strings. Even the names argc, argv, and env are arbitrary; the program will work just as well if you call them Sleepy, Sneezy, and Doc.
In line 4, argc is used to print the number of command-line arguments: eight in all, counting the program name itself.
In lines 5 and 6, each of the command-line arguments is printed, passing the NULL-terminated strings to cout by indexing into the array of strings. Similarly, in lines 7 and 8, the environment variables are accessed. Note that the output has been modified to remove a number of environment variables on my computer. Run the program and print your own environment; you might be surprised at what you find.
DO use the names argc, argv, and env.
DON'T forget that argv is indexed from 0 to argc-1.
DO remember that argv[0] is the name of the program.
DO treat argv as an array of pointers to C-style strings.
ROBIN expects the first argument other than the name of the program (argv[1]) to contain a flag. You can process that flag with a switch statement. Because the protocol for ROBIN dictates that only the first letter of the flag is significant, the code for switching on the flag is straightforward, as shown in listing 6.2.
Listing 6.2 Switching on Command-Line Flags
1: // Listing 6.2 - Switching on Command-Line Flags 2: 3: #include <iostream.h> 4: 5: // function prototypes 6: void Process(int argc, char ** argv); 7: int main(int argc, char **argv, char ** env); 8: 9: // driver program 10: int main(int argc, char **argv, char ** env) 11: { 12: // rudimentary usage testing 13: if (argc < 3 || argv[1][0] != '-') 14: { 15: cout << "Usage: " << argv[0] << " -Flag [arguments]" << endl; 16: return 1; 17: } 18: Process (argc, argv); 19: return 0; 20: } 21: 22: // switch on command line arguments 23: void Process(int argc, char ** argv) 24: { 25: switch (argv[1][1]) 26: { 27: case 'I': 28: case 'i': 29: cout << "Index following text" << endl; 30: break; 31: 32: case 'F': 33: case 'f': 34: cout << "Index from a File" << endl; 35: break; 36: 37: case 'L': 38: case 'l': 39: cout << "Make a note for each line in a file" << endl; 40: break; 41: 42: case 'S': 43: case 's': 44: case '?': 45: cout << "Search for following text" << endl; 46: break; 47: } 48: }
Output:
d:\>0602 -I this and that Index following text d:\>0602 -S this and that Search for following text d:\>0602 F this and that Usage: d:\>0602.EXE -Flag [arguments]
Analysis:
In line 13, the number of arguments is checked. This program expects at least two (the name of the program and the flags, and at least one more). If there are fewer than three arguments, or if the first character of the second argument (the flag) isn't a dash, a usage statement is printed in line 15, and the program exits in line 16.
Assuming that the program passes this rudimentary usage testing, control is passed to the Process() function, and argc and argv are passed along.
The tests and actions illustrated in this function are a skeleton of what the actual program will do. Note that the text of the full word is not checked; the user might have entered 0602 -Fail and this program would have treated it exactly as if the user had written 0602 -File.
This program switches on one of a very limited set of flags. Exactly the same effect could be accomplished by copying the executable to a set of new names and then switching on argv[0]. In DOS, this is wasteful of disk space. In UNIX, however, links can be used (ln), thereby providing the same functionality with virtually no wasted disk space. Listing 6.3 illustrates this idea.
Listing 6.3 Using the Name of the File
1: // Listing 6.3 - Using the Name of the File 2: 3: #include <iostream.h> 4: #include <string.h> 5: 6: // function prototypes 7: void Process(char **argv); 8: int main(int argc, char **argv, char ** env); 9: 10: // driver program 11: int main(int argc, char **argv, char ** env) 12: { 13: // rudimentary usage testing 14: if (argc < 2 ) 15: { 16: cout << "Usage: " << argv[0] << " [arguments]" << endl; 17: return 1; 18: } 19: Process (argv); 20: return 0; 21: } 22: 23: // switch on command line arguments 24: void Process(char ** argv) 25: { 26: // find the name without the path 27: int len = strlen(argv[0]); 28: for (char *ptr = &argv[0][len-1]; *(ptr-1) != '\\';ptr-- ); 29: 30: if (strcmp(ptr,"INDEX.EXE") == 0) 31: cout << "Index following text" << endl; 32: 33: if (strcmp(ptr,"FILE.EXE") == 0) 34: cout << "Index from a File" << endl; 35: 36: if (strcmp(ptr,"LIST.EXE") == 0) 37: cout << "Make a note for each line in a file" << endl; 38: 39: if (strcmp(ptr,"FIND.EXE") == 0) 40: cout << "Search for following text" << endl; 41: }
Output:
d:\>copy 0603.exe index.exe d:\>copy 0603.exe find.exe d:\>Index don't forget to rename the file Index following text d:\>find the text you saved Search for following text
Note: Your compiler may warn you that the env parameter to main() is never used in this program and many of the subsequent listings.
Analysis:
Listing 6.3 is similar to listing 6.2. This time, however, process() is sent only the value of argv. In fact, you might want to send in only argv[0], because that is the only argument that Process() really needs to consider.
In line 28, a pointer is set to the end of the string held in argv[0]. The pointer ticks backward, searching for the backslash character that DOS puts at the beginning of each directory. This takes a string such as D:\PROGRAMS\TYMCPP\DAY6\FIND.EXE and turns it into FIND.EXE. In lines 30, 33, 36, and 39, this string then is compared with the expected names for the program; when a match is found, the correct function will be called.
Although ROBIN does not take advantage of this technique, many other utilities do. There are a set of UNIX utilities that really are one program with a number of different names. The program "does the right thing," depending on how you call it.
Note: The specific processing of the file name as shown in line 28 is appropriate for DOS programs. If you are programming in a different environment, you may need to modify this line slightly. In UNIX, for example, the slash goes the other way (/).
It is convenient that DOS (and other operating systems) present the command line as an array of pointers to C-style strings. It therefore is easy to adapt the QuickSort() routine from day 4 to sort the command-line arguments. Listing 6.4 extends listing 6.2 to include the capability to sort the command line (except the file name and the flag) when the -I flag is used.
Note: This program requires inclusion of string.hpp that you wrote earlier, and that you compile in string.cpp as a module of this program.
Listing 6.4 Sorting the Arguments
1: // Listing 6.4 - Sorting the Arguments 2: 3: #include <iostream.h> 4: #include <string.h> 5: #include "string.hpp" 6: 7: // function prototypes 8: void QuickSort(String**, int, int); 9: void Swap(String*& i, String*& j); 10: int main(int argc, char **argv, char ** env); 11: void Process(int argc, char ** argv); 12: 13: // driver program 14: int main(int argc, char **argv, char ** env) 15: { 16: // rudimentary usage testing 17: if (argc < 3 || argv[1][0] != '-') 18: { 19: cout << "Usage: " << argv[0] << " -Flag [arguments]" << endl; 20: return 1; 21: } 22: Process (argc, argv); 23: return 0; 24: } 25: 26: // switch on command line arguments 27: void Process(int argc, char ** argv) 28: { 29: String *buffer[100]; 30: int i; 31: switch (argv[1][1]) 32: { 33: 34: case 'I': 35: case 'i': 36: for (i=0; i<100 && i < argc-2; i++) 37: buffer[i] = new String(argv[i+2]); 38: QuickSort(buffer,0,argc-2); 39: for (i = 0; i < argc-3; i++) 40: cout << *buffer[i] << ", "; 41: cout << *buffer[argc-3] << endl; 42: break; 43: 44: case 'F': 45: case 'f': 46: cout << "Index from a File" << endl; 47: break; 48: 49: case 'L': 50: case 'l': 51: cout << "Make a note for each line in a file" << endl; 52: break; 53: 54: case 'S': 55: case 's': 56: case '?': 57: cout << "Search for following text" << endl; 58: break; 59: } 60: } 61: 62: // QuickSort implemented with Median of Three 63: void QuickSort(String** Input, int left, int right) 64: { 65: if (right > left) 66: { 67: int i = left-1; 68: int x = right; 69: for (;;) 70: { 71: while (*Input[++i] < *Input[right-1]) 72: ; 73: while (*Input[--x] > *Input[right-1]) 74: ; 75: 76: if (i >= x) 77: break; 78: Swap(Input[i], Input[x]); 79: } 80: Swap(Input[i], Input[right-1]); 81: QuickSort(Input,left,i); 82: QuickSort(Input,i+1,right); 83: } 84: } 85: 86: void Swap(String*& i, String*& j) 87: { 88: String* temp; 89: temp = j; 90: j = i; 91: i = temp; 92: }
Output:
d:\>0604 -I Eternal Vigilance Is The Price Of Liberty Eternal, Is, Liberty, Of, Price, The, Vigilance
Analysis:
Listing 6.4 sorts the command-line arguments. It does so by creating an array of strings one for each command-line argument other than the name of the program and the -I switch, and then passing the array of strings to QuickSort().
In line 37, the array is created; in line 38, it is passed to QuickSort(). One terrible flaw in this approach is that the array must be of fixed size, as shown in line 29. This is wasteful of memory and limits your program to the number of parameters specified as the upper limit of the array.
Note, by the way, that the buffer and int must be declared in lines 29 and 30outside of the switch statement itself. It is tempting to declare them in line 36, but a case statement does not have scope and it is an error to declare variables there unless you use braces to create a scope. Lines 29 through 42 could be rewritten:
29: switch (argv[1][1]) 30: { 31: 32: case 'I': 33: case 'i': 34 { 25: String *buffer[100]; 36: for (int i=0; i<100 && i < argc-2; i++) 37: buffer[i] = new String(argv[i+2]); 38: QuickSort(buffer,0,argc-2); 39: for (i = 0; i < argc-3; i++) 40: cout << *buffer[i] << ", "; 41: cout << *buffer[argc-3] << endl; 42: } 43: break;
This code would have worked just as well.
As noted in the preceding section, listing 6.4 suffers the fatal flaw that it must declare a fixed-size buffer of String pointers. This problem is rectified easily by creating a dynamic array of strings, implemented using an unsorted linked list, as shown in listing 6.5. Note that even this program should be modified to flesh out the Array class and to parameterize it.
Listing 6.5 Sorting a List of Command-Line Arguments
1: // Listing 6.5 - Sorting a List of Command-Line Arguments 2: 3: #include <iostream.h> 4: #include <string.h> 5: #include "string.hpp" 6: typedef unsigned long ULONG; 7: 8: // **************** String Node class ************ 9: class Node 10: { 11: public: 12: Node (String*); 13: Node (); 14: ~Node(); 15: void InsertAfter(Node*); 16: Node * GetNext() { return itsNext; } 17: const Node * GetNext() const { return itsNext; } 17: void SetNext(Node * next) { itsNext = next; } 18: String* GetString() const; 19: String*& GetString(); 20: 21: private: 22: String *itsString; 23: Node * itsNext; 24: }; 25: 26: 27: // **************** String List ************ 28: class StringList 29: { 30: public: 31: StringList(); 32: ~StringList(); 33: ULONG GetCount() const { return itsCount; } 34: void Insert(String *); 35: void Iterate(void (String::*f)()const) ; 36: String* operator[](ULONG) const ; 37: String*& operator[](ULONG) ; 38: 39: private: 40: Node itsHead; 41: ULONG itsCount; 42: }; 43: 44: // *** node implementations **** 45: 46: Node::Node(String* pString): 47: itsString(pString), 48: itsNext(0) 49: {} 50: 51: Node::Node(): 52: itsString(0), 53: itsNext(0) 54: {} 55: 56: 57: Node::~Node() 58: { 59: delete itsString; 60: itsString = 0; 61: delete itsNext; 62: itsNext = 0; 63: } 64: 65: void Node::InsertAfter(Node* newNode) 66: { 67: newNode->SetNext(itsNext); 68: itsNext=newNode; 69: } 70: 71: String * Node::GetString() const 72: { 73: if (itsString) 74: return itsString; 75: else 76: return NULL; //error 77: } 78: 79: String*& Node::GetString() 80: { 81: return itsString; 82: } 83: 84: // Implementations for Lists... 85: 86: StringList::StringList(): 87: itsCount(0), 88: itsHead(0) // initialize head node to have no String 89: {} 90: 91: StringList::~StringList() 92: { 93: } 94: 95: String * StringList::operator[](ULONG offSet) const 96: { 97: const Node* pNode = itsHead.GetNext(); 98: 99: if (offSet+1 > itsCount) 100: return NULL; // error 101: 102: for (ULONG i=0;i<offSet; i++) 103: pNode = pNode->GetNext(); 104: 105: return pNode->GetString(); 106: } 107: 108: String*& StringList::operator[](ULONG offSet) 109: { 110: 111: if (offSet+1 > itsCount) 112: { 113: Node* NewNode = new Node; 114: for (Node *pNode = &itsHead;;pNode = pNode->GetNext()) 115: { 116: if (pNode->GetNext() == NULL ) //|| *(pNode->GetNext()) < *NewNode) 117: { 118: pNode->InsertAfter(NewNode); 119: itsCount++; 120: return NewNode->GetString(); 121: } 122: } 123: } 124: Node *pNode = itsHead.GetNext(); 125: for (ULONG i=0;i<offSet; i++) 126: pNode = pNode->GetNext(); 127: return pNode->GetString(); 128: } 129: 130: void StringList::Insert(String* pString) 131: { 132: Node * NewNode = new Node(pString); 133: itsCount++; 134: for (Node * pNode = &itsHead;;pNode = pNode->GetNext()) 135: { 136: if (pNode->GetNext() == NULL ) //|| *(pNode->GetNext()) < *NewNode) 137: { 138: pNode->InsertAfter(NewNode); 139: break; 140: } 141: } 142: } 143: 144: void StringList::Iterate(void (String::*func)()const) 145: { 146: for (Node* pNode = itsHead.GetNext(); 147: pNode; 148: pNode=pNode->GetNext() 149: ) 150: (pNode->GetString()->*func)(); 151: 152: } 153: 154: // function prototypes 155: void QuickSort(StringList&, int, int); 156: void Swap(String*& i, String*& j); 157: int main(int argc, char **argv, char ** env); 158: void Process(int argc, char ** argv); 159: 160: // driver program 161: int main(int argc, char **argv, char ** env) 162: { 163: // rudimentary usage testing 164: if (argc < 3 || argv[1][0] != '-') 165: { 166: cout << "Usage: " << argv[0] << " -Flag [arguments]" << endl; 167: return 1; 168: } 169: Process (argc, argv); 170: return 0; 171: } 172: 173: // switch on command line arguments 174: void Process(int argc, char ** argv) 175: { 176: StringList buffer; 177: int i; 178: switch (argv[1][1]) 179: { 180: 181: case 'I': 182: case 'i': 183: for (i=0; i<100 && i < argc-2; i++) 184: buffer[i] = new String(argv[i+2]); 185: QuickSort(buffer,0,argc-2); 186: for (i = 0; i < argc-3; i++) 187: cout << *buffer[i] << ", "; 188: cout << *buffer[argc-3] << endl; 189: break; 190: 191: case 'F': 192: case 'f': 193: cout << "Index from a File" << endl; 194: break; 195: 196: case 'L': 197: case 'l': 198: cout << "Make a String for each line in a file" << endl; 199: break; 200: 201: case 'S': 202: case 's': 203: case '?': 204: cout << "Search for following text" << endl; 205: break; 206: } 207: } 208: 209: // QuickSort implemented with Median of Three 210: void QuickSort(StringList& Input, int left, int right) 211: { 212: if (right > left) 213: { 214: int i = left-1; 215: int x = right; 216: for (;;) 217: { 218: while (*Input[++i] < *Input[right-1]) 219: ; 220: while (*Input[--x] > *Input[right-1]) 221: ; 222: 223: if (i >= x) 224: break; 225: Swap(Input[i], Input[x]); 226: } 227: Swap(Input[i], Input[right-1]); 228: QuickSort(Input,left,i); 229: QuickSort(Input,i+1,right); 230: } 231: } 232: 233: void Swap(String*& i, String*& j) 234: { 235: String* temp; 236: temp = j; 237: j = i; 238: i = temp; 239: }
Output:
d:\>0605 -I Eternal Vigilance Is The Price Of Liberty Eternal, Is, Liberty, Of, Price, The, Vigilance
Analysis:
In lines 8 through 24, a string node class is declared. Two constructors are offered, one takes a pointer to a string, and the other is the default constructor, which takes no parameters.
In lines 15 through 19, simple accessor and manipulation functions are provided; and in lines 22 and 23, the member variables are declared.
In lines 27 through 42, the StringList class is declared. A StringList is an unsorted linked list of String objects. The critical method is declared in line 37: the non-constant offset operator[] is declared to return a reference to a pointer to a string. This is what enables you to swap strings in QuickSort().
The implementations for the StringList are much like what you saw on day 3, except for the implementation of the non-constant offset operator[]. If the user requests access to an element that does not yet exist, a new node is created in line 118, and the count of nodes is incremented in line 119. In line 120, the new node's string pointer is returned by reference. This allows the user to write the line of code that appears in line 184:
buffer[i] = new String(argv[i+2]);
This is a critical line of code, so examine it closely. A new String object is being created, initialized with a command-line argument. The pointer returned by new() is passed into the StringList using the offset operator[], but the offset requested does not yet exist in the array. A new node is created, which returns a reference to its pointer to String. That pointer then is set to point to the new string just created, so the command-line argument is put into the array.
The array is passed to QuickSort() in line 185, and then the results are printed in lines 186 through 188. QuickSort() operates as in previous examples, except that the array is in fact a StringList, passed into QuickSort() by reference to save memory. This cannot be a constant reference, of course, because QuickSort() changes the array.
The DOS operating system (and others as well) utilizes what is known as the environment. The environment consists of values stored by the operating system and available to your program. You can access these variables as an array of strings from your command line, much like the command arguments themselves. Listing 6.6 reworks listing 6.5 to present a sorted list of the environment variables.
Listing 6.6 Sorting the Environment Variables
1: //Listing 6.6 - Sorting the Environment Variables 2: 3: #include <iostream.h> 4: #include <string.h> 5: #include "string.hpp" 6: 7: typedef unsigned long ULONG; 8: 9: // **************** String Node class ************ 10: class Node 11: { 12: public: 13: Node (String*); 14: Node (); 15: ~Node(); 16: void InsertAfter(Node*); 17: Node * GetNext() { return itsNext; } 18: const Node * GetNext() const { return itsNext; } 19: void SetNext(Node * next) { itsNext = next; } 20: String* GetString() const; 21: String*& GetString(); 22: 23: private: 24: String *itsString; 25: Node * itsNext; 26: }; 27: 28: // **************** String List ************ 29: class StringList 30: { 31: public: 32: StringList(); 33: ~StringList(); 34: ULONG GetCount() const { return itsCount; } 35: void Insert(String *); 36: void Iterate(void (String::*f)()const) ; 37: String* operator[](ULONG) const ; 38: String*& operator[](ULONG) ; 39: 40: private: 41: Node itsHead; 42: ULONG itsCount; 43: }; 44: 45: // *** node implementations **** 46: 47: Node::Node(String* pString): 48: itsString(pString), 49: itsNext(0) 50: {} 51: 52: Node::Node(): 53: itsString(0), 54: itsNext(0) 55: {} 56: 57: 58: Node::~Node() 59: { 60: delete itsString; 61: itsString = 0; 62: delete itsNext; 63: itsNext = 0; 64: } 65: 66: void Node::InsertAfter(Node* newNode) 67: { 68: newNode->SetNext(itsNext); 69: itsNext=newNode; 70: } 71: 72: String * Node::GetString() const 73: { 74: if (itsString) 75: return itsString; 76: else 77: return NULL; //error 78: } 79: 80: String*& Node::GetString() 81: { 82: return itsString; 83: } 84: 85: // Implementations for Lists... 86: 87: StringList::StringList(): 88: itsCount(0), 89: itsHead(0) // initialize head node to have no String 90: {} 91: 92: StringList::~StringList() 93: { 94: } 95: 96: String * StringList::operator[](ULONG offSet) const 97: { 98: const Node* pNode = itsHead.GetNext(); 99: 100: if (offSet+1 > itsCount) 101: return NULL; // error 102: 103: for (ULONG i=0;i<offSet; i++) 104: pNode = pNode->GetNext(); 105: 106: return pNode->GetString(); 107: } 108: 109: String*& StringList::operator[](ULONG offSet) 110: { 111: 112: if (offSet+1 > itsCount) 113: { 114: Node* NewNode = new Node; 115: for (Node *pNode = &itsHead;;pNode = pNode->GetNext()) 116: { 117: if (pNode->GetNext() == NULL ) //|| *(pNode->GetNext()) < *NewNode) 118: { 119: pNode->InsertAfter(NewNode); 120: itsCount++; 121: return NewNode->GetString(); 122: } 123: } 124: } 125: Node *pNode = itsHead.GetNext(); 126: for (ULONG i=0;i<offSet; i++) 127: pNode = pNode->GetNext(); 128: return pNode->GetString(); 129: } 130: 131: void StringList::Insert(String* pString) 132: { 133: Node * NewNode = new Node(pString); 134: itsCount++; 135: for (Node * pNode = &itsHead;;pNode = pNode->GetNext()) 136: { 137: if (pNode->GetNext() == NULL ) //|| *(pNode->GetNext()) < *NewNode) 138: { 139: pNode->InsertAfter(NewNode); 140: break; 141: } 142: } 143: } 144: 145: void StringList::Iterate(void (String::*func)()const) 146: { 147: for (Node* pNode = itsHead.GetNext(); 148: pNode; 149: pNode=pNode->GetNext() 150: ) 151: (pNode->GetString()->*func)(); 152: 153: } 154: 155: // function prototypes 156: void QuickSort(StringList&, int, int); 157: void Swap(String*& i, String*& j); 158: int main(int argc, char **argv, char ** env); 159: 160: 161: // driver program 162: int main(int argc, char **argv, char ** env) 163: { 164: StringList buffer; 165: for (int i = 0; env[i] != NULL; i++) 166: buffer[i] = new String(env[i]); 167: int NumEnv = i; 168: 169: QuickSort(buffer,0,NumEnv); 170: 171: for (i = 0; i < NumEnv-1; i++) 172: cout << *buffer[i] << "\n"; 173: cout << *buffer[NumEnv-1] << endl; 174: 175: return 0; 176: } 177: 178: void QuickSort(StringList& Input, int left, int right) 179: { 180: if (right > left) 181: { 182: int i = left-1; 183: int x = right; 184: for (;;) 185: { 186: while (*Input[++i] < *Input[right-1]) 187: ; 188: while (*Input[--x] > *Input[right-1]) 189: ; 190: 191: if (i >= x) 192: break; 193: Swap(Input[i], Input[x]); 194: } 195: Swap(Input[i], Input[right-1]); 196: QuickSort(Input,left,i); 197: QuickSort(Input,i+1,right); 198: } 199: } 200: 201: void Swap(String*& i, String*& j) 202: { 203: String* temp; 204: temp = j; 205: j = i; 206: i = temp; 207: }
Output:
d:\>Set CONFIG=Normal CFG_COMPILER=MSC8 COMSPEC=C:\NDOS.COM CMDLINE=C:\AUTOEXEC.BAT PROMPT=$p$g PATH=C:\BIN NU=d:\nu PROCOMM=\comm\pro\ MOUSE=C:\MSMOUSE TEMP=c:\temp TMP=c:\temp EPSPATH=d:\eps65 ESESSION=d:\eps65\EPSILON.SES d:\>0606 CFG_COMPILER=MSC8 CMDLINE=proj0004 COMSPEC=C:\NDOS.COM CONFIG=Normal EPSPATH=d:\eps65 ESESSION=d:\eps65\EPSILON.SES MOUSE=C:\MSMOUSE NU=d:\nu PATH=C:\BIN PROCOMM=\comm\pro\ PROMPT=$p$g TEMP=c:\temp TMP=c:\temp
Analysis:
Listing 6.6 is similar to listing 6.5; however, this time it is the environment variables that are sorted. The output reflects an abridged environment set, first printed as it is output by DOS, and then reprinted using the program listed in listing 6.6.
In line 164, a StringList object is created, and it is filled by iterating through all the strings provided by the third parameter to main(). Because there is no equivalent to argc for the environment parameters, the for loop terminates when a Null string is found. The number of strings is recorded in the local variable NumEnv, and this number is passed into QuickSort() in line 169, and then used again when printing the list in lines 171 through 173.
It is possible to search the environment for a single variable. DOS, UNIX, and the ANSI-proposed standard all call for the getenv() function, which returns a pointer to the environment variable or NULL if the variable doesn't exist.
Listing 6.7 Using getenv()
1: // Listing 6.7 - Using getenv() 2: 3: #include <ctype.h> 4: #include <stdio.h> 5: #include <stdlib.h> 6: #include <string.h> 7: #include "string.hpp" 8: #include <dos.h> 9: 10: 11: typedef unsigned long ULONG; 12: 13: int main() 14: { 15: char *ptr; 16: char buffer[100]; 17: 18: cout << "What do you want to search for? "; 19: cin.getline(buffer,100); 20: 21: for (int i=0; i<strlen(buffer); i++) 22: buffer[i] = toupper(buffer[i]); 23: 24: ptr = getenv(buffer); 25: 26: if (ptr) 27: { 28: String found(ptr); 29: cout << "found: " << found << endl; 30: } 31: else 32: cout << "Not found. Try again. \n" << endl; 33: 34: return 0; 35: }
Output:
What do you want to search for? include found: y:\h;y:\rsrc;d:\bc4\include What do you want to search for? abc Not found. Try again.
Analysis:
The user is prompted for a string in line 18. The string is forced to all uppercase letters in lines 21 and 22 to match the environment variables on my machine. In line 24, the string is passed to getenv() and the result is assigned to the pointer ptr.
In line 26, the results are tested. If they are not null, a String object is created and displayed; otherwise, a message is printed.
It is possible to change the value of an environment variable once you have a pointer to it. Listing 6.8 demonstrates getting, changing, and displaying an environment variable.
Listing 6.8 Using putenv
1: #include <ctype.h> 2: #include <stdio.h> 3: #include <stdlib.h> 4: #include <string.h> 5: #include <dos.h> 6: #include <iostreams.h> 7: int main() 8: { 9: char *ptr; 10: char buffer[100]; 11: char buff2[100]; 12: 13: cout << "What do you want to search for? "; 14: cin.getline(buffer,100); 15: for (int i=0; i<strlen(buffer); i++) 16: buffer[i] = toupper(buffer[i]); 17: ptr = getenv(buffer); 18: if (ptr) 19: { 20: cout << "found: " << ptr << endl; 21: cout << "What do you want to set it to? " ; 22: cin.getline(buff2,100); 23: for (int i=0; i<strlen(buff2); i++) 24: buff2[i] = toupper(buff2[i]); 25: strcat(buffer,"="); 26: strcat(buffer,buff2); 27: putenv(buffer); 28: cout << "Displaying..." << endl; 29: while (_environ[i]) 30: cout << _environ[i++] << endl; 31: } 32: else 33: cout << "Not found. Try again. \n" << endl; 34: 35: return 0; 36: }
Output:
SET FUN=programming What do you want to search for? fun found: programming What do you want to set it to? sleeping Displaying... FUN=SLEEPING
Analysis:
This version dispenses with the String class altogether and uses old-fashioned, C-style strings. In line 13, the user is prompted for a string to search the environment for. If it is found, it is displayed in line 20 and the user is prompted for a replacement in line 21.
The original string, with the name of the environment variables, has an equal sign added to it in line 25 and the new value added in line 26, and that is inserted into the environment in line 27. The result is displayed in line 28.
Today you learned how to access the command-line parameters to your program in DOS and UNIX, and how to access the environment variables. These parameters and variables are text strings, and can be sorted, combined, and tested using normal text-manipulation functions.
There are two ways to access the environment variables in DOS: from the array of C-style strings passed to main(), and from the global array of C-style strings accessed using getenv().
Q: Why is the argument list presented as two variables, the count (argc) and the vector (argv), but the environment is presented as only the vector (env), which is terminated with a null pointer?
A: The C programming language originally used just argc and argv, with no concept of the environment. The environment vector was added years later, after the C programming community had much more experience with vectors in general. By this time, it was known that a null-terminated list is easier to deal with than a count and a list. However, it was too late to go back and change all the existing programs, so argc and argv weren't changed. C++ inherits this behavior from C.
Q: : Why is it easier to use the name of the program rather than flags in UNIX than it is in DOS?
A: It is easier for the user, because there is one less thing to type. In UNIX, there is no real cost to using this approach, because you can create "links" to the file. These make it appear as if the executable file appears several times, under different names, but in reality it is stored only once.
Q: If I pass in a sentence as an argument, how can I prevent each word in the sentence from being considered to be a separate argument? I just want the whole sentence to be a single argument.
A: Surround the sentence in quotation marks.
Q: Are there command-line arguments in Windows?
A: There are, but it is awkward to access them, so most Windows programs don't require their use. If you invoke a program using File Run, you have an opportunity to add parameters, and you can add parameters to the command line associated with an icon via File Properties. Finally, if you run something that is not executable, but its extension is listed in the [Extensions] portion of your WIN.INI file, the executable that is associated with that extension is run, and the file name you originally selected is passed in as an argument.
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you have learned. Try to answer the quiz and exercise questions before checking the answers in Appendix A, and make sure that you understand the answers before continuing to the next chapter.
1: #include <ctype.h> 2: #include <stdio.h> 3: #include <stdlib.h> 4: #include <string.h> 5: #include <dos.h> 6: #include <iostream.h> 7: int main() 8: { 9: char *ptr; 10: char buffer[100]; 11: char buff2[100]; 12: 13: cout << "What do you want to search for? "; 14: cin.getline(buffer,100); 15: for (int i=0; i<strlen(buffer); i++) 16: buffer[i] = toupper(buffer[i]); 17: ptr = getenv(buffer); 18: if (ptr) 19: { 20: cout << "found: " << ptr << endl; 21: cout << "What do you want to set it to? " ; 22: cin.getline(buff2,100); 23: for (int i=0; i<strlen(buff2); i++) 24: buff2[i] = toupper(buff2[i]); 25: strcat(ptr,"="); 26: strcat(ptr,buff2); 27: putenv(ptr); 28: cout << "Displaying..." << endl; 29: while (_environ[i]) 30: cout << _environ[i++] << endl; 31: } 32: else 33: cout << "Not found. Try again. \n" << endl; 34: 35: return 0; 36: }
Go to: Table of Contents | Next Page