|
 |
|
 |
08-03-2006, 08:55 PM
|
#1 (permalink)
|
|
Registered User
Join Date: Aug 2006
Posts: 5
|
C: Help me with a file generation program?
I began learning C about one day ago. So far, I have successfully written a program that removes files (or notifies you if you attempt to remove a file that does not exist).
I was not so lucky with my second program, however. What it mainly does is create files of a certain name. My difficulties began I tried to add an option to create a certain number of files of similar names. What I would like it to do is behave so that if the command
Code:
$ ./ifc -n 3 newfile otherfile
were issued, the following files would be created:
Code:
$ ls
newfile-1 newfile-2 newfile-3 otherfile-1 otherfile-2 otherfile-3
In reality, however, this is all that happens:
Code:
$ ./ifc -n 3 newfile
$ ls
newfile-1
gcc has no problems compiling my program. Here is the specific code block I am having trouble with:
Code:
/* This `for' loop processes each filename. */
for (i = 3; i < argc; i++) /* to skip `./ifc', `-n' and `3' cmd line args */
{
int j; /* j keeps track of how many files of each filename are created */
/* This `for' loop processes each individual file. */
for (j = 1; j <= n; j++) /* `n' is 3, because of `-n 3' args */
{
char jstr[50]; /* jstr holds the whole name of the file */
sprintf (jstr, "%s-%d", argv[i], j); /* use sprintf store the name */
FILE *file = fopen (jstr, "r"); /* make sure the file doesn't exist */
/* if fopen can find the file... */
if (file != 0) /* stop if the file is found */
{
printf ("\n%s: File exists.\n", jstr);
fclose (file);
}
else /* We're okay for creation. */
{
fclose (file);
FILE *success = fopen (jstr, "w"); /* create the file */
if (success == 0) /* in case it ends up not working */
{
printf ("\n%s: ", jstr);
printf ("Creation unsuccessful.\n");
}
}
}
}
Thank you in advance for any assistance you may be able to give.
|
|
|
08-04-2006, 06:22 AM
|
#2 (permalink)
|
|
Newbie
Join Date: Jun 2002
Location: Denmark
Posts: 1,713
|
This looks like it is something which can be handled in a POSIX environment, so I took a chance and moved the thread to " Platform/API C++"
Now you want to parse arguments to your program.. there are different ways to handle that, if you want it to be ANSI/ISO compliant, then you need to loop through argv[] from 1 to (argc -1) looking for your desired flag, and shift every following items so you'd end up with an argv[] which only holds the users file-names...
However, here comes the POSIX part in, theres a very userfriendly function called getopt() And you'd use it in a way like this:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
char opt; /* this will hold the return value from optind */
int count=1, j, i; /* as default we create one file */
while( (opt = (char)getopt(argc, argv, "n:h")) > 0)
{
switch (opt)
{
case 'n':
/* this will be the copies of filename created */
count = atoi(optarg);
break;
case 'h':
printf("Usage: %s [-n <count>] [filename filename ... filename]\n",
argv[0]);
return 0;
}
}
/* at this point the provided optind from getopt()
* will point somewhere in argv[], we need to cover
* every position it might have
*/
/* if we're in luck it is pointing at the beginning */
if(optind == 3){
/* assume the remaining arguments are "filename" qualifiers */
for(i=optind; i < argc; ++i)
for(j=1; j <= count; ++j)
printf("Creating file: %s-%d\n", argv[i], j);
}
else
if(optind == argc || optind > argc){
/* it was given as the very last argument so correct that */
for(i=1; i < (argc -2); ++i)
for(j=1; j <= count; ++j)
printf("Creating file: %s-%d\n", argv[i], j);
}
else
if(optind > 1 && optind < argc){
/* it must've been given somewhere in the mittle (worst case) */
for(i=1; i < (optind - 2); ++i)
for(j=1; j <= count; ++j)
printf("Creating file: %s-%d\n", argv[i], j);
for(i+=2; i < argc; ++i)
for(j=1; j <= count; ++j)
printf("Creating file: %s-%d\n", argv[i], j);
}
else{
/* was only called with filename */
for(i=1; i < argc; ++i)
printf("Creating file: %s\n", argv[i]);
}
return 0;
}
Now if we remain in the POSIX world, theres a access() function, which can tell you if the file exists, and a number of other things, this might lighten your load with the fopen()/fread() thingy to check for an existing file...
ie:
Code:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int i;
for(i=1; i < argc; ++i)
if(!access(argv[i], F_OK))
printf("File exist: %s\n", argv[i]);
else
printf("File dosn't exist: %s\n", argv[i]);
return 0;
}
Hope this sheds some light onto your frustration...
|
|
|
08-04-2006, 01:14 PM
|
#3 (permalink)
|
|
Registered User
Join Date: Aug 2006
Posts: 5
|
Thank you for all your hard work.
Your post helped me get rid of the design problems, but I still need assistance with actually writing the file. I understand the `sprintf' function can be used to save a string to a buffer. In that case, I would use
Code:
sprinf (outfile, "%s-%d", argv[i], j);
correct?
The problem is that I'm still having trouble grasping the concept of strings (which are actually char arrays, I believe?) in C.
I have the algorithm figured out for the most part:
Code:
<string_type> outfile;
sprintf (outfile, "%s-%d", argv[i], j);
fopen (outfile, "w");
But the problem is I don't quite know how to initialize a string. char[999] might work, but I don't want the array to be a fixed size. I tried char** (an array pointer, I believe) but that also caused problems.
|
|
|
08-04-2006, 03:08 PM
|
#4 (permalink)
|
|
Newbie
Join Date: Jun 2002
Location: Denmark
Posts: 1,713
|
If you don't want to run into a buffer problem, you need to turn towards something like dynamic memory allocation with the use of malloc()...
Now I don't want to provide you with the actual program, but in this case I would advice you to split it up, say create a seperate function, where you've actualy create the file ie:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* predefine the file-creation function, so we know it exist,
* when refferencing to it from main()
*/
int create_file(char* filename, int count);
int main(int argc, char* argv[])
{
/* this is the main function in my example with getopt()
* in this one every instance of:
* for(j=1; j < count; ++j)
* printf("Creating file: ...");
* is exchanged with:
* create_file(argv[i], count);
*/
}
/* this is the actual body of the create_file(),
* This will create a files by the name: filename-n
* for n in {1, 2, ..., count}
*/
int create_file(char* filename, int count)
{
char* buff; /* our filename with dynamic memory allocation */
/* how long will our char* need to be */
int length = strlen(filename)+3; /* add room for '-' and '\0' */
int i,n=count;
/* deside length */
while(n >= 10){
length++;
n=n/10;
}
/* allocate needed memory for filename-count */
if(!(buff = (char*) malloc(sizeof(char*)*length)))
return -1; /* error allocating memory */
for(i=1; i <= count; ++i){
snprintf(buff, length, "%s-%d", filename, i);
if(!access(buff, F_OK))
printf("Error file: %s exist\n", buff);
else
printf("Creating file: %s\n", buff);
}
free(buff); /* make sure we dont hogg unneeded memory */
return 0;
}
I hope I didn't provide you with too much help.. Since I like for you to figure out how this thing actualy should work by yourself..
Naturaly I could have said..
malloc() is used in a way like:
Code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* buff;
int length=1337;
if(!(buff = (char*) malloc(sizeof(char*)*length))){
printf("Error allocating memory on the heap\n");
return -1;
}
else
printf("Success in allocating memory on the heap\n");
free(buff);
return 0;
}
And then tell you to figure out how to use it in your program...
But then again, I wouldn't be encurraging you to make a reasonable design in your program, so you wont end up with a main() function which is thousands of lines long...
But as an exercise I've left the part of combining the example for getopt() with this small example...
|
|
|
08-04-2006, 05:14 PM
|
#5 (permalink)
|
|
Registered User
Join Date: Aug 2006
Posts: 5
|
It took a bit more thinking, but it finally works properly. Thank you so much for all of your help.
Here is my completed code:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* predefine the file-creation function so that we know it exists when
referencing from main() */
int create_file (char* filename, int count);
int main (int argc, char** argv)
{
char opt; /* this will hold the return value
from optind */
int count = 1; /* create one file by default */
int j;
int i;
while ((opt = (char) getopt (argc, argv, "n:h")) > 0)
{
switch (opt)
{
/* copies of filename created */
case 'n': count = atoi (optarg);
break;
/* help message */
case 'h': printf("Usage: %s [-n ", argv[0]);
printf("<count>] [filename1[ ...]]\n");
return 0;
}
}
/* ``at this point the provided optind from getopt() will point
somewhere in argv[], we need to cover every position it might
have'' */
/* normally, it should point from the
beginning. */
if (optind == 3)
{
/* assume remaining arguments
indicate which filename to use. */
for (i = optind; i < argc; ++i)
create_file(argv[i], count);
}
else
if (optind == argc || optind > argc)
{
/* was given as last arg in this case */
for (i = 1; i < (argc -2); ++i)
create_file(argv[i], count);
}
else {
if (optind == argc && optind < argc) {
/* it was given in the middle! */
for (i = 1; i < (argc -2); ++i)
create_file(argv[i], count);
}
else
{
/* was only called with filename */
for (i = 1; i < argc; ++i)
create_file(argv[i], count);
}
}
return 0;
}
int create_file (char* filename, int count) {
char* buff; /* our filename with dynamic memory
allocation */
/* how long `char*' will need to be; + 3
to add room for `-' and `\0' */
int length = strlen (filename) + 3;
int i;
int n = count;
while (n >= 10) { /* decide length */
length++;
n = n / 10;
}
/* allocate memory for filename-x */
if (!(buff = (char*) malloc (sizeof (char*) * length)))
return -1; /* error allocating */
for (i = 1; i <= count; ++i) {
snprintf (buff, length, "%s-%d", filename, i);
if (!access (buff, F_OK))
printf ("%s: File exists\n", buff);
else {
FILE *success = fopen (buff, "w");
if (!success)
printf ("%s: Error creating file.", buff);
else
printf ("Created file: %s\n", buff);
}
}
free (buff); /* free memory */
return 0;
}
Thanks again. I'm new at this, so there may be some conventions I am breaking. You can point those out if you'd like.
|
|
|
08-04-2006, 08:26 PM
|
#6 (permalink)
|
|
Newbie
Join Date: Jun 2002
Location: Denmark
Posts: 1,713
|
This is not a correction or anything, just a small examination to see if you know what the main function does... - Please describe to me, how a valid call of the program would be in order to catch the different checks for:
- if(optind == 3)
- if (optind == argc || optind > argc)
- if (optind > 1 && optind < argc)
- And the last else section.
Now to the correction part...
I see you've removed nearly half the searching through argv[] for the case with optind pointing in to the mittle of argv[]. If you explain the questions I've put up for you, then I know for sure, because from what I see, it seems like you're not entirely aware of what getopt()/optind/optarg does..
Which might be why you've changed my
Code:
if(optind > 1 && optind < argc){
part to
Code:
if (optind == argc && optind < argc) {
So for now I wont comment on that part of your program.
In order to make nice with the OS, you need to close the filepointers you've opened, else they wont be closed untill your app terminates, so if at some point it fails misserably youd end up with alot of unusable files, because they all have unclosed filepointers.
So you need to do somethign like:
Code:
...
else {
FILE *success = fopen (buff, "w");
if (!success)
printf ("%s: Error creating file.", buff);
else {
printf ("Created file: %s\n", buff);
fclose(success);
}
}
...
Another thing, I know this isn't exactly ANSI/ISO compliant since it uses alot of POSIX relevant extensions like getopt(), access(), snprintf(), etc.
But to make nice with the compiler in a ANSI/ISO C -way, you have to declare the variables befor assignment, which is why, for instance, I declare the type of i befor I make any reference to it, or perform any other actions for that matter..
It would be nice to declare your FILE* at the top of your create_file() function. So it would be something like:
Code:
...
int create_file (char* filename, int count) {
FILE* success;
char* buff; /* our filename with dynamic memory
allocation */
...
else {
if(!(success = fopen (buff, "w")))
printf ("%s: Error creating file.", buff);
...
If you want to figure out thesse sort of ANSI/ISO compliances, then you can tell gcc, to a strict ANSI/ISO type check with the flag -ansi, note that I did mention this isn't following the ANSI/ISO completely, so it wont compile with that flag... Mostly due to the use of getopt().
And one last thing, the description for char opt; in main() should be a refference to getopt(), not optind.. Sorry that one was partialy my mistake, I had my mind in another part of the program, when I wrote that description...
Else a nice little proggy for a 3.rd day student  So welcome to the world of C...
Last edited by redhead; 08-04-2006 at 08:47 PM.
|
|
|
08-04-2006, 09:45 PM
|
#7 (permalink)
|
|
Registered User
Join Date: Aug 2006
Posts: 5
|
Quote:
|
Originally Posted by redhead
This is not a correction or anything, just a small examination to see if you know what the main function does... - Please describe to me, how a valid call of the program would be in order to catch the different checks for:
- if(optind == 3)
- if (optind == argc || optind > argc)
- if (optind > 1 && optind < argc)
- And the last else section.
|
To be honest, I don't really understand your question. ): I believe optind is the argv[] index of the first command line argument that isn't a switch option?
Unfortunately, there is a lot about that main function I do not understand. It has become obvious to me that there is a lot more reading and studying to be done before I attempt to write any more programs with any real complexity. C is much more difficult then I thought it would be. I was able to learn HTML, PHP, and CSS with very little effort and thought C would be the same way. Apparently not....
Quote:
|
Originally Posted by redhead
Now to the correction part...
I see you've removed nearly half the searching through argv[] for the case with optind pointing in to the mittle of argv[]. If you explain the questions I've put up for you, then I know for sure, because from what I see, it seems like you're not entirely aware of what getopt()/optind/optarg does..
Which might be why you've changed my
Code:
if(optind > 1 && optind < argc){
part to
Code:
if (optind == argc && optind < argc) {
So for now I wont comment on that part of your program.
|
That was merely sloppy typing. Hoping that would memorize the program better, I retyped all of it by hand. I thought that be better then simply cutting and pasting, but it appears as though I have made a few mistakes along they way. ):
Quote:
|
Originally Posted by redhead
In order to make nice with the OS, you need to close the filepointers you've opened, else they wont be closed untill your app terminates, so if at some point it fails misserably youd end up with alot of unusable files, because they all have unclosed filepointers.
So you need to do somethign like:
Code:
...
else {
FILE *success = fopen (buff, "w");
if (!success)
printf ("%s: Error creating file.", buff);
else {
printf ("Created file: %s\n", buff);
fclose(success);
}
}
...
|
Oops! I remember that from my first program. I don't know why I forgot it this time around. Next time, I will be sure to remember to fclose() any pointers I fopen()ed. (;
Quote:
|
Originally Posted by redhead
Another thing, I know this isn't exactly ANSI/ISO compliant since it uses alot of POSIX relevant extensions like getopt(), access(), snprintf(), etc.
But to make nice with the compiler in a ANSI/ISO C -way, you have to declare the variables befor assignment, which is why, for instance, I declare the type of i befor I make any reference to it, or perform any other actions for that matter..
It would be nice to declare your FILE* at the top of your create_file() function. So it would be something like:
Code:
...
int create_file (char* filename, int count) {
FILE* success;
char* buff; /* our filename with dynamic memory
allocation */
...
else {
if(!(success = fopen (buff, "w")))
printf ("%s: Error creating file.", buff);
...
If you want to figure out thesse sort of ANSI/ISO compliances, then you can tell gcc, to a strict ANSI/ISO type check with the flag -ansi, note that I did mention this isn't following the ANSI/ISO completely, so it wont compile with that flag... Mostly due to the use of getopt().
And one last thing, the description for char opt; in main() should be a refference to getopt(), not optind.. Sorry that one was partialy my mistake, I had my mind in another part of the program, when I wrote that description...
Else a nice little proggy for a 3.rd day student  So welcome to the world of C...
|
Thank you. (:
Before any examination though, I believe more learning is necessary. What would you do if you were my position and couldn't tell a optind from a getopt()? (; I am a beginning computer science student (have taken an introductory Java class so far) and will most assuredly take a C class if it is offered (especially if it is required for my major, though I understand my university is mostly geared toward C++).
So far, I have read this paper from a university professor and read most of this tutorial. Are there any other links you would recommend for a beginner?
|
|
|
08-04-2006, 11:32 PM
|
#8 (permalink)
|
|
Newbie
Join Date: Jun 2002
Location: Denmark
Posts: 1,713
|
Quote:
|
To be honest, I don't really understand your question.
|
What I was inteersted in, was, what if the program was called like:
Quote:
|
> ./ifc pete john henry -n 4 alex karen
|
in what part of the main() function would your create_file() function be called from ?
Just to see if you understood the checking of optind values agains how the program initialy had been executed.
Quote:
|
I believe optind is the argv[] index of the first command line argument that isn't a switch option?
|
Both yes and no.. optind points to where in argv[] the last occurance of the given possible arguments has been found, but first I want to explain getopt() for you.
getopt()'s last argument, in this case "n:h" tells it which switches to look for, in this case -n and -h, since theres a ':' emidiately following the 'n' this tells getopt() to place the next item in argv[] in the internal optarg variable.
Your program is a very simple program, when dealing with getopt(), since it only takes these two flags, and since the program emidiately terminates, if the -h switch is met, it means the only flag that is of interrest further down through your main() is the -n.
This can be good, and it can be bad...
I would considder this good, as with ls, where
Quote:
> ls -l file1 file2 file3
> ls file1 -l file2 file3
> ls file1 file2 file3 -l
|
is essentialy the same thing.
Since this provides the user with the abillety to parse your "-n <number>" flag anywhere in the list they want, altho if they were to call it like:
Quote:
|
> ./ifc -n 2 pete brian -n 4 john
|
It would create 4 instances of the filenames in question, and ommit the first -n flag, this would by the logic I've shown you, also make the program try to create files named "-n" and "2" hence the bad part.
When providing the user with this sort of scalability, you need to extend your checking throughout your handling of the parsed arguments, so the program tries to capture all possible ways the the execution could have went.
So the Will be met, when the execution was performed with
Quote:
|
> ./ifc -n 3 pete john betty
|
Since optind has the value 3, it is pointing at index 3 of argv[], which is where we find "pete" so we know the execution was performed correct in teh sence as you default would expect the user to use your program.
So we just run through the rest of the arguments and take them as beeing the filenames that should be created.
If forinstance the user had written a whole bunch of filenames, then suddently realizes " -hey I need 3 of all of them, damned I dont want to cyckle through them all again, and add the '-n 3' flag at the beginning, I'll just add it at the end" Then you get a call to your program which is like
Quote:
|
> ./ifc pete mona john ursula -n 3
|
In this case your
Code:
if (optind == argc || optind > argc)
part of the main() function will handle it..
This however needs you to only cyckle through all given arguments untill it reaches the one just befor '-n', hence the i < (argc -2)
check in the for() loop, since we know at this stage the two remaining can only be '-n' and '3'.
Should there be some sneaky little prick, who wants to see if he can realy screw up the program execution, perhaps to find any exploitable weaknesses, he might try and fool it by calling it with
Quote:
|
> ./ifc pete joe -n 4 alex karen
|
This is where optind would point somewhere in teh mittle of argv[] so the main function will catch it at
Code:
if (optind > 1 && optind < argc)
But in this case we need to cyckle through all the arguments befor the encountered '-n' aswell as all teh arguments after it, thus it requires two for() loops like
Code:
for(i=1; i < (optind - 2); ++i)
create_file(argv[i], count);
for(i+=2; i < argc; ++i)
create_file(argv[i], count);
And the very last else will capture anything which dosn't fit in, like if the program had been executed with where theres no intention on creating a multiple of the files, but only the single file.
Now one last part hasn't been covered, if a mallicius user realy wanted to screw it up, they would try and call it like:
Quote:
|
> ./ifc -n -1 pete karen brian
|
But since every for() loop which check against the given count as long as the files created dosn't voershoot the count value, we have it covered, since it's pretty hard to create less than one file.
When veturing into teh world of C, I would suggest to read the The C book it's a nice intro to the structure of the language, which I guess you've discovered ressembles PHP quite alot.
Since I've guessed you're using a *nix environment, theres quite alot of info to get from the man pages aswell ie is the same as the online man-pages I've referenced to..
The next big thing, once you get comfortable with C, is to use autoconf, automake and libtool so your programs can be ported to any system.
But to be honest, lerning C in 3 - 4 days would require quite alot of programming experiences within a number of different languages..
|
|
|
08-05-2006, 12:35 AM
|
#9 (permalink)
|
|
Registered User
Join Date: Aug 2006
Posts: 5
|
Thanks again for all your help. I will definitely get to reading The C Book...it looks very informative. I am currently using Windows/Cygwin but I believe I can find the man pages online. I also have access to Linux system so learning autoconf, automake and libtool should be quite useful. Thank you also for your hard work in explaining getopt(), I much appreciate it. It'll probably take more then 3-4 days but I think I'll (sort of!) get the hang of it eventually.
|
|
|
| Thread Tools |
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -8. The time now is 04:49 PM.
|
Copyright © 2000-2008, Milano Interactive
Web Hosting provided by Portal 360 Web Hosting
|
 |
|