Changing the way arguments are read from program

I have the following piece of code. Currently the command line arguments are passed as shown below using the "= "sign. I capture the name of the argument, for example vmod and it's corresponding user parameter which is jcdint-z30.cmd.

./raytrac vmod=jcdint-z30.cmd srFile=jcdint.sr

Now I want to be able to calling raytrac using

./raytrac --vmod jcdint-z30.cmd --srFile jcdint.sr
void  Parsing::ParseCmd(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  poseq;

  String  S;
  String  Name;
  String  File;
  String  Ends;

  List<int>  Ord; Ord += 0;

  ifstream  ifs;

  for (i = 1; i < argc; i++) {

      S = String(argv);
      if (S[0] == '?') {
          File = S.ToEnd(1);
          ifs.open((char *)File);
          if (ifs.bad()) { error("Error including file"); }
          ParseFile(ifs);
          ifs.close();
      } else {
          Search(S, "=", poseq);
          Name = ToUpper( S.Substr(0, poseq-1) );
          Ends = S.ToEnd( poseq + 1 );
          Index += ParseEl(Name, Ends, Ord);
      }

  }

  Ptr = 0;

}

There's nothing in there which would prevent that. The problem's in the rest of the program I presume.

I have changed the code as follows. Any suggestions would be appreciated.
I want to check if the -- is in the front though so then I can take the name. How can I do this?

void  Parsing::ParseCmd(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  k;
  int  poseq;
  int  skipFlag;

  String  S;
  String  Name;
  String  File;
  String  Ends;

  ifstream  ifs;

  List<int>  Ord;
  Ord += 0;

  skipFlag = 0;
  for (i = 1; i < argc; i++) {

      if (skipFlag == 1) {
        skipFlag = 0;
        continue;                                     // Skips the iteration
      }

      S = String(argv);
      if (S[0] == '?') {
          File = S.ToEnd(1);
          ifs.open((char *)File);
          if (ifs.bad()) { error("Error including file"); }
          ParseFile(ifs);
          ifs.close();
      } else {
          Search(S, "--", poseq);
          Name = ToUpper( S.ToEnd( poseq + 2 ) );
          Ends = String(argv[i+1]);
          Index += ParseEl(Name, Ends, Ord);
          skipFlag = 1;
          cout << Name << " "<< Ends << " " << poseq << "\n";
      }

  }

  Ptr = 0;

}

Sorry for misunderstanding the original question.

#include <string.h>

...

if(strncmp("--", argv, 2)==0)
{
...
}

Ok, so I now have the following two functions and want to integrate them together so that the user can pass arguments in two different ways:

--vmod=jcdint.cmd

or

--vmod  jcdint.cmd 
void  Parsing::ParseCmd(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  poseq;

  String  S;
  String  Name;
  String  File;
  String  Ends;

  List<int>  Ord; Ord += 0;

  ifstream  ifs;

  for (i = 1; i < argc; i++) {

      S = String(argv);
      if (S[0] == '?') {
          File = S.ToEnd(1);
          ifs.open((char *)File);
          if (ifs.bad()) { error("Error including file"); }
          ParseFile(ifs);
          ifs.close();
      } else {
          Search(S, "=", poseq);
          Name = ToUpper( S.Substr(0, poseq-1) );
          Ends = S.ToEnd( poseq + 1 );
          Index += ParseEl(Name, Ends, Ord);
      }

  }

  Ptr = 0;

}

////////////////////////////////////////////////////////////////////////////////////////////////////

// Reading arguments from the command line

void  Parsing::ParseCmdLine(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  k;
  int  poseq;
  int  skipFlag;
  int  StrSize;

  String  S;
  String  Name;
  String  File;
  String  Ends;
  String  ErrMsg;

  ifstream  ifs;

  List<int>  Ord;
  Ord += 0;

  skipFlag = 0;
  for (i = 1; i < argc; i++) {

      if (skipFlag == 1) {
        skipFlag = 0;
        continue;                                     // Skips the iteration
      }

      S = String(argv);
      StrSize = S.size();
      if (S[0] == '?') {
          File = S.ToEnd(1);
          ifs.open((char *)File);
          if (ifs.bad()) { error("Error including file"); }
          ParseFile(ifs);
          ifs.close();
      } else {
          Search(S, "--", poseq);
          cout << poseq << "\n";
          if (poseq == 0) { Name = ToUpper( S.ToEnd( poseq + 2 ) );
          } else { error("Bad command line argument: " + S);
          }

          Ends = String(argv[i+1]);
          Index += ParseEl(Name, Ends, Ord);
          skipFlag = 1;
          cout << Name << " "<< Ends << " " << poseq << "\n";
      }

  }

  Ptr = 0;

}

I've had a hard time helping you because haven't documented your code at all, you use all kinds of things you never declared anywhere, and use lots of functions which aren't standard STL. I have to guess at what the meaning of all of these is. I figured out your ToEnd function eventually, but many things remain mysterious. To that end, I'm forced to rewrite it instead of modifying it.

You can simplify this a lot, anyway. There's no reason to keep converting most of those things to String and back when all you do is convert them back to 'char *'. [] works just as well on char *'s as it does on strings too, and far faster.

You can avoid SkipFlag. Increment the loop right now if you need to skip something, and you won't need to remember later.

Most of your variables make much more sense to declare where you're using them, instead of in one big lump at the top of your function. It took me a while to realize that each code block uses separate, independent variables here, even though they all just use the value immediately...

You can avoid the close() by declaring ifs inside the code block, too. When you do that, it auto-closes whenever it goes out of scope. This will also let you get rid of the open(), because you can feed the file name straight into the constructor.

#include <string.h>

...

void Parsing::ParseCMD(int argc, char *argv[])
{
        List<int>  Ord; Ord += 0;
        int n;

        for(n=1; n<argc; n++)
        {
                if(argv[n][0] == '?') // "?abcd"[0] is '?'.
                {
                        ifstream ifs(argv[n]+1); // "?abcd"+1 is "abcd", so open filename "abcd".
                        if(!ifs.good()) error("Error including file");
                        else    ParseFile(ifs);
                } // end of code block.  ifs closes here.
                else if(strncmp(argv[n], "--", 2) == 0) // compare first 2 chars.  Will be 0 if they match.
                {
                        // strchr("--asdf=b", '=') is "=b".  If not found, it is NULL instead.
                        const char *After=strchr(argv[n], '='), *Name;
                        string Ends; // Make it a 'string' so we can use your ToUpper function

                        if(After == NULL)
                        {
                                n++; // Get the next argument
                                // If there is no next argument, quit instead of crashing!
                                if(n == argc)
                                {
                                        error("out of arguments");
                                        continue;
                                }

                                Name=argv[n];
                        }
                        else                    Name=After+1; // "=b"+1 is "b"

                        Ends=ToUpper(Name);

                        Index += ParseEl(Name, Ends, Ord);
                }
        }
}
1 Like

Thanks a lot. I did not include a lot of things as the program is quite large.

I understand, but you chopped out a bit too much, and forgot to explain any of your logic...

I am getting back to this but I am confused about something. The problem concerns the variable Name.

String Ends = ToUpper(Name);

As ToUpper uses my defines type called String.

That's the only thing I used your string type for, yes -- converting to uppercase.

Everything else is just (original pointer + characters offset), but that actually needed to change the string, so I used your String. I could've written my own version of a toupper, but you'd have to worry about freeing it later, so it was simpler to use yours.

What was the question?

The routine I have got now is this one. However, it does not seem to work. Something is going wrong but cannot figure it out yet.

void  Parsing::ParseCmdLine2(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  StrSize;                        // Size of string
  int  EndsSize;                       // Size of string

  List<int>  Ord;
  Ord += 0;

  cout << "ParseCmdLine2 argv" << endl;
  cout << " i = " << i << ", argv = " << argv << endl;
  cout << "argc = " << argc << endl;

  for (i = 1; i < argc; i++) {

//      S = String(argv);
//      StrSize = S.size();
      cout << "i = " << i << endl;

      // First character is '?', e.g. "?abcd"[0] is '?'.
      if (argv[0] == '?') {
      cout << "Check ?" << endl;

          ifstream ifs(argv+1);     // "?abcd"+1 is "abcd", so open filename "abcd".
          if ( ifs.bad() ) error("Error including file " + String(argv) );
          else ParseFile(ifs);

      }                                // end of code block. ifs closes here.

      // First 2 chars are '-'. A zero value indicates that the characters compared in both
      // strings are all equal.
      else if (strncmp(argv, "--", 2) == 0)
      {

      cout << "Check -- long option" << endl;
          // Pointer to first occurrence of '='
          // strchr("--asdf=b", '=') is "=b". If '=' is not found, function returns null pointer.
          const char* After = strchr(argv, '=');

          const char* Name;
//          String Ends;                 // Make it a 'String' so we can use ToUpper.

          // '=' is not found. Get user input from next argument.
          if (After == NULL)
          {
            i++;                       // ++i is more efficient than i++.
            if (i == argc) {           // If next argument not found, quit instead of crashing.
              error("Out of arguments");
              continue;
            }
            Name = argv;            // Set user input

          // '=' is found.
          } else {
            Name = After + 1;          // Set user input. "=file"+1 is "file".
          }
          String Ends = ToUpper(String(Name)); // Conversion of 'Name' to a 'String' so we can use ToUpper.
          cout << "Name = " << Name << ", Ends = " << Ends << endl;
          Index += ParseEl(Name, Ends, Ord);
      }

  }

  Ptr = 0;

}


---------- Post updated at 06:19 PM ---------- Previous update was at 06:13 PM ----------

I think I am making some progress. Name should have the string to the left of '=' whereas Ends should have the string to the right of '='.

The following is the original code:

void  Parsing::ParseCmd(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  poseq;

  String  S;
  String  Name;
  String  File;
  String  Ends;

  List<int>  Ord; Ord += 0;

  ifstream  ifs;

  for (i = 1; i < argc; i++) {

      S = String(argv);
      if (S[0] == '?') {
          File = S.ToEnd(1);
          ifs.open((char *)File);
          if (ifs.bad()) { error("Error including file"); }
          ParseFile(ifs);
          ifs.close();
      } else {
          Search(S, "=", poseq);
          Name = ToUpper( S.Substr(0, poseq-1) );
          Ends = S.ToEnd( poseq + 1 );
          Index += ParseEl(Name, Ends, Ord);
      }

  }

  Ptr = 0;

}

---------- Post updated at 06:34 PM ---------- Previous update was at 06:19 PM ----------

We are not capturing the Name.

If the user parses vmod=jcdint-z30.cmd, I want to have Name=VMOD and Ends=jcdint-z30.cmd

---------- Post updated at 07:02 PM ---------- Previous update was at 06:34 PM ----------

Some more progress. I now have the code

void  Parsing::ParseCmdLine2(
  const int  argc,
  char*  argv[]) {

  int  i;
  int  StrSize;                        // Size of string
  int  EndsSize;                       // Size of string

  List<int>  Ord;
  Ord += 0;

  cout << "ParseCmdLine2 argv" << endl;
  cout << " i = " << i << ", argv = " << argv << endl;
  cout << "argc = " << argc << endl;

  for (i = 1; i < argc; i++) {

//      S = String(argv);
//      StrSize = S.size();
      cout << "i = " << i << endl;
      cout << "argv = " << argv << endl;
      cout << "strncmp = " << strncmp(argv, "--", 2) << endl;

      // First character is '?', e.g. "?abcd"[0] is '?'.
      if (argv[0] == '?') {
      cout << "Check ?" << endl;

          ifstream ifs(argv+1);     // "?abcd"+1 is "abcd", so open filename "abcd".
          if ( ifs.bad() ) error("Error including file " + String(argv) );
          else ParseFile(ifs);

      }                                // end of code block. ifs closes here.

      // First 2 chars are '-'. A zero value indicates that the characters compared in both
      // strings are all equal.
      else if (strncmp(argv, "--", 2) == 0)
      {

          cout << "Check long option" << endl;
          // Pointer to first occurrence of '='
          // strchr("--asdf=b", '=') is "=b". If '=' is not found, function returns null pointer.
          const char* After = strchr(argv, '=');
          const char* optName;
          const char* Ends;
          String Name;                 // Make it a 'String' so we can use ToUpper.

          // '=' is not found. Get user input from next argument.
          if (After == NULL)
          {
            i++;                       // ++i is more efficient than i++.
            if (i == argc) {           // If next argument not found, quit instead of crashing.
              error("Out of arguments");
              continue;
            }
            Ends = argv;            // Set user input

          // '=' is found.
          } else {
            Ends = After + 1;          // Set user input. "=file"+1 is "file".
          }
            Name = "VMOD";
//          String optName = ToUpper(); // Conversion of 'Name' to a 'String' so we can use ToUpper.
          cout << "Name = " << Name << ", Ends = " << Ends << endl << endl;
//          Index += ParseEl(Name, Ends, Ord);
      }

  }

  Ptr = 0;

}

After passing

/home/chrisd/tatsh/trunk/hstmy/bin/prog/raytrac --vmod=jcdint-z30.cmd --srfile=jcdint.sr --rcfile=jcdint.rc --phases=P --level=twopt --format="X T" --dtau=0.1 mdacc=0.3 --mindist=0.03 --maxitertp=25 --ray=jcdint-z30.ry --out=jcdint-z30.xt --vrb=medium | & tee jcdint-z30-raytrac.log

I get the following out put

ParseCmdLine2 argv
 i = 0, argv = /home/chrisd/tatsh/trunk/hstmy/bin/prog/raytrac
argc = 14
i = 1
argv = --vmod=jcdint-z30.cmd
strncmp = 0
Check long option
Name = VMOD, Ends = jcdint-z30.cmd

i = 2
argv = --srfile=jcdint.sr
strncmp = 0
Check long option
Name = VMOD, Ends = jcdint.sr

i = 3
argv = --rcfile=jcdint.rc
strncmp = 0
Check long option
Name = VMOD, Ends = jcdint.rc

i = 4
argv = --phases=P
strncmp = 0
Check long option
Name = VMOD, Ends = P

i = 5
argv = --level=twopt
strncmp = 0
Check long option
Name = VMOD, Ends = twopt

i = 6
argv = --format=X T
strncmp = 0
Check long option
Name = VMOD, Ends = X T

i = 7
argv = --dtau=0.1
strncmp = 0
Check long option
Name = VMOD, Ends = 0.1

i = 8
argv = mdacc=0.3
strncmp = 1
i = 9
argv = --mindist=0.03
strncmp = 0
Check long option
Name = VMOD, Ends = 0.03

i = 10
argv = --maxitertp=25
strncmp = 0
Check long option
Name = VMOD, Ends = 25

i = 11
argv = --ray=jcdint-z30.ry
strncmp = 0
Check long option
Name = VMOD, Ends = jcdint-z30.ry

i = 12
argv = --out=jcdint-z30.xt
strncmp = 0
Check long option
Name = VMOD, Ends = jcdint-z30.xt

i = 13
argv = --vrb=medium
strncmp = 0
Check long option
Name = VMOD, Ends = medium

What I need to do is get the Name to be VMOD, SRFILE, RCFILE, ...

Hello, I am new to the forum.

I don't know where you got the idea i++ is more efficient than ++i. The operation is identical -- the only difference is the order.

I have absolutely no way to test this code because of the heavy use of variables and classes you never defined anywhere. Therefore, without you telling me in what way it's going wrong, it's extremely hard to tell what it is doing.

How about this:

#include <ctype.h> // for standard C toupper() function.  toupper('a') returns 'A'

// Reads 'var' part from 'var=whatever' in input, into VAR in 'buf'
void setname(char *buf, const char *input)
{
        char *e;
        strcpy(buf, input);
        e=strchr(buf, "=");
        if(e) (*e)='\0';

        e=buf;

        while(*e) { // Convert to uppercase, char by char
                (*e)=toupper(*e);
                e++;
        }   
}

...

void  Parsing::ParseCmdLine2(
  const int  argc,
  char*  argv[]) {
...
        char Namebuf[512];
...

        setname(Namebuf, argv); // string class no longer needed!
...