Simple Program Segmentation Faults - c

I'm new to C, and I've been trying to figure out pointers.
This program works with -i but segfaults after a few lines and -f segfaults right away.
#include <stdio.h>
#include <string.h>
void search_and_print ( char pattern[], FILE* search_file );
int main ( int argc, char *argv[] ) {
const char TOO_MANY_VARIABLES[] = "Too many arguments from the command line!";
const char NOT_ENOUGH_VARIABLES[] = "\nUSAGE: a.out [-i] [-f filename] (Search Pattern)\n";
if (argc < 2) { printf(NOT_ENOUGH_VARIABLES); return(1);}
// If input
if (strcmp(argv[1],"-i") == 0) {
char *pattern = argv[2];
search_and_print(pattern, stdin);
}
// If file
if (strcmp(argv[1],"-f") == 0) {
char *pattern = argv[3];
// Check if file exists
// Open file
FILE *file = fopen( argv[2], "r" );
search_and_print(pattern, file);
fclose( file );
}
}
void search_and_print ( char pattern[], FILE* search_file ) {
// Read through file
const int MAX_CHARACTERS_PER_LINE = 1000;
char* line[MAX_CHARACTERS_PER_LINE];
while ( fgets(*line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(*line, pattern) != NULL )
printf(*line);
}

You have quite a few bugs here.
char* line[MAX_CHARACTERS_PER_LINE];
defines an array of 1000 pointers, not characters. fgets(*line, ... passes the first of those pointers, which is uninitialized, to fgets, most likely causing a segvio.
printf(*line);
The first argument to printf is a format. Never ever pass user input as the format, as this opens a huge security hole in your program ... see http://en.wikipedia.org/wiki/Uncontrolled_format_string
You should use fputs(line) or printf("%s", line) (once you fix the declaration of line).
int main
You don't return a value (except in the error case) ... that results undefined behavior.
FILE *file = fopen( argv[2], "r" );
You should check whether this succeeds. If the file can't be opened (e.g., it doesn't exist), passing it to fgets results in undefined behavior.
if (argc < 2) { printf(NOT_ENOUGH_VARIABLES); return(1);}
This test isn't sufficient for your -f case.

you don't need
char* line[MAX_CHARACTERS_PER_LINE];
this is a pointer to arrays (maybe useful if you want to store the file or input line by line) and you haven't allocated it first. so the seg fault is quite obvious.
change your search_and_print to this:
void search_and_print ( char pattern[], FILE* search_file ) {
// Read through file
const int MAX_CHARACTERS_PER_LINE = 1000;
char line[MAX_CHARACTERS_PER_LINE];
while ( fgets(line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(line, pattern) != NULL )
printf("%s\n", line);
}
additional to Jim Balter's very good advices, also I would suggest using getopt for parsing your parameters.

For future reference for anyone, here is the code with the suggested changes that seems to work. Thanks again for the help!
#include <stdio.h>
#include <string.h>
void search_and_print ( char pattern[], FILE* search_file );
int usage(const char* err);
const char USAGE[] =
"\nUSAGE: a.out [-i] [-f filename] (Search Pattern)";
int main ( int argc, char *argv[] ) {
const char TOO_MANY_VARIABLES[] = "Too many arguments from the command line!";
if (argc < 2) return usage("Not enough options");
if (argc > 4) return usage("Too many arguments from the command line!");
// If input
if (strcmp(argv[1],"-i") == 0) {
if (argc > 2) {
char *pattern = argv[2];
search_and_print(pattern, stdin);
}
else {
printf("Need a pattern to search by!");
return 1;
}
}
// If file
if (strcmp(argv[1],"-f") == 0) {
if (argc > 3) {
char *pattern = argv[3];
// Open file
FILE *file = fopen( argv[2], "r" );
// Check if file exists
if ( file != NULL) {
search_and_print(pattern, file);
fclose( file );
} else {
printf("File not found!");
return 1;
}
} else {
printf("Need a pattern to search by!");
return 1;
}
}
return 0;
}
int usage(const char* err) {
fprintf(stderr, "%s\n%s\n", err, USAGE);
return 1;
}
void search_and_print ( char pattern[], FILE* search_file ) {
const int MAX_CHARACTERS_PER_LINE = 1000;
char line[MAX_CHARACTERS_PER_LINE];
// Read through file
while ( fgets(line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(line, pattern) != NULL )
printf("%s", line);
}

Related

C program that reads a file the user inputs and returns the first line

#include <stdio.h>
int main (int argc, char **argv)
{
FILE* file;
char scratch[1024];
char filename[1024] = argv[1];
file = fopen( filename, "r" );
if( file != NULL ){
if( fgets( scratch, 1024, file ) != NULL ){
fprintf( stdout, "read line: %s", scratch );
}
fclose(file);
return 0;
}
}
Essentially if the user was to run
./nameOfTheProgram nameOfTheTextFile
it should return the first line of the Text File
I'm getting the following error:
error: invalid initializer
char filename[1024] = argv[1];
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
... your variables
char *filename;
if (argc>1)
{
filename = argv[1];
}
else
{
fprintf(stderr, "need a filename\n");
exit(1);
}
... your code
return 0;
}

Function that copies parts of a file to another in C

I have to make a function which reads a file line by line and copies a some of them to another file. The head of the function has to look like this: "int write_x(const char *input_path, const char *output_path, int x)" and can't be changed. For example if x = 3 every third line of the input txt-file should be copied to the output txt-file. Every line has a maximum of 80 signs. I hope someone can help me because i'm not programming very long yet.
This is what I have alreaday, but it doesn't work:
#include <stdlib.h>
#include <stdio.h>
int write_x(const char *input_path, const char *output_path, int x){
input_path = "in.txt";
output_path = "out.txt";
FILE *in
FILE *out
char text[100];
for(int i = 0; i < 100; i++){
fgets(text[i], 80, in);
if(i % x == 0) {
fprintf(out, "%s\n", text[i]);
}
}
return 0;
}
I didn't compile it (I'll leave that to you), but how about something like this?
Note that if your teacher penalizes for the use of 'goto', then you'll need to modify the error handling.
#include <stdio.h>
#include <stdlib.h>
int write_x(const char *input_path, const char *output_path, int x) {
int result = 0 ;
FILE * inputFile=NULL;
FILE * outputFile=NULL;
/* Open the input file for reading */
inputFile = fopen( input_path, "r" ) ;
if ( inputFile == NULL ) {
perror( "fopen(input_path)" ) ;
result = -1;
goto clean_up;
}
/* Open the output file for writing */
outputFile = fopen( output_path, "w") ;
if ( outputFile == NULL ) {
perror( "fopen(output_path)" ) ;
result = -1;
goto clean_up;
}
int lineNumber = 0 ;
char * lineBuffer = NULL ; // memory will be allocated by getline()... but you'll need to free it up afterwards
int lenBuffer ; // getline will set how bug the buffer is
/* Repeat for each line read */
while (getline( &lineBuffer, &lenBuffer, inputFile) != -1) {
/* Only at each 'x' line count */
if ( (++lineNumber % x ) == 0 ) {
/* Write the line to the output file */
if ( fwrite( lineBuffer, lenBuffer, 1, outputFile ) != lenBuffer ) {
perror("fwrite");
result = -1;
goto clean_up;
}
}
}
clean_up:
/* avoid memory leak */
if ( lineBuffer != NULL ) {
free( lineBuffer ) ;
}
/* Close the input file */
if ( inputFile != NULL ) {
fclose( inputFile ) ;
}
/* Close the output file */
if ( outputFile != NULL ) {
fclose( outputFile ) ;
}
/* All done */
return result ;
}
It seems like a really bad design to be passing paths to this function as arguments instead of FILE *'s, but you're under no obligation to read the file line by line. Just read one character at a time. Something like:
#include <stdlib.h>
#include <stdio.h>
FILE * xfopen(const char *path, const char *mode);
int
write_x(const char *input_path, const char *output_path, int x)
{
FILE *in = xfopen(input_path, "r");
FILE *out = xfopen(output_path, "w");
int c;
int count = 1;
while( (c = fgetc(in)) != EOF ){
if( count == x ){
fputc(c, out);
}
if( c == '\n' ){
count = ( count % x ) + 1;
}
}
return 0;
}
int
main(int argc, char **argv)
{
int step = argc > 1 ? strtol(argv[1], NULL, 10) : 1;
char *in = argc > 2 ? argv[2] : "in.txt";
char *out = argc > 3 ? argv[3] : "out.txt";
write_x(in, out, step);
return 0;
}
FILE *
xfopen(const char *path, const char *mode)
{
FILE *fp = path[0] != '-' || path[1] != '\0' ? fopen(path, mode) :
*mode == 'r' ? stdin : stdout;
if( fp == NULL ){
perror(path);
exit(EXIT_FAILURE);
}
return fp;
}

C program to print given number of lines from beginning of text file. File name and number of lines from command line argument

I'm writing a program said in this post title. I take reference at this webpage.
https://www.includehelp.com/c-programs/c-program-to-print-given-number-of-lines-of-a-file-like-head-command-in-linux.aspx
Here are the codes from that webpage.
#include <stdio.h>
int main(int argc, char * argv[])
{
FILE *fp; // file pointer
char *line = NULL;
int len = 0;
int cnt = 0;
if( argc < 3)
{
printf("Insufficient Arguments!!!\n");
printf("Please use \"program-name file-name N\" format.\n");
return -1;
}
// open file
fp = fopen(argv[1],"r");
// checking for file is exist or not
if( fp == NULL )
{
printf("\n%s file can not be opened !!!\n",argv[1]);
return 1;
}
// read lines from file one by one
while (getline(&line, &len, fp) != -1)
{
cnt++;
if ( cnt > atoi(argv[2]) )
break;
printf("%s",line); fflush(stdout);
}
// close file
fclose(fp);
return 0;
}
My problem is the getline function. Since I'm not using Linux that function's giving error in my compiler. I tried to change it to fgets function. This is my revised codes.
I got two errors in the line ' while (fgets(&line, bufferLength, fp) != -1)'.
Error: passing argument 1 of 'fgets' from incompatible pointer type.
Error: comparison between pointer and integer.
My question is - how can I modify the program using fgets? Many thanks to anyone who can work this out.
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp; // file pointer
char *line = NULL;
int bufferLength = 255;
int cnt = 0;
if( argc < 3)
{
printf("Insufficient Arguments!!!\n");
printf("Please use \"program-name file-name N\" format.\n");
return -1;
}
// open file
fp = fopen(argv[1],"r");
// checking for file is exist or not
if( fp == NULL )
{
printf("\n%s file can not be opened !!!\n",argv[1]);
return 1;
}
// read lines from file one by one
while (fgets(&line, bufferLength, fp) != -1)
{
cnt++;
if ( cnt > atoi(argv[2]) )
break;
printf("%s",line);
fflush(stdout);
}
// close file
fclose(fp);
return 0;
}
Your program should compile and run correctly follows:
//c program to print given number of lines from beginning of a file
//file name and number of lines must be supply as command line argument
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
FILE* fp; // file pointer
char* line = malloc(255);
int bufferLength = 255;
int cnt = 0;
if( argc < 3)
{
printf("Insufficient Arguments!!!\n");
printf("Please use \"program-name file-name N\" format.\n");
return -1;
}
// open file
fp = fopen(argv[1],"r");
// checking for file is exist or not
if( fp == NULL )
{
printf("\n%s file can not be opened !!!\n",argv[1]);
return 1;
}
// read lines from file one by one
while (fgets(line,bufferLength, fp))
{
cnt++;
if ( cnt > atoi(argv[2]) )
break;
printf("%s",line);
fflush(stdout);
}
// close file
fclose(fp);
free(line);
return 0;
}
we have two main problems, first
char * line = NULL;
line is a line of characters, a string if you want to call it that, so we must reserve enough memory to accommodate a complete line, and we do this with the malloc function, as seen in the program, the other problem we have with fgets, this function returns a pointer therefore we cannot compare the value returned by fgets with an integer, the declaration
while (fgets (line, bufferLength, fp))
is equivalent to running the loop while fgets is other than NULL. Finally we must use line instead of &line, the latter asks for the address of the line pointer, and not the address it points to.
There's no need to keep track of more than a single character. Reading full lines is overkill. Just do:
#include <stdio.h>
#include <stdlib.h>
FILE *
xfopen(const char *path, const char *mode)
{
FILE *fp = fopen(path, mode);
if( fp == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return fp;
}
int
main(int argc, char **argv)
{
int count = argc > 1 ? strtol(argv[1], NULL, 10) : 1;
FILE *in = argc > 2 ? xfopen(argv[2], "r") : stdin;
int line = 0;
int c;
while( line < count && ( c = fgetc(in)) != EOF ) {
putchar(c);
if( c == '\n' ) {
line += 1;
}
}
}
Note that I've reversed the order of the arguments, so that stdin is read if only a count is given.

Reading line by line from a file in C

What I am trying to do is print out the contents of a file line by line. I run the program in terminal by doing: ./test testText.txt. When I do this, random characters are printed out but not what is in the file. The text file is located in the same folder as the makefile. What's wrong?
#include <stdio.h>
FILE *fp;
int main(int argc, char *argv[])
{
char line[15];
fp = fopen(*argv, "r");
while((fgets(line, 15, fp)) != NULL)
{
printf(line);
printf("\n");
}
}
When I do this, random characters are printed out but not what is in the file
These characters are not random, and in fact they are coming from a file. It's not the file that you are trying to read, though - it's the executable file which you are running.
*argv represents the name of the executable; add this line to see what's in *argv:
printf("%s\n", *argv);
The actual command line arguments start at argv[1], so you need
fp = fopen(argv[1], "r");
The first argument passed on the command line is at argv[1], while *argv refers to argv[0]. argv[0] contains the filename of the executable - you are printing out the content of the executable.
The following code prints out the entire argv[] array, then reads your file and prints it.
#include <stdio.h>
int main( int argc, char *argv[] )
{
for( int i = 0; i < argc; i++ )
{
printf( "argv[%d] : %s\n", i, argv[i] ) ;
}
if( argc >= 2 )
{
FILE* fp = fopen( argv[1], "r" ) ;
if( fp != NULL )
{
char line[15];
while( fgets( line, sizeof(line), fp ) != NULL )
{
printf( "%s", line ) ;
}
}
}
return 0 ;
}
Note that fgets() will read an entire line including the , so there is no need to print '\n', especially because with only 15 characters, your line buffer may well not contain an entire line. Note also the tighter localisation of variables - your code needlessly made fp global.
Other refinements are the safe use of the array size rather than literal 15, and the use of a literal constant string for the format specifier. You should avoid passing a variable string for the printf() format string - if your input itself contains format specifiers, printf() will try to read data from arguments that do not exist with undefined results.
Q: What's wrong?
A humble critique:
#include <stdio.h>
FILE *fp; // Perhaps this should be declared inside main?
int main(int argc, char *argv[])
{
char line[15]; // Are the file lines all 14 characters or less? (seems small)
fp = fopen(*argv, "r"); // Opening the binary executable file (argv[0])? Intereting.
// Should check here to ensure that fopen() succeeded.
while((fgets(line, 15, fp)) != NULL)
OK... well, remember that this isn't a text file.. it's an executable (due to *argv). This will read some wacky (but not random) characters from the executable.
{
printf(line); // Bad practice. Should be: printf("%s", line);
Ok... now print the wacky characters?
printf("\n"); // Redundant. The '\n' characters will be supplied in 'line'.
}
// fclose() call missing.
// Integer return value for main() is missing.
}
Here is (perhaps) what was actually intended:
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int rCode = 0;
FILE *fp = NULL;
char line[255+1];
if(argc != 2)
{
printf("Usage: %s {filepath}\n", *argv);
goto CLEANUP;
}
errno=0;
fp = fopen(argv[1], "r");
if(NULL == fp)
{
rCode=errno;
fprintf(stderr, "fopen() failed. errno:%d\n", rCode);
goto CLEANUP;
}
while(fgets(line, sizeof(line), fp)) /* --As per 'chux' comment */
printf("%s", line);
CLEANUP:
if(fp)
fclose(fp);
return(rCode);
}
Or, if the intent is truly to print the content of the executable, perhaps this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int rCode = 0;
FILE *fp = NULL;
off_t offset = 0;
errno=0;
fp = fopen(*argv, "r");
if(NULL == fp)
{
rCode=errno;
fprintf(stderr, "fopen() failed. errno:%d\n", rCode);
goto CLEANUP;
}
for(;;)
{
char line[16];
size_t bytesRead;
int index;
char ascii[16+1];
memset(ascii, 0, sizeof(ascii));
bytesRead = fread(line, 1, sizeof(line), fp);
if(0==bytesRead)
break;
printf(" %08zX | ", offset);
for(index=0; index < bytesRead; ++index)
{
printf("%02hhX%c", line[index], 7==index ? '-' : ' ');
ascii[index] = isprint(line[index]) ? line[index] : '.';
}
printf("%*s %s\n", (16 -index) * 3, "", ascii);
offset += bytesRead;
}
if(errno)
{
rCode=errno;
fprintf(stderr, "fgets() failed. errno:%d\n", errno);
}
CLEANUP:
if(fp)
fclose(fp);
return(rCode);
}
your file name found at index 1 of argv.
if (argc <= 1) {
printf("no file was given\n");
exit(-1);
}
// open file from argv[1]
// ...

Why does it matter where fopen is used?

I could use some help understanding something puzzling to me. It concerns the position of of fopen() to read out a file.
Following code (C compiled with gcc 4.5.2):
#include <stdlib.h>
#include <stdio.h>
void try_fopen(FILE* f_handle, const char* f_name, const char* mode) {
f_handle = fopen(f_name, mode);
if( f_handle == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(EXIT_FAILURE);
}
}
int cnt_ones(FILE* pFile) {
int c;
int n = 0;
do {
c = fgetc (pFile);
if (c == '1') n++;
} while (c != EOF);
return n;
}
Why is it that putting the fopen in a function gives a Segfault:
int main (int argc, char** argv) {
FILE * pFile;
try_fopen(pFile, argv[1], "r"); // Gives a Segfault
printf ("The file contains %d ones.\n", cnt_ones(pFile) );
fclose (pFile);
return 0;
}
While putting it into the main (along with the if doesn't):
int main (int argc, char** argv) {
FILE * pFile;
pFile = fopen(argv[1], "r"); // While this doesn't give a Segfault
if( pFile == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", argv[1]);
exit(EXIT_FAILURE);
}
printf ("The file contains %d sign characters.\n", cnt_ones(pFile) );
fclose (pFile);
return 0;
}
C is pass by value, not by reference, so you need to pass the pointer to pFile, otherwise you don't change it outside of the function scope:
void try_fopen(FILE** f_handle, const char* f_name, const char* mode) {
*f_handle = fopen(f_name, mode);
if( *f_handle == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(EXIT_FAILURE);
}
}
// ...
try_fopen(&pFile, argv[1], "r");
Because the pointer pFile is passed by value to the function try_open. The value modified inside the function is not available in the main. To solve this, you need to pass the address of the pointer to the function, so try_open would accept FILE** and assign the result of fopen to *pFile. While calling this function you should pass the address of pFile using &pFile.
You can either do :
File * fp;
try_fopen( &fp,.....); /* void try_fopen (FILE ** fp,....) */
or the following :
File * fp = try_fopen("file name"); /* FILE * try_fopen (const char * file_name,...) */
The reason is simple, when you pass FILE* to function, it's updation will be lost as it is passed by Value. Try passing FILE ** to the function and it will work. Refer to Binyamin Sharet's answer above for the code snippet
The reason for this can be understood by reading this link
Or
You can change the function try_open to return FILE * as the return value.
FILE *try_fopen(const char* f_name, const char* mode)
{
FILE *f_handle = NULL;
*f_handle = fopen(f_name, mode);
if( *f_handle == NULL )
{
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(0);
}
}
//In the main function.
FILE *pFile = try_fopen(argv[1], "r");

Resources