newbie question: whats wrong with my code?

D

Daniel Rudy

At about the time of 3/24/2007 4:50 AM, Army1987 stated the following:
What if INT_MAX were 32767?
Decimal constants are interpreted as int before being cast to anything else
(size_t in this case).
Maybe you could use (size_t)65536L...

On entering only one filename as a command-line parameter, I'd expect being
told which the syntax is, or being asked only for the output filename.

This was coded on the fly.
If for some insane reason I've redirected the output to a file, I won't see
the error message 'till I open the output file.
I'd use fputs("error: input filename too long\n", stderr);

I usually do fprintf for that...good point to consider.
Why don't you like perror("error: unable to allocate buffer space");?

Force of habit.
You meant feof(infile)?

You know, I missed that. I wonder why it still worked then?

--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
E

Eric Sosman

Doug said:
Hi Eric,

Just for laughs, can you tell us more about that episode?

(Let me begin by confessing to a little white lie: It wasn't
fclose() whose failure went undetected, but the POSIX close()
function; this part of the application used POSIX I/O. The lie
is harmless, though, because the C I/O facilities would have
failed in exactly the same way, and an undetected failure would
have had the same consequences. I'll describe what happened in
terms of C's I/O to avoid dwelling on POSIX too much.)

The situation was very much as Richard Tobin described.
The application was a document management system that loaded a
document file into memory, applied the user's edits to the in-
memory copy, and then wrote everything to a new file when told
to save the edits. It also maintained a one-level "old version"
backup for safety's sake: the Save operation wrote to a temp
file, and then if that was successful it deleted the old backup,
renamed the old document file to the backup name, and renamed the
temp file to the document. bak -> trash, doc -> bak, tmp -> doc.

The write-to-temp-file step checked almost everything. The
fopen(), obviously, but also all the fwrite()s and even a final
fflush() were checked for error indications -- but the fclose()
was not. And on one system it happened that the last few disk
blocks weren't actually allocated until fclose() -- the I/O
system sat atop VMS' lower-level file access machinery, and a
little bit of asynchrony was inherent in the arrangement.

The customer's system had disk quotas enabled, and the
victim was right up close to his limit. He opened a document,
edited for a while, saved his work thus far, and exceeded his
quota -- which went undetected because the error didn't appear
until the unchecked fclose(). Thinking that the save succeeded,
the application discarded the old backup, renamed the original
document to become the backup, and renamed the truncated temp
file to be the new document. The user worked a little longer
and saved again -- same thing, except you'll note that this time
the only surviving complete file got deleted, and both the
backup and the master document file are truncated. Result: the
whole document file became trash, not just the latest session
of work but everything that had gone before.

As Murphy would have it, the victim was the boss of the
department that had purchased several hundred licenses for our
software, and I got the privilege of flying to St. Louis to be
thrown to the lions.
And just for my education, can you tell me more about failures to
check the retcode from fclose()? If I detect a bad retcode what
should I do, etc.? Pointers to the FAQ humbly received.

In this case, the failure of fclose() would (if detected) have
stopped the delete-and-rename sequence. The user would have been
told "Hey, there was a problem saving the document; do something
about it and try again. Meanwhile, nothing has changed on disk."
Even if he'd been unable to save his latest batch of work, he would
at least not have lost everything that went before.
 
B

Bill Pursell

At about the time of 3/24/2007 4:50 AM, Army1987 stated the following:

You know, I missed that. I wonder why it still worked then?

Because feof != 0 is always evaluating to true. I'm surprised
that I'm having a very diffictult time getting gcc to
emit a warning for this. The following code compiles cleanly
with as many warning options that I can find in the documentation.

#include <stdio.h>
int
main(void)
{
int *x;

if( x == 0 ) {
;
}
if( feof != 0) {
;
}
return 0;
}
 
F

Flash Gordon

Army1987 wrote, On 24/03/07 11:50:
What if INT_MAX were 32767?

Not a problem.
Decimal constants are interpreted as int

No, they are only of type int if they fit in to an int.
> before being cast to anything else
(size_t in this case).

A cast is an explicit operation, one which Daniel did not use.
Maybe you could use (size_t)65536L...

No point since what Daniel did was fine.
There is no guarantee this will be output before waiting for input. You
want to flush stdout here.
On entering only one filename as a command-line parameter, I'd expect being
told which the syntax is, or being asked only for the output filename.

<snip>

Its worse than that, if the first filename was too long then the rest of
it will be grabbed as the second, so you will see the prompt but not get
a chance to enter anything at it.
 
A

Army1987

Daniel Rudy said:
You know, I missed that. I wonder why it still worked then?

Good question... What is supposed to happen if I use the name of a function
as an expression?
AFAIK C doesn't have namespaces (except for structure/union tags), so one
can't have a function called feof() and a variable called feof in the same
program...
 
R

Richard Heathfield

Army1987 said:
Good question... What is supposed to happen if I use the name of a
function as an expression?

You get a pointer to that function. Since the feof() function exists, a
pointer to it is non-null, so the comparison with 0 yields "unequal".
 
A

Army1987

Flash Gordon said:
Army1987 wrote, On 24/03/07 11:50:

Not a problem.


No, they are only of type int if they fit in to an int.
I seem to remember having read about someone including a very large decimal
constant (bigger than INT_MAX but less than LONG_MAX) as an operand in an
expression where it would have automatically converted to a long (e.g k = l
+ 2000000000 where k and l are declared as long), and getting a wrong result
because the compiler was trying to read 2000000000 as an int and only then
to convert it to a long; the suggested solution was writing 2000000000L
directly in the source.

If I remember where I read that I'll quote that...
 
B

Barry Schwarz

At about the time of 3/24/2007 3:23 AM, Daniel Rudy stated the following:
At about the time of 3/23/2007 11:17 AM, ericunfuk stated the following:

This is a complete version of what you are trying to do. As for the
regulars, critiques are welcome. :)



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


#define BUFFSIZE 65536 /* buffer size */
#define FILENAMESIZE 1024 /* max size of filenames */


int main(int argc, char **argv)
{
size_t readsize; /* size of data read */
size_t writesize; /* size of data written */
int eof_flag; /* flag if eof encountered */
FILE *infile; /* file pointer for input file */
FILE *outfile; /* file pointer for output file */
void *buffer; /* pointer to buffer space */
char infilename[FILENAMESIZE]; /* input filename */
char outfilename[FILENAMESIZE]; /* output filename */



/* get filenames */
if (argc != 3)
{
printf("Enter path/filename to copy -->");
fgets(infilename, sizeof(infilename), stdin);

If the user's input is less than 1024 characters, you have left the
'\n' in the file name. This will usually cause fopen() to fail.
printf("Enter destination path/filename -->");
fgets(outfilename, sizeof(outfilename), stdin);
}
else
{
if (strlen(argv[1]) > sizeof(infilename) - 2)

Why -2? If strlen() returns 5 and sizeof evaluates to 6, there is
room for the name plus the '\0'.
{
printf("error: input filename too long\n");
exit(EXIT_FAILURE);
}
if (strlen(argv[2]) > sizeof(outfilename) - 2)
{
printf("error: output filename too long\n");
exit(EXIT_FAILURE);
}
strncpy(infilename, argv[1], sizeof(infilename) - 2);

Since you know it will fit at this point, why not strcpy, which will
include the terminal '\0', eliminating the need for the next
statement?
infilename[sizeof(infilename) - 1] = '\0';
strncpy(outfilename, argv[2], sizeof(outfilename) - 2);
outfilename[sizeof(outfilename) - 1] = '\0';
}

/* allocate buffer space */
buffer = malloc(BUFFSIZE);
if (buffer == NULL)
{
printf("error: unable to allocate buffer space: %s\n", strerror(errno));

Does malloc() set errno? Is it required to? Shouldn't you clear
errno before the call?
exit(EXIT_FAILURE);
}

/* open input and output files */
infile = fopen(infilename, "rb");
if (infile == NULL)
{
printf("error: unable to open input file %s: %s\n", infilename, strerror(errno));

Does fopen() set errno?
free(buffer);
exit(EXIT_FAILURE);
}
outfile = fopen(outfilename, "wb");
if (outfile == NULL)
{
printf("error: unable to open output file %s: %s\n", outfilename, strerror(errno));
free(buffer);
fclose(infile);
exit(EXIT_FAILURE);
}

/* copy input file to output file in a loop */
eof_flag = 0;
while (eof_flag == 0)
{

/* read input file into buffer */
readsize = fread(buffer, 1, BUFFSIZE, infile);
if (readsize < BUFFSIZE) /* can only be caused by a eof or error */
{
if (feof != 0) eof_flag = 1;
feof(infile)

else
{
printf("error: error reading file: %s\n", strerror(errno));

Does fread set errno? Can feof clear it?
fpurge(outfile);

What is fpurge()? Did you mean fflush()?
fclose(infile);
fclose(outfile);
remove(outfilename);

It is a little presumptuous of you to decide that the partial output
is useless and should be deleted. What if the user would like to know
where the error occurred or can still make use of the data that was
copied?

And if you decide to leave the file for his analysis, you should also
write however much data was read with the last fread().
free(buffer);
exit(EXIT_FAILURE);
}
}

/* write contents of buffer to output file */
writesize = fwrite(buffer, 1, readsize, outfile);
if (writesize < readsize) /* can only be caused by an error */
{
printf("error: error writing to file: %s\n", strerror(errno));

Does fwrite() set errno?
fpurge(outfile);
fclose(infile);
fclose(outfile);
remove(outfilename);
free(buffer);
exit(EXIT_FAILURE);
}
}

/* clean up */
free(buffer);
fflush(outfile);
fclose(infile);
if (fclose(outfile) == EOF)
{
printf("error: problem closing output file: %s\n", strerror(errno));

Does fclose() set errno?

Why do you leave the output file in the error case but in none of the
others?
exit(EXIT_FAILURE);
}


/* return to operating system */
exit(EXIT_SUCCESS);
}


As you can see, about half the code is dealing with errors. I
didn't use perror though like you did.


Remove del for email
 
P

pete

Army1987 said:
I seem to remember having read about someone
including a very large decimal
constant (bigger than INT_MAX but less than LONG_MAX)
as an operand in an
expression where it would have automatically
converted to a long
(e.g k = l + 2000000000 where k and l are declared as long),
and getting a wrong result because the compiler was
trying to read 2000000000 as an int and only then
to convert it to a long; the suggested solution was writing
2000000000L directly in the source.

If I remember where I read that I'll quote that...

N869
6.4.4.1 Integer constants

[#5] The type of an integer constant is the first of the
corresponding list in which its value can be represented.

Suffix Decimal Constant
 
D

Doug

As Murphy would have it, the victim was the boss of the
department that had purchased several hundred licenses for our
software, and I got the privilege of flying to St. Louis to be
thrown to the lions.

Cheers, Eric. Priceless. Did you have good diagnostics in place, or
was it a 'savage bug hunt'?

<OT>Anyone know of a decent place with similar horror stories? The
Dialy WTF isn't bad. Any others? I'm bored while waiting for my
compiler.</OT>

Doug
 
G

gw7rib

I also would
appreciate if anyone could explain further the following code segment
from Chris's reply, I thought it would be really complicated if you
use fgetc, because you need to write it to the file you are writing to
separately?

char mychar;
/* ... */

while( (mychar=fgetc(file)) != EOF ) {
/* do things with mychar */

}

One of the strange things about the C language is that lots of things
give an "answer", even if that answer is not used. For example, printf
is a function which returns an int, showing the number of characters
written. So you can say:

x = printf("Hello, world!\n");

and this will set x to the value of 14. Usually however the value
returned from printf is just thrown away, for example:

printf("Hello, world!\n");

... the value is unused and just gets lost.

Now, if you do:

mychar=fgetc(file);

then this reads in a value from a file and sets mychar to it (or to
EOF if there are no more characters to be read). But this expression
itself has a value - the same as the value that mychar is set to - and
this value is again being thrown away. You could, if you wanted, do:

x = mychar=fgetc(file);

and this would set x to the same value as mychar. But, more usefully,
you can use the value as the control for a "while" statement. So when
you do:

while( (mychar=fgetc(file)) != EOF ) {
/* do things with mychar */
}

it will read in a character and set mychar to its value. It will then
test whether mychar is EOF. If it is, the program moves on to after
the loop. Otherwise, it will execute the instructions in the loop (ie
the instructions that you have put in the place marked "/* do things
with mychar */" and the value that you want to use is stored in the
mychar variable. After this it will go back and read in another
character.

If instead you were to do:

while( fgetc(file) != EOF ) {

then you would read in a character and compare it to EOF, but if it
isn't the same as EOF you have no way of finding out what it was.
Assigning the value to a variable in the while statement lets you keep
hold of the value as well as comparing it.

Hope this makes sense.
Paul.
 
D

Daniel Rudy

Considering how many replies that I received on this, I decided
to consolidate them and the updated source code file into one
post. The updated program is at the bottom. Critiques are
welcome. :) This has been quite a learning exercise for me.



At about the time of 3/24/2007 4:34 AM, Bill Pursell stated the following:
Why not just use BUFSIZ from stdio.h?

I prefer to be in control of stuff like that.
Hmmm, why not declare buffer as a char * or unsigned char *?

Does it really matter? The data is not going to be manipulated
by me. It's just read in and write out.
printf("error: input filename too long\n");

This is an error message. It should probably go to stderr rather
than stdout.
Fixed.

Why make copies of the filenames? Why not just declare infilename
as a char * and have "infilename = argv[1];" Also, since
you've already checked the lengths of the filenames, you know
that strncpy is going to write the null at the end, so
there's no need to do it.

Because, the filenames can be acquired from either the command line
or from prompting. Somehow, somewhere, there is going to be copying.
As for strncpy(3), the man page states the following:

The strncpy() function copies at most len characters from src into dst.
If src is less than len characters long, the remainder of dst is filled
with `\0' characters. Otherwise, dst is not terminated.
feof shuould probably be called. eg feof(infile)

Fixed. Gcc did not give a warning about that. It seems other people are
having a similar problem...
Overall, the main function is way too long, and I don't like
seeing the error checking stuff strewn throughout. It's hard
to see the flow of the program.

Agreed. Fixed...somewhat...That's why I have extra newlines between
blocks of code, and comments thoughout.

******************************

At about the time of 3/24/2007 4:50 AM, Army1987 stated the following:
What if INT_MAX were 32767?
Decimal constants are interpreted as int before being cast to anything else
(size_t in this case).
Maybe you could use (size_t)65536L...

Huh? (size_t)65536L Forgive my ignorance, but what does the L at the end
of the constant do?

As for the INT_MAX issue, it's not a problem because I'm not using int
datatype, but I addressed it anyways.
On entering only one filename as a command-line parameter, I'd expect being
told which the syntax is, or being asked only for the output filename.
Fixed.

If for some insane reason I've redirected the output to a file, I won't see
the error message 'till I open the output file.
I'd use fputs("error: input filename too long\n", stderr);

Fixed, I used fprintf instead. (See above)
Why don't you like perror("error: unable to allocate buffer space");?

I never used it. And some of the error messages includes other data
too, like the filename in error or something. Force of habit.
You meant feof(infile)?

Fixed. (See Above)


******************************

At about the time of 3/24/2007 6:28 AM, Flash Gordon stated the following:
Army1987 wrote, On 24/03/07 11:50:

There is no guarantee this will be output before waiting for input. You
want to flush stdout here.

Although I have never had a problem with this...

Fixed.
<snip>

Its worse than that, if the first filename was too long then the rest of
it will be grabbed as the second, so you will see the prompt but not get
a chance to enter anything at it.

Fixed...I think.

******************************

At about the time of 3/24/2007 9:04 AM, Barry Schwarz stated the following:
if (strlen(argv[1]) > sizeof(infilename) - 2)

Why -2? If strlen() returns 5 and sizeof evaluates to 6, there is
room for the name plus the '\0'.

Call it paranoia about security. I didn't want to take the chance of
having a off-by-one error. Better to be sure now than fixing a
security problem later.
strncpy(infilename, argv[1], sizeof(infilename) - 2);

Since you know it will fit at this point, why not strcpy, which will
include the terminal '\0', eliminating the need for the next
statement?

Security Paranoia.
Does malloc() set errno? Is it required to? Shouldn't you clear
errno before the call?

Yes, and No.
feof(infile)

Fixed. (See Above)
Does fread set errno? Can feof clear it?

As far as I know, fread sets errno on error. As for feof, the man page
states the following:

DESCRIPTION
The function clearerr() clears the end-of-file and error indicators for
the stream pointed to by stream.

The function feof() tests the end-of-file indicator for the stream
pointed to by stream, returning non-zero if it is set. The end-of-file
indicator can only be cleared by the function clearerr().

The function ferror() tests the error indicator for the stream pointed to
by stream, returning non-zero if it is set. The error indicator can only
be reset by the clearerr() function.

ERRORS
These functions should not fail and do not set the external variable
errno.

Does that answer your question?
What is fpurge()? Did you mean fflush()?

Yes, I did mean fpurge(). It's standard C. This goes along with...
It is a little presumptuous of you to decide that the partial output
is useless and should be deleted. What if the user would like to know
where the error occurred or can still make use of the data that was
copied?

And if you decide to leave the file for his analysis, you should also
write however much data was read with the last fread().

And that is why I was using fpurge() instead of fflush(). You bring up
an interesting point. This issue has been addressed.

Fixed.
Does fwrite() set errno?

As far as I know, fwrite sets errno on error.
Why do you leave the output file in the error case but in none of the
others?

Oversight.

Fixed.

******************************

Updated program source:

copy.c:



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>


#define BUFFSIZE 65536 /* buffer size */
#if BUFFSIZE > INT_MAX
#define BUFFSIZE INT_MAX
#endif
#define FILENAMESIZE 1024 /* max size of filenames */




int copyfile(const char *infilename, const char *outfilename);
void cleanup(FILE *infile, FILE *outfile, void *buffer);

int main(int argc, char **argv)
{
int i; /* generic variable */
char infilename[FILENAMESIZE]; /* input filename */
char outfilename[FILENAMESIZE]; /* output filename */

/* get filenames */
if (argc != 3)
{
switch(argc)
{
case 1:
printf("Enter path/filename to copy -->");
fflush(stdout);
fpurge(stdin);
fgets(infilename, sizeof(infilename) - 2, stdin);
case 2:
printf("Enter destination path/filename -->");
fflush(stdout);
fpurge(stdin);
fgets(outfilename, sizeof(outfilename) - 2, stdin);
break;
default:
fprintf(stderr, "usage: copy <input file> <output file>");
exit(EXIT_FAILURE);
break;
}
}
else
{
if (strlen(argv[1]) > sizeof(infilename) - 2)
{
fprintf(stderr, "error: input filename too long\n");
exit(EXIT_FAILURE);
}
if (strlen(argv[2]) > sizeof(outfilename) - 2)
{
fprintf(stderr, "error: output filename too long\n");
exit(EXIT_FAILURE);
}
strncpy(infilename, argv[1], sizeof(infilename) - 2);
strncpy(outfilename, argv[2], sizeof(outfilename) - 2);
}

/* to be sure for security reasons. probably not
needed though. force of habbit... */
infilename[sizeof(infilename) - 1] = '\0';
outfilename[sizeof(outfilename) - 1] = '\0';

/* strip newline char off of strings */
i = strlen(infilename) - 1;
if (infilename == '\n') infilename = '\0';
i = strlen(outfilename) - 1;
if (outfilename == '\n') outfilename = '\0';


/* do copy */
i = copyfile(infilename, outfilename);
if (i != 0) exit(EXIT_FAILURE);


/* return to operating system */
exit(EXIT_SUCCESS);
}

int copyfile(const char *infilename, const char *outfilename)
{
size_t readsize; /* size of data read */
size_t writesize; /* size of data written */
int eof_flag; /* flag if eof encountered */
int err_flag; /* flag if error encountered */
FILE *infile; /* file pointer for input file */
FILE *outfile; /* file pointer for output file */
void *buffer; /* pointer to buffer space */


/* allocate buffer space */
buffer = malloc(BUFFSIZE);
if (buffer == NULL)
{
fprintf(stderr, "error: unable to allocate buffer space: %s\n",
strerror(errno));
return(1);
}

/* open input and output files */
infile = fopen(infilename, "rb");
if (infile == NULL)
{
fprintf(stderr, "error: unable to open input file %s: %s\n",
infilename, strerror(errno));
free(buffer);
return(1);
}
outfile = fopen(outfilename, "wb");
if (outfile == NULL)
{
fprintf(stderr, "error: unable to open output file %s: %s\n",
outfilename, strerror(errno));
free(buffer);
fclose(infile);
return(1);
}

/* copy input file to output file in a loop */
eof_flag = 0;
err_flag = 0;
while (eof_flag == 0)
{

/* read input file into buffer */
readsize = fread(buffer, 1, BUFFSIZE, infile);
if (readsize < BUFFSIZE) /* can only be caused by a eof or error */
{
if (feof(infile) != 0) eof_flag = 1;
else
{
fprintf(stderr, "error: error reading file: %s\n",
strerror(errno));
if (readsize > 0) err_flag = 1;
}
}

/* write contents of buffer to output file */
writesize = fwrite(buffer, 1, readsize, outfile);
if (writesize < readsize) /* can only be caused by an error */
{
fprintf(stderr, "error: error writing to file: %s\n",
strerror(errno));
err_flag = 1;
}
if (err_flag == 1)
{
cleanup(infile, outfile, buffer);
return(1);
}
}

/* clean up */
cleanup(infile, outfile, buffer);

return(0);
}

void cleanup(FILE *infile, FILE *outfile, void *buffer)
{
if (fflush(outfile) == EOF)
{
fprintf(stderr, "error: problem flushing output file: %s\n",
strerror(errno));
}
fclose(infile);
if (fclose(outfile) == EOF)
{
fprintf(stderr, "error: problem closing output file: %s\n",
strerror(errno));
}
free(buffer);
return;
}


--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
A

Army1987

pete said:
I seem to remember having read about someone
including a very large decimal
constant (bigger than INT_MAX but less than LONG_MAX)
as an operand in an
expression where it would have automatically
converted to a long
(e.g k = l + 2000000000 where k and l are declared as long),
and getting a wrong result because the compiler was
trying to read 2000000000 as an int and only then
to convert it to a long; the suggested solution was writing
2000000000L directly in the source.

If I remember where I read that I'll quote that...

N869
6.4.4.1 Integer constants

[#5] The type of an integer constant is the first of the
corresponding list in which its value can be represented.

Suffix Decimal Constant

I guess I got confused... Probably that problem was about unsigned int, not
about long int, and the suggested suffix was U. Sorry.
 
A

Army1987

Daniel Rudy said:
Huh? (size_t)65536L Forgive my ignorance, but what does the L at the end
of the constant do?

It tells the compiler to consider 65536 a long int constant.
Anyway it is not needed. Decimal constants are considered in the first type
among int, long int, and long long int in which they fit.
I got confused with the U suffix, needed to consider 40000U an unsigned int
rather than a long int (in case INT_MAX < 40000).
Nevermind.
Yes, I did mean fpurge(). It's standard C. This goes along with...

What does it do? I can't find it in my copy of n1124.pdf...
 
G

Guest

Army1987 said:
What does it do? I can't find it in my copy of n1124.pdf...

It's a BSD extension to clear the buffers (discarding the contents) of
an input or output stream. It's absolutely not standard C.
 
B

Barry Schwarz

snip
As for strncpy(3), the man page states the following:

The strncpy() function copies at most len characters from src into dst.
If src is less than len characters long, the remainder of dst is filled
with `\0' characters. Otherwise, dst is not terminated.

snip
if (strlen(argv[1]) > sizeof(infilename) - 2)

Why -2? If strlen() returns 5 and sizeof evaluates to 6, there is
room for the name plus the '\0'.

Call it paranoia about security. I didn't want to take the chance of
having a off-by-one error. Better to be sure now than fixing a
security problem later.
strncpy(infilename, argv[1], sizeof(infilename) - 2);

Since you know it will fit at this point, why not strcpy, which will
include the terminal '\0', eliminating the need for the next
statement?

Security Paranoia.
Does malloc() set errno? Is it required to? Shouldn't you clear
errno before the call?

Yes, and No.

No and yes actually. According to n1124:

Setting errno by malloc is neither required nor prohibited. It
may on your system but portable code cannot depend on it. (Portable
also include the next update to your library.)

Library functions are allowed to set errno even if no error
occurs.

No library function is allowed to reset errno.

Footnote 172 recommends setting errno to 0 before calling a
function if you intend to use the value after the function returns.

A little paranoia in a programmer is a good thing. But you are acting
inconsistently. You refuse to accept the documented behavior of
strncpy (propagating '\0' to fill the destination array) but are quite
willing to accept undocumented properties about errno (who sets it and
how long the values persist). Hobgoblins of small minds not
withstanding, consistency in a programmer is a very good thing.

snip
As far as I know, fread sets errno on error. As for feof, the man page
states the following:

Not according to n1124. It is neither required nor prohibited.
Consequently, portable code cannot depend on it.
DESCRIPTION
The function clearerr() clears the end-of-file and error indicators for
the stream pointed to by stream.

The function feof() tests the end-of-file indicator for the stream
pointed to by stream, returning non-zero if it is set. The end-of-file
indicator can only be cleared by the function clearerr().

If your man page says this then it is wrong. n1124 does not state
"only" which is good because we know that fseek will also clear the
end-of-file indicator.

Unfortunately, this has nothing to do with my question.

While feof cannot clear errno (see previous comment), it is allowed to
set it to a non-zero value. Depending on the value from fread still
being present after feof is called is non-portable.
The function ferror() tests the error indicator for the stream pointed to
by stream, returning non-zero if it is set. The error indicator can only
be reset by the clearerr() function.

The error indicator and errno are two completely separate topics.
Again, n1124 does not say "only".
ERRORS
These functions should not fail and do not set the external variable
errno.

This statement is not part of the standard. Furthermore, the standard
specifically states that errno need not be an external variable.

From your context, I cannot tell what "These functions" refer to. If
it is only ferror and feof (seems likely given the context) then it is
probably true but also irrelevant.
Does that answer your question?

Not portably.
Yes, I did mean fpurge(). It's standard C. This goes along with...

There is no fpurge in n1124. It may be posix (since you reference man
pages) but it is not standard.

snip
******************************

Updated program source:

copy.c:



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>


#define BUFFSIZE 65536 /* buffer size */
#if BUFFSIZE > INT_MAX
#define BUFFSIZE INT_MAX
#endif

The #if block is unnecessary. If you intend to keep it, you probably
need to insert a #undef before the #define.
#define FILENAMESIZE 1024 /* max size of filenames */




int copyfile(const char *infilename, const char *outfilename);
void cleanup(FILE *infile, FILE *outfile, void *buffer);

int main(int argc, char **argv)
{
int i; /* generic variable */
char infilename[FILENAMESIZE]; /* input filename */
char outfilename[FILENAMESIZE]; /* output filename */

/* get filenames */
if (argc != 3)
{
switch(argc)
{
case 1:
printf("Enter path/filename to copy -->");
fflush(stdout);
fpurge(stdin);

No such standard function.

snip
}

int copyfile(const char *infilename, const char *outfilename)
{
snip

}

void cleanup(FILE *infile, FILE *outfile, void *buffer)
{
if (fflush(outfile) == EOF)
{
fprintf(stderr, "error: problem flushing output file: %s\n",
strerror(errno));

You call cleanup for normal end of input and for errors on either
stream. If the error was on outfile, this fflush will probably also
fail and you will have two error messages for the same condition.

Slightly redundant since fclose(outfile) will automatically flush the
stream.
fclose(infile);
if (fclose(outfile) == EOF)
{
fprintf(stderr, "error: problem closing output file: %s\n",
strerror(errno));
}
free(buffer);
return;
}


Remove del for email
 
C

Chris Torek

I seem to remember having read about someone including a very large decimal
constant (bigger than INT_MAX but less than LONG_MAX) as an operand in an
expression where it would have automatically converted to a long (e.g k = l
+ 2000000000 where k and l are declared as long), and getting a wrong result
because the compiler was trying to read 2000000000 as an int and only then
to convert it to a long; the suggested solution was writing 2000000000L
directly in the source.

If I remember where I read that I'll quote that...

C89 and C99 differ here. Unsuffixed decimal constants in C99 have
type "int", "long", or "long long", whichever is the first one that
makes the value fit. Unsuffixed decimal constants in C89 have type
"int", "unsigned int" [I think], "long", or "unsigned long",
whichever is the first one that makes the value fit.

Practically speaking, that means that, in C89, on your typical
32-bit machine (i.e., something only 10 years old or so, or running
in compatibility mode) ... well, try the following program:

#include <stdio.h>

int main(void) {
if (-1 > 3000000000)
puts("must be c89");
else
puts("could be c99");
return 0;
}

I note that gcc with -std=c99 gets this wrong, at least with gcc
3.2.3:

% cc -std=c99 -pedantic -W -Wall -O4 -o t t.c
t.c: In function `main':
t.c:4: warning: decimal constant is so large that it is unsigned
t.c:4: warning: comparison between signed and unsigned
%

The warning is correct (and behavior is accurate) for C89, but not
for C99, where 3000000000 should have type "long long".
 
C

Chris Torek

Yes, I did mean fpurge(). It's standard C.

fpurge() is not Standard C (as others have noted). It is my
invention: I put it into 4.4BSD, because people keep claiming they
want a "discard data" behavior, and it did seem as though stdio
should probably have functionality similar to the "discard data"
ioctl() calls.

Unlike the non-Standard "fflush on stdin", fpurge() works in *both*
directions, which means that you have a chance to throw out
un*written* data as well as un*read* data. Like the non-Standard
"fflush on stdin", fpurge is non-Standard. :)
 
P

pete

Chris Torek wrote:
C89 and C99 differ here. Unsuffixed decimal constants in C99 have
type "int", "long", or "long long", whichever is the first one that
makes the value fit. Unsuffixed decimal constants in C89 have type
"int", "unsigned int" [I think], "long", or "unsigned long",
whichever is the first one that makes the value fit.

ISO/IEC 9899: 1990
6.1.3.2 Integer constants

Semantics

The type of an integer constant is the first of the corresponding
list in which its value can be represented.
Unsuffixed decimal:
int, long int, unsigned long int;
unsuffixed octal or hexadecimal:
int, unsigned int, long int, unsigned long int;
suffixed by the letter u or U:
unsigned int, unsigned long int;
suffixed by the letter l or L:
long int, unsigned long int;
suffixed by both the letters u or U and l or L:
unsigned long int.
 
C

Christopher Benson-Manica

Doug said:
Cheers, Eric. Priceless. Did you have good diagnostics in place, or
was it a 'savage bug hunt'?

Often (and unfortunately), the fact that the bug existed means that no
one thought about diagnostics either :)
 

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

Similar Threads

RSA implementation issues in public key pem loader function 0
error 28
code 34
fread/fwrite 2 18
Whats wrong with my stream ? 4
question 33
Can not read VCD file in Linux 8
file bug 28

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top