Exercise 7-1

M

mdh

I am not sure how relevant to C this is, but here goes.
The exercise is to write a simple program "upper" that is invoked by
this name. The program itself is pretty easy...even for me. :)
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, const char * argv[]) {
int c;
if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/
upper")==0)
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
else
putchar(tolower(c));
return 0;
}

<<<<<<

But, my question is this?
When I type "/.....upper" immediately followed by an argument, the
program does not work.
If I type "/...upper" RETURN, then enter the arguments, it works.
Any reason? I have asked on Xcode forum, but no answer yet?
If this is OT, then I apologize.
 
K

Keith Thompson

mdh said:
I am not sure how relevant to C this is, but here goes.
The exercise is to write a simple program "upper" that is invoked by
this name. The program itself is pretty easy...even for me. :)
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, const char * argv[]) {
int c;
if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/
upper")==0)
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
else
putchar(tolower(c));
return 0;
}

<<<<<<

But, my question is this?
When I type "/.....upper" immediately followed by an argument, the
program does not work.
If I type "/...upper" RETURN, then enter the arguments, it works.
Any reason? I have asked on Xcode forum, but no answer yet?
If this is OT, then I apologize.

Having the program silently do nothing if argv[0] doesn't have a
specific value is odd, but if that's what you want it to do, that's
ok.

Your indentation is inconsistent and doesn't correspond either to the
actual structure of your code or to the intended structure of your
code.

Here's your program after it's been filtered through "indent -kr" (and
after I manually joined the split line):

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(int argc, const char *argv[])
{
int c;
if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/upper") == 0)
while ((c = getchar()) != EOF)
putchar(toupper(c));
else
putchar(tolower(c));
return 0;
}

This should be enough to tell you what the problem is. It's also a
good argument for using braces for all structured statements, whether
they're required or not. For example, I always write:

if (condition) {
statement;
}

rather than

if (condition)
statement;

It makes this kind of error slightly more difficult to make.
 
M

mdh

Your indentation is inconsistent and doesn't correspond either to the
actual structure of your code or to the intended structure of your
code.

Sorry about the formatting.I will have to figure that out sometime.
Here's your program after it's been filtered through "indent -kr" (and
after I manually joined the split line):

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    int c;
    if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/upper") == 0)
        while ((c = getchar()) != EOF)
            putchar(toupper(c));
    else
        putchar(tolower(c));
    return 0;

}

This should be enough to tell you what the problem is.

The solution, except for the omission in error of a second "while ((c
= getchar()) != EOF)" is from Tondo and Gimpel. I think it is really
simple to illustrate the issue that K&R are trying to make, instead of
making it bullet proof?


while ((c = getchar()) != EOF){
putchar(toupper(c));
}
else {
while ((c = getchar()) != EOF)
putchar(tolower(c)); }

<<<<<

But, have I missed something. Is that the reason one needs a 2 step
invocation of this program?
 
V

vippstar

I am not sure how relevant to C this is, but here goes.

It's a C exercise from a C book, how more relevant can it be?
The exercise is to write a simple program "upper" that is invoked by
this name. The program itself is pretty easy...even for me. :)

You seem to be having problems with it though...
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, const char * argv[]) {

argv is char *argv[] or char **argv, etc.
Drop `const'.
int c;
if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/
upper")==0)

argv[argc] is always NULL. argc may be 0.
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
else
putchar(tolower(c));

`else' is reached if strcmp returns a non-zero value.
`c' is not initialized, thus you are invoking undefined behavior when
you use its value.
return 0;

}

<<<<<<

But, my question is this?
When I type "/.....upper" immediately followed by an argument, the
program does not work.

Because there are several errors in your code.
If I type "/...upper" RETURN, then enter the arguments, it works.
Any reason? I have asked on Xcode forum, but no answer yet?
If this is OT, then I apologize.

Because you read from stdin, you don't do anything with the arguments.
Here's a complete program that does what you want.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(void) {
int (*f[2])(int) = { toupper, tolower }, c, i;

if(argc == 0) return EXIT_FAILURE;
i = !!strcmp(argv[0], "upper");
while((c = getchar()) != EOF) putchar(f(c));
if(ferror(stdin)) return EXIT_FAILURE;
return 0;
}
 
K

Keith Thompson

mdh said:
Your indentation is inconsistent and doesn't correspond either to the
actual structure of your code or to the intended structure of your
code.

Sorry about the formatting.I will have to figure that out sometime.
Here's your program after it's been filtered through "indent -kr" (and
after I manually joined the split line):

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    int c;
    if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/upper") == 0)
        while ((c = getchar()) != EOF)
            putchar(toupper(c));
    else
        putchar(tolower(c));
    return 0;

}

This should be enough to tell you what the problem is.

The solution, except for the omission in error of a second "while ((c
= getchar()) != EOF)" is from Tondo and Gimpel.

That would be "The C Answer Book". If you're going to cite books
other than K&R, please be specific; I had to look that up.
I think it is really
simple to illustrate the issue that K&R are trying to make, instead of
making it bullet proof?



while ((c = getchar()) != EOF){
putchar(toupper(c));
}
else {
while ((c = getchar()) != EOF)
putchar(tolower(c)); }

<<<<<

But, have I missed something. Is that the reason one needs a 2 step
invocation of this program?

(Does the subject "Exercise 7-1" refer to K&R? My copy isn't handy.)

I'm afraid I can't tell from your response whether you understood my
point or not. In the code you've marked with ">>>>>" and "<<<<<", you
have an "else" with no corresponding "if". If that's a fragment of
Tondo and Gimpel's solution, it's a poorly chosen fragment.

If I understand correct, the program is supposed to convert its input
to upper case if it's invoked as "upper", and to lower case otherwise.

The problem is that, due to the way you've structured the code, if
it's invoked as "upper" (or rather as "/Users/.../upper") it uses a
while loop to read each character from stdin, otherwise it reads only
a single character.

There are two obvious ways to do what you're trying to do.

In pseudo-code, with structure shown only by indentation, you can do
either:

if my name is "upper"
for each input character
print that character in upper case
else
for each input character
print that character in lower case

or:

for each input character
if my name is "upper"
print that character in upper case
else
print that character in lower case

What you've done is an incorrect mixture of the two approaches.
 
R

Ron Ford

That would be "The C Answer Book". If you're going to cite books
other than K&R, please be specific; I had to look that up.

I've got Tondo and Gimpel at hand. I can't imagine that it would trump
Keith if the exercise were "do io in contemporary standard C."

#include <stdio.h>
#include <ctype.h>

#define MAXLINE 100
#define OCTLEN 6

int main ( int vipp, ** char star)
{
// berkeley bracing ala dan pop
int c, pos;
int inc(int pos, int n);

//main control
pos = 0;
while((c = getchar()) != EOF)
if (iscntrl(c) || c==' '
{
// non-graphic or blank
pos = inc(pos, OCTLEN);
printf =(" \\%03o ", c);
// sic

// newline ?
if (c == '\n')
{

pos = 0;
putchar('\n');

}

}


// brace mismatch alert

else
{

pos = inc( pos, 1);
putchar(c);

}


return 0;

// an external function; this belongs up top

int inc(int pos, int n)
{
if (pos + n < MAXLINE)
return pos + n;
else
{
putchar('\n');
return n;
}


}


Anyways, I like to type in the middle of the night. This is a classic

and outdated soultion. The T&C solns don't hold up together, with this

an example of monstrous msicalculation.

I don't have a C compiler online other than Richard right now.

}
 
M

mdh

That would be "The C Answer Book".  If you're going to cite books
other than K&R, please be specific; I had to look that up.


Sorry about that. I assumed that Tondo and Gimpel and K&R were like a
"horse and carriage" and that everyone just knew about them.(Their
book is promoed on the back cover of my K&R) I have previously
alluded to them using the acronym "T&G" and it had not been
questioned...just goes to show where assumptions will get you! It
really has helped me up to now getting through those exercises.

(Does the subject "Exercise 7-1" refer to K&R?  My copy isn't handy.)

Yes it does..

"Write a program that converts upper case to lower case or lower case
to upper, depending upon the name that it is invoked with, as found in
argv[0]."
I'm afraid I can't tell from your response whether you understood my
point or not.  In the code you've marked with ">>>>>" and "<<<<<", you
have an "else" with no corresponding "if".  If that's a fragment of
Tondo and Gimpel's solution, it's a poorly chosen fragment.

It's a poorly chosen fragment.
My question was specifically directed to the problem of needing to
invoke the program ....which in my case means typing the path name and
then RETURN...then typing something in lower case, then RETURN
converts this to upper case.
As I thought the issue was not really related to the code that was
written, I did not really give due diligence to the issues you were
pointing out...which was my mistake, and which I will try and correct
below.

The problem is that, due to the way you've structured the code, if
it's invoked as "upper" (or rather as "/Users/.../upper") it uses a
while loop to read each character from stdin, otherwise it reads only
a single character.

There are two obvious ways to do what you're trying to do.



What you've done is an incorrect mixture of the two approaches.

Your point is taken. Let me then accept your code, but having done
this, does it answer the query that I am puzzled about?
And for completeness, I include, the **hopefully** full code from T&G
as well. ( I have added braces as per your suggestion )

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, char * argv[]) {
int c;
if (strcmp(argv[0], "upper")==0){
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
}
else {
while (( c = getchar() ) != EOF)
putchar(tolower(c));
}
return 0;
}

<<<<<<<<
 
M

mdh

It's a C exercise from a C book, how more relevant can it be?

Well, I am not sure if it is a "C" issue or an "Xcode" issue, which is
what I use when working through the exercises. If it is an Xcode
issue, I think I would be directed to the appropriate forum ( which
exists) but having said that, I ask here to find out with certainty
which it is.
 
K

Keith Thompson

Your point is taken. Let me then accept your code, but having done
this, does it answer the query that I am puzzled about?
And for completeness, I include, the **hopefully** full code from T&G
as well. ( I have added braces as per your suggestion )

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, char * argv[]) {
int c;
if (strcmp(argv[0], "upper")==0){
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
}
else {
while (( c = getchar() ) != EOF)
putchar(tolower(c));
}
return 0;
}

<<<<<<<<

Ok, that looks reasonable, mostly. Your indentation is still
inconsistent, and I'd add braces to the while statements as well as the
if statements:

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main(int argc, char *argv[])
{
int c;
if (strcmp(argv[0], "upper") == 0) {
while ((c = getchar()) != EOF) {
putchar(toupper(c));
}
}
else {
while ((c = getchar()) != EOF) {
putchar(tolower(c));
}
}
return 0;
}

But now I've lost track of what you were asking about.

One possible issue I can see is that argv[0], if it's non-null, points
to a string that "represents the program name". The manner in which
it does so may vary. For example, on a Unix-like system, it could be
any of "./upper", "/some/long/path/upper", or just "upper", depending
on how you invoked the program. I suggest printing the value of
argv[0] to see how it's actually being set. <OT>Try both installing
the program in some directory in your $PATH and invoking it by name,
and typing the full path to the executable file, and see what
happens.</OT>

For a simple test, you can just pick a way to invoke the program and
check for whatever value of argv[0] that gives you. For more
generality, you could figure out how to detect all the possible ways
it could be invoked.
 
M

mdh

Ok, that looks reasonable, mostly.

Finally!! :)


But now I've lost track of what you were asking about.


LOL!

The question was why one invoked the program by using the "name" , in
this case lower/upper. Then on my computer, I need to hit "Return",
then type some stuff, which, after a return, converts it to upper or
lower as requested in the name.

In other words, you cannot do this...

/....whateverthepathis/lower CONVERT ME TO LOWER

but you can do this.

/....whateverthepathis/lower RETURN

**program launches**

Now type:

CONVERT ME TO LOWER

and it performs as expected. ie output is "convert me to lower"
 I suggest printing the value of
argv[0] to see how it's actually being set.


Thank you...did not think of that.
 
V

vippstar

Well, I am not sure if it is a "C" issue or an "Xcode" issue, which is
what I use when working through the exercises. If it is an Xcode
issue, I think I would be directed to the appropriate forum ( which
exists) but having said that, I ask here to find out with certainty
which it is.


It's a C exercise from a C book, how more relevant can it be?
 
K

Keith Thompson

mdh said:
The question was why one invoked the program by using the "name" , in
this case lower/upper. Then on my computer, I need to hit "Return",
then type some stuff, which, after a return, converts it to upper or
lower as requested in the name.

In other words, you cannot do this...

/....whateverthepathis/lower CONVERT ME TO LOWER

but you can do this.

/....whateverthepathis/lower RETURN

**program launches**

Now type:

CONVERT ME TO LOWER

and it performs as expected. ie output is "convert me to lower"
[...]

The program obtains its data by reading from stdin (standard input)
using getchar().

In order for the command

/....whateverthepathis/lower CONVERT ME TO LOWER

to work, it would instead have to obtain its data by looking at the
values of the strings pointed to by the argv array (the command line
arguments). In fact, the program ignores those strings, apart from
using argv[0] to determine the name of the program, so it has no way
of knowing that you typed "CONVERT ME TO LOWER" on the command line.

I'm making some assumptions here about how command-line arguments are
mapped to argv. Typically if you type

program_name CONVERT ME TO LOWER

the program will be invoked with:

argc == 5
argv[0] --> "program_name"
argv[1] --> "CONVERT"
argv[2] --> "ME"
argv[3] --> "TO"
argv[4] --> "LOWER"
argv[5] == NULL

but it might not work that way on all systems.

The bottom line is that command-line arguments and standard input are
two very different sources of data, and a program uses entirely
different mechanisms to access them.
 
M

mdh

The program obtains its data by reading from stdin (standard input)
using getchar().

In order for the command

    /....whateverthepathis/lower    CONVERT ME TO LOWER

snip


the program ignores those strings, apart from
using argv[0] to determine the name of the program, so it has no way
of knowing that you typed "CONVERT ME TO LOWER" on the command line.

I'm making some assumptions here about how command-line arguments are
mapped to argv.  Typically if you type

    program_name CONVERT ME TO LOWER

the program will be invoked with:

    argc == 5
    argv[0] --> "program_name"
    argv[1] --> "CONVERT"
    argv[2] --> "ME"
    argv[3] --> "TO"
    argv[4] --> "LOWER"
    argv[5] == NULL

but it might not work that way on all systems.

The bottom line is that command-line arguments and standard input are
two very different sources of data, and a program uses entirely
different mechanisms to access them.

Hi Keith...thank you so much for that. What you say makes perfect
sense, and moreover, it works as you suggest. Your diagram clinches
it for me.
 
B

Barry Schwarz

I am not sure how relevant to C this is, but here goes.
The exercise is to write a simple program "upper" that is invoked by
this name. The program itself is pretty easy...even for me. :)
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (int argc, const char * argv[]) {
int c;
if (strcmp(argv[0], "/Users/m/Desktop/upper/build/Release/
upper")==0)
while ( (c = getchar() ) != EOF)
putchar(toupper(c));
else
putchar(tolower(c));
return 0;
}

<<<<<<

But, my question is this?
When I type "/.....upper" immediately followed by an argument, the
program does not work.
If I type "/...upper" RETURN, then enter the arguments, it works.
Any reason? I have asked on Xcode forum, but no answer yet?
If this is OT, then I apologize.

After you fix the bad logic and undefined behavior in your code, print
out the string argv[0] points to and find out what your "shell" is
doing different between the two invocations of your program.
 
M

mdh

After you fix the bad logic and undefined behavior in your code, print
out the string argv[0] points to and find out what your "shell" is
doing different between the two invocations of your program.

No difference.
As this is the first time K&R involved I/O use, I wanted to get this
working. What I missed was the fact that getchar() reads from the
standard input, as Keith showed with his diagram.
 
L

lawrence.jones

It's a C exercise from a C book, how more relevant can it be?

It's hard to say -- some books which claim to be about C contain
exercises that have little to nothing to do with C.
 
R

Richard Bos

It's hard to say -- some books which claim to be about C contain
exercises that have little to nothing to do with C.

Well, yeah, but in this case it's not from a C book, but from the C
book: K&R 2.

Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top