tags:

views:

533

answers:

6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 1000

int getline(char *s, int lim)
{
    int i = 0;

    while(i < lim - 1 && (*s = getchar()) != EOF && *s++ != '\n')
       i++;

    if(*s == '\n')
      *s++ = '\n', i++;

    *s = '\0';
    return i;
}
int main(int argc, char *argv[])
{   

     char line[MAXLINE];
     long lineno = 0;
     int c, except = 0, number = 0, found = 0;

     while(--argc > 0 && (*++argv)[0] == '-')
        while(c = *++argv[0])
          switch(c) {
             case 'x':
                  except = 1;
                  break;
             case 'n':
                  number = 1;
                  break;
             default:
                  printf("find: illegal option %c\n", c);
                  argc = 0;
                  found = -1;
                  break;
          }

     if(argc != 1)
         printf("Usage: find -x -n pattern\n");
     else
         while(getline(line, MAXLINE) > 0) {
             lineno++;
             if((strstr(line, *argv) != NULL) != except) {
                   if(number)
                      printf("%ld:", lineno);
                   printf("%s", line);
                   found++;
             }
         }

     printf("Found: %d", found);
     return found;
}

while(--argc > 0 && (*++argv)[0] == '-')

Does this expression in the parantheses (*++argv)[0] differs from while(c = *++argv[0]) without parantheses?

If so, in what? Does (*++argv) means pointer to the next argument, and *++argv[0] means pointer to the next character in the current char array which is being pointed at?

+5  A: 

yes, you are correct.

while(--argc > 0 && (*++argv)[0] == '-')

is scanning the array (of length argc) of command line arguments one by one looking for those starting with a - option prefix. For each of those:

while(c = *++argv[0])

is scanning through the set of switch characters that follow the first - in the current argument (i.e. t and n in -tn, until it hits the string null terminator \0, which terminates the while loop, since it evaluates as false.

This design allows both

myApp -t -n

and

myApp -tn

to both work and be understood as having the options t and n.

Alex Brown
This design is simple and mostly reasonable, apart from the fact it modifies argc, and the contents of the array argv, which is poor design since it prevents any further use of these variables.
Alex Brown
+5  A: 

Incrementing argv is a very bad idea, as once you have done so it is difficult to get the original value back. It is simpler, clearer and better to use an integer index - after all argv IS an array!

To answer your question ++argv increments the pointer. This then has indirection applied to it to get the first character.

anon
Actually, the indirection starts with the first character after the -, and each cycle it moves onto the next, to support clusters of option flags after a single - character.
Alex Brown
I was referring to (*++argv)[0] == '-'
anon
+2  A: 

Yes, the two expressions differ (though only slightly). IMO, this code is a bit on the excessively clever side. You'd be better off with something like this:

for (int i=1; i<argc; i++)
    if (argv[i][0] == '-') {
       size_t len = strlen(argv[i]);
       for (int j=0; j<len; ++j)
           switch(argv[i][j]) {
               case 'x':
               // ...

This is pretty much equivalent to the code above, but I doubt anybody (who knows C at all) would have any difficulty figuring out what it really does.

Jerry Coffin
but this code would not detect chains of options - you need another iterator to walk the chain of options -tn
Alex Brown
@Alex Brown: I believe I've fixed that -- though I'm not sure it's necessarily any real improvement. Allowing `-tn` instead of `-t -n` would have meant a fair amount when a typical terminal was a Teletype, but it's hardly worthwhile anymore.
Jerry Coffin
@Jerry It's entirely worthwhile. Every command-line user expects to be able to provide single-letter options in a group, and being lazy about the code means violating those strongly held expectations.The deeper issue here is the custom-coding of this functionality, rather than the use of getopt or similar.
Novelocrat
@Novelocrat: I'm afraid I can't really agree -- quite a few command line tools either don't allow clustered arguments at all, or have specific limitations about what arguments can and can't be clustered. Nobody with any substantial amount of experience can honestly have much expectation about this subject. Given that it only supports two arguments, neither with any associated parameter, I can see where using getopt would probably make the code more complex, so I can understand not using it, even though I agree that it *probably* should anyway.
Jerry Coffin
you try taking tar -xvzf a.tar.gz away from me and see what happens. or ls -laTr, or ps -elF, etc, etc.
Alex Brown
+2  A: 

The parentheses change the order in which the expressions are evaluated.

Without parentheses *++argv[0]:

  1. argv[0] gets the pointer to character data currently pointed to by argv.
  2. ++ increments that pointer to the next character in the character array.
  3. * gets the character.

with parentheses (*++argv)[0]:

  1. ++argv increments the argv pointer to point to the next argument.
  2. * defereferences it to obtain a pointer to the character data.
  3. [0] gets the first character in the character array.
Dave Cluderay
+9  A: 
Alok
Wow, really thanks for the effort. This really helped.
Tool
Very nice explanation.
Matt Jordan
+1 for pretty pictures.
dreamlax
A: 

I did find a 'bug' in this program tho, entering myprogram.exe -xxxxnnnnn in the command line will actually work(as intended lol?). Should i fix K&R's version about this?

Tool
While that's not pretty, I wouldn't say that's a bug, as the value of except and number are still their expected values regardless of the number of 'x' or 'n' passed in.
Matt Jordan
yea, i guess the code would be too complicated and long to accept only -x and -n or -xn or -nx.
Tool
This is the way everything else behaves too: `ls -ll` doesn't complain either.
Alok