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.
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.
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.
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