C Basic Head Command - c

I'm trying to recreate the head, and tail commands from linux for my programming class.
We just started using C so I'm new to the idea of allocating memory and pointers.
I'm wondering why this doesn't work.
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv){
/* Checks if correct amount of arguements */
if(argc != 2 || argc != 4){
printf("Usage: %s head <file> \n Or: head <file> -n <number of characters>", argv[0]);
exit(-1);
}
if(strcmp(argv[1], "-n" != 0)){
char fileName[strlen(argv[1])] = argv[1];
}
}
//Compile error on char fileName[strlen(argv[1])] = argv[1];
Any additional insight would also be helpful.

I think it's better to write:
char fileName[strlen(argv[1])+1];
strcpy(fileName, argv[1]);
or (if you don't whant to make a copy of string) :
char* fileName = argv[1];

First things first, your usage doesn't match your argument checking. According to the usage, you must use one of:
head <filename>
head <filename> -n <count>
In other words, argv[1] is always the filename, argv[2] is the one that needs to be set to -n if there are more than two arguments.
Secondly, unless you want to use VLAs (variable length arrays), you should probably just set up a pointer to the filename argument with something like:
char *fileName = argv[1];
You don't need to change it at all (you'll just be passing it to fopen, presumably), so it's a waste trying to make another copy.
In addition, your if statement is wrong as an or, it should be an and. It's guaranteed that argc will either not be 2 or not be 4, since it can't be both at the same time.
I would start with something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int usage (void) {
printf ("Usage: head <file>\n");
printf (" or: head <file> -n <number of characters>\n");
return -1;
}
int main (int argc,char *argv[]) {
char *fileName;
int lineCount;
// Checks if correct arguments
if ((argc != 2) && (argc != 4)) return usage();
if ((argc == 4) && (strcmp(argv[2], "-n" != 0)) return usage();
// Get file spec and line count
fileName = argv[1];
lineCount = (argc == 2) ? 10 : atoi (argv[3]); // or strtol for purists
if (linecount < 0) lineCount = 0;
// Now go ahead and implement the logic for head.
}

Related

Why am I segfaulting?

I'm very new to C, I am attempting to read the contents of one file character by character and output them to the stream. But even with my fopen() command commented out I receive segfault (core dumped).
I must run a command: ./a.out < testWords.in > myOut.txt to execute my file properly.
Here is what I have so far:
#include <stdio.h>
void main(char *fileName[])
{
printf("filename is %s.\n",fileName[0]);
//Get file based on a string inputed
FILE *fp=fopen(fileName[0],"r"); //Fetches our file as read only
char ch;
int lineCount = 0;
int wordCount = 0;
int charCount = 0;
//Failed to find/open file. NULL character.
if (fp == 0) printf("Woops! Couldn't open file!\n");
//While not at end of file, grab next char.
else while( (ch=fgetc(fp)) != EOF)
{
if (ch == '\n') //on newline
{
//Prints (charCount,wordCount)\n lineCount:
printf("(%d,%d)%c%d:",charCount,wordCount,ch,lineCount);
charCount = 0;
lineCount += 1;
}
else printf("%c",ch); //mirrors char.
}
fclose(fp); //Closes file (gotta be tidy!)
}
You can't just invent a way to call main. You need to use one of the standard ways, like this:
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return -1;
}
FILE *fp = fopen(argv[1], "r");
// ...
}
And note that argv[0] contains the program name (if available; if not it contains an empty string).
Your program segfaulted because you received the int argc argument into your char *filename[] parameter. If you ran the program with a single command line parameter, the value passed in as the first argument would have been 2, which is not a valid pointer value. The expression filename[0] dereferences that address and causes a segfault.
Any time you get a segfault in C, you should smell a bad pointer or address in an argument list. In this particular case., the signature of main is always int main(int argc, char** argv). Yours isn't.
What you want is
int main(int argc, char ** argv) {
...
FILE * fp = fopen(argv[1]); // Quiz: why argv[1]? What's argv[0]?
You're getting away with it in the compiler because, basically, luck.
I also notice in your example call, there's actually no argument in the argument list, because you're using redirection.
Use:
int main(int argc, char* argv[])
And use argv[1] as fileName.
Main function must receive always that two parameters.

Segmentation Fault when using fopen on argv

I continue to have a segmentation fault when I try and execute the following code...
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
if (argc < 4){
fprintf (stderr,"usage message");
return (1);
}
FILE *src = fopen(argv[1],"r"); //file pointer to inputFile
FILE *outputFile = fopen(argv[2],"w"); //file pointer to outputFile
int nth = atoi(argv[3]); //nth term value
printf("nth term is %d",nth);
int c;
int currNum;
int currCount = 1;
c = fscanf(src, "%d\n",currNum); //read ints line by line
while( c == 1 ){
fscanf(src,"%d\n",currNum);
++currCount;
if (currCount % nth == 0){
fprintf (outputFile, "%d\n", currNum);
}
}
}
I'm not sure if I have to somehow convert argv[1] and argv[2] before I can use them as the file names.
Did you provide a command line argument? You should check that by using an if statement before opening the files. For example, you could add
if ( argc < 4 )
{
printf ( stderr, "usage message\n" );
return ( 1 );
}
Also, change that stoi for argv[3] to atoi.
You don't need to add \n for fscanf. Just "%d" will do fine.
giving input parameters to "fscanf" is wrong. check below one.......
c = fscanf(src, "%d\n",currNum); // wrong
c = fscanf(src, "%d\n",&currNum);
fscanf(src,"%d\n",&currNum);
You can use atoi to convert to int
int nth = atoi( argv[3] );
You also can NULL check file* src and outfile. If you are not giving correct paths then fopen may fail.

Getting directory and directorates of a given path or use current working directory if no path is given

i have the following program working, but the problem is it only works when a path is given. I'm trying to find a way to set the path to current working directory if no path is given. For that i am using char *cdir = getcwd(0,0); I need to find a way to set that to argv, so that it points to that path instead of null. Can anyone check my code and tell me what i'm doing wrong. I am using a unix system to compile this.
#include <dirent.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
typedef struct stat Sub;
typedef struct dirent Dir;
void skimPath(const char *);
main(int argc, char *argv[])
{
int i;
Sub path;
char *cdir = getcwd(0,0);
if (argc <= 1)
{
/*
this is the part i'm having trouble with, everything else works. I need a way to set
the path that is in cdir, to argv, so that it would work just like the case below
where argc is more than 2
*/
argv = &cdir;
printf("%s",argv);
for (i = 1; i < argc; i++)
{
if (stat(*(argv + i), &path) == -1)
{
printf("WROTH PATH, DIRECTORY NOT FOUND \n%s\n", *(argv + i) );
continue;
}
if (S_ISDIR(path.st_mode))
skimPath(*(argv + i));
}
}
if (argc >= 2)
{
for (i = 1; i < argc; i++)
{
if (stat(*(argv + i), &path) == -1)
{
printf("WROTH PATH, DIRECTORY NOT FOUND \n%s\n", *(argv + i) );
continue;
}
if (S_ISDIR(path.st_mode))
skimPath(*(argv + i));
}
}
}
void skimPath(const char *dirName)
{
char str[100];
DIR *dir;
Sub path;
Dir *d;
if ((dir = opendir(dirName)) == NULL)
{
printf(str, "File or Directory Could Not Open");
}
while ((d = readdir(dir)) != NULL)
{
// check if directory is d or d's paren
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
continue; // if ture rest will be ignored from while loop
// saves in a buffer pointed by str
sprintf(str, "%s/%s", dirName, d->d_name);
if (stat(str, &path) == -1)
{
continue;
}
//checks to see if its a d
if (S_ISDIR(path.st_mode))
{
printf("%s \n",d->d_name);
// directory goes in str
skimPath(str);
}
}
}
We can analyze your code, but what you're trying to do is fairly bizarre (in detail — there are other ways to do what you want, I think).
main(int argc, char *argv[])
{
int i;
Sub path;
char *cdir = getcwd(0,0);
Since you don't always use cdir, you could — should — declare it inside the block where you use it. getcwd() is an expensive function, especially if you've got multiple mounted file systems to deal with, and especially NFS-mounted file systems.
if (argc <= 1)
{
/*
this is the part i'm having trouble with, everything else works. I need a way to set
the path that is in cdir, to argv, so that it would work just like the case below
where argc is more than 2
*/
argv = &cdir;
This statement is 'legitimate', but you've not thought through the consequences. You now have argv pointing at precisely one string (no null termination on the list of pointers) and argc is now immaterial.
printf("%s",argv);
This is wrong; it should be one of these lines:
printf("%s\n", argv[0]);
printf("%s\n", *argv);
printf("%s\n", cdir);
You've zapped your original argument list and the only argument left is the current directory.
for (i = 1; i < argc; i++)
{
Since argv now points at cdir, you can't iterate over the arguments. Doubly, you can't do that starting at index 1.
if (stat(*(argv + i), &path) == -1)
Yes, you can write argv[i] like that, but why would you do so?
{
printf("WROTH PATH, DIRECTORY NOT FOUND \n%s\n", *(argv + i) );
continue;
}
While you will find 'wroth' in a decent dictionary ('adj (archaic): angry'), you probably mean 'wrong'. And SHOUTING at people is unkind. Also, error messages are best printed to standard error; that's what it is for. And if you used an else (or else if) you could avoid the continue.
if (S_ISDIR(path.st_mode))
skimPath(*(argv + i));
}
}
This much is OK.
if (argc >= 2)
{
...
}
I'd write this as an else clause for the current code structure.
Since you'll do the same thing if the user explicitly passes the name . as an argument, it is very tempting to fake things by providing the current directory as the first argument if the user didn't provide one. Since when you enter the main(), the condition argv[argc] == NULL is true, you can actually write:
int main(int argc, char **argv)
{
if (argc == 1)
argv[argc++] = ".";
assert(argc > 1);
for (int i = 1; i < argc; i++)
{
...code from the if (argc >= 2) part of your code...
}
return 0;
}
If you needed to insert more than one argument, you'd have to go through a bit more of a rigmarole, more like:
if (argc < XXX)
{
static char *alt_argv[] = { 0, "xyz", "pqr", "abc", 0 };
alt_argv[0] = argv[0];
argv = alt_argv;
argc = (sizeof(alt_argv) / sizeof(alt_argv[0])) - 1;
}
Despite warnings from others, argc and argv are local variables in the main() function and can be modified (carefully). It is more nearly dodgy to modify the data in argv[argc], but it depends on whether your code uses the null pointer sentinel or uses the count. If you use the count and never access beyond the (modified) argv array end, you'll be fine. If you do access beyond the end, you're trampling on (or reading from) your environment variables on most variants of Unix.
If you do decide you want the absolute path name of the current directory used, then you can still adapt the scheme I outlined to work with that. Assuming that you are working on Linux or a BSD-derived platform, your version of getcwd() will allocate memory when given a null pointer, so you can write:
if (argc == 1)
argv[argc++] = getcwd(NULL, 0);
The only thing to watch for is a null pointer:
for (i = 1; i < argc && argv[i] != NULL; i++)
...
When you need to do the job in real life instead of practicing the basic system calls, consider using nftw()
to traverse the directory hierarchy for you.

How would I get more then one text file accepted?

Right now, I have something like this...
CMD console window:
c:\users\username\Desktop> wrapfile.txt hello.txt
Hello
How would I get something like this?
CMD console window:
c:\users\username\Desktop> wrapfile.txt hello.txt hi.txt
Hello Hi
with this code?
#include <stdio.h>
#include <stdlib.h>
int main(int argc[1], char *argv[1])
{
FILE *fp; // declaring variable
fp = fopen(argv[1], "rb");
if (fp != NULL) // checks the return value from fopen
{
int i;
do
{
i = fgetc(fp); // scans the file
printf("%c",i);
printf(" ");
}
while(i!=-1);
fclose(fp);
}
else
{
printf("Error.\n");
}
}
Well, first of all: in your main declaration, you should use int main(int argc, char* argv[]) instead of what you have right now. Specifying an array size makes no sense when declaring an extern variable (that's what argv and argc are). On the top of that, you are not using the correct types. argc is integer and argv is array of strings (which are arrays of chars). So argv is an array of arrays of chars.
Then, simply use the argc counter to loop through the argv array. argv[0] is the name of the program, and argv[1] to argv[n] will be the arguments you pass to your program while executing it.
Here is a good explanation on how this works: http://www.physics.drexel.edu/courses/Comp_Phys/General/C_basics/#command-line
My 2 cents.
EDIT: Here is a commented version of the working program.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *fp;
char c;
if(argc < 3) // Check that you can safely access to argv[0], argv[1] and argv[2].
{ // If not, (i.e. if argc is 1 or 2), print usage on stderr.
fprintf(stderr, "Usage: %s <file> <file>\n", argv[0]);
return 1; // Then exit.
}
fp = fopen(argv[1], "rb"); // Open the first file.
if (fp == NULL) // Check for errors.
{
printf("Error: cannot open file %s\n", argv[1]);
return 1;
}
do // Read it.
{
c = fgetc(fp); // scans the file
if(c != -1)
printf("%c", c);
} while(c != -1);
fclose(fp); // Close it.
fp = fopen(argv[2], "rb"); // Open the second file.
if (fp == NULL) // Check for errors.
{
printf("Error: cannot open file %s\n", argv[2]);
return 1;
}
do // Read it.
{
c = fgetc(fp); // scans the file
if(c != -1)
printf("%c", c);
} while(c!=-1);
fclose(fp); // Close it.
return 0; // You use int main and not void main, so you MUST return a value.
}
I hope it helps.
argv[2] would be the second file name.
Do not forget to check the value of argc to see if enough arguments are valid.
Better: use boost::program_options.
Caution: this code is not unicode-aware on Windows system, which makes it not portable. Refer to utf8everywhere.org about how to make it support all file names on this platform.

Save data into a file: where the adress of the file is given by the user

I ran a simulation for some data y1, y2,..yn and generate vectors w, mu. At each simulation these results are stored into a file, let us say (normally w and mu are very long vectors 10,000 entries)
/home/carlos/Documents/Results/w.txt
/home/carlos/Documents/Results/mu.txt
But if I want to run my algorithm with other data set, and do not want to lose the previous results, I have to go directly into my C code and change (or move the w.txt, mu.txt to other file)
/home/carlos/Documents/Results/OtherData/w.txt
/home/carlos/Documents/Results/OtherData/mu.txt
I do not want to go every time into my C code to change the address(or move again and again w.txt, mu.txt), I would like to just create a new folder with a name: OtherData and store the data there just giving the address
/home/carlos/Documents/Results/OtherData/
as an input for the code
I did a very simplified example but it does not work, could somebody give me a hand?
#include <stdio.h>
#include <string.h>
void main(char *dir){
char dir_happy[100] = *dir, dir_sad[100]=*dir;
FILE *ffile_happy, *ffile_sad;
strcat(dir_happy, "/happy.txt");
strcat(dir_sad, "/sad.txt");
ffile_happy = fopen("dir_happy.txt", "w");
ffile_sad = fopen("dir_sad.txt", "w");
fprintf(ffile_happy, "Hello!, happy world\n");
fprintf(ffile_sad, "Hello!, sad world\n");
fclose(ffile_happy);
fclose(ffile_sad);
}
You have the arguments to main() wrong. The proper prototype is:
int main(int argc, char *argv[]);
Where argc is the number of arguments given, and argv is a vector holding each argument. The first argument (in argv[0]) is generally the program's name.
Untested. Have fun.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FILENAME_LENGTH 100
#define DEFAULT_DIR "."
#define HAPPY_NAME "/happy.txt"
#define SAD_NAME "/sad.txt"
int main(int argc, char **argv) {
char name1[MAX_FILENAME_LENGTH+1], name2[MAX_FILENAME_LENGTH+1];
size_t dirlen;
char *dir = DEFAULT_DIR;
FILE *ffile_happy, *ffile_sad;
if (argc == 2) {
dir = argv[1];
}
dirlen = strlen(dir);
if (len + strlen(HAPPY_NAME) > MAX_FILENAME_LENGTH) {
fprintf(stderr, "Directory name too long. Program aborted.\n");
exit(EXIT_FAILURE);
}
if (len + strlen(SAD_NAME) > MAX_FILENAME_LENGTH) {
fprintf(stderr, "Directory name too long. Program aborted.\n");
exit(EXIT_FAILURE);
}
strcpy(name1, dir); strcat(name1, HAPPY_NAME);
strcpy(name2, dir); strcat(name2, SAD_NAME);
ffile_happy = fopen(name1, "w");
if (ffile_happy == NULL) {
fprintf(stderr, "Unable to open file \"%s\" for writing. Program aborted.\n", name1);
exit(EXIT_FAILURE);
}
ffile_sad = fopen(name2, "w");
if (ffile_sad == NULL) {
fprintf(stderr, "Unable to open file \"%s\" for writing. Program aborted.\n", name2);
fclose(ffile_happy);
exit(EXIT_FAILURE);
}
/* use files */
fclose(ffile_happy);
fclose(ffile_sad);
return 0;
}
void main(char *dir) is the problem.
Main takes 2 args int/void main(int argc, char *argv[])
argc is the number of arguments to the executable.
argv[0] is the filename of the executable.
argv[1..n] are the arguments passed (normally space separated, with quotes allowed)
So /a.out Hello "Look at me" would parse as
argv[0] => './a.out'
argv[1] => 'Hello'
argv[2] => 'Look at me'

Resources