stdout buffering problems - c

I have written up this code
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
char *list[20],*story[100];
FILE*listfile;
FILE*infile;
FILE*outfile;
int check(char*string)
{
int i=0;
while(list[i]!=NULL)
{
if(strcmp(string,list[i])==0){return 1;};
i++;
};
return 0;
};
void print_d(int d){ printf(" debug %d ",d);};
int main(int argc,char**argv){
assert(argc==4);
printf("hello \n");
//assigning the file pointers in their respective modes
printf("%s %s %s ",argv[1],argv[2],argv[3]);
listfile=fopen(argv[1],"r");
print_d(12);
infile=fopen(argv[2],"r");
outfile=fopen(argv[3],"w");
print_d(0);
int i=0; /* the infamous 'i' */
while(1)
{
if(feof(listfile)!=0)
{ break;};
list[i]=malloc(sizeof(char [15]));
fscanf(listfile,"%s[^\n]",list[i]);
i++;
}
i=0;
print_d(1);
while(1)
{
if(feof(infile)!=0)
{ break;};
story[i]=malloc(sizeof(char [25]));
fscanf(infile,"%s",story[i]);
i++;
}
fclose(infile);
fclose(listfile);
i=0;
print_d(2);
while(1)
{
if(check(story[i])==1)
{ fprintf(outfile,"%s","censored");}
else
{
fprintf(outfile,"%s",story[i]);
};
};
print_d(3);
fclose(outfile);
i=0;
while(list[i]!=NULL)
{ free(list[i]);};
return 0;
}
The following problem ensues
[1] THe output is a hello followed by a seg fault
and here is where things get interesting
if I modify
printf("%s %s %s ",argv[1],argv[2],argv[3]);
to
printf("%s %s %s\n ",argv[1],argv[2],argv[3]);
the ouput is a 'hello' followed with the three names of the files and then a segfault.
After user danfuzz pointed out I should change the print_d debug to print to stderr (which I did) ..the debug prints work fine now.SO I suppose a better question would be why did that happen in the first place and steps to prevent such things from happening?
It may seem trivial a problem for seasoned programmers but mind you, the earlier version(the one in the code above) failed to print out any message before a seg fault , leading me to conclude that something happened in the commandline part of things /opening of the file.

Some observations,
You should read about fflush(stdout), as it will help you with your debug statements,
void print_d(int d)
{
printf(" debug %d ",d); fflush(stdout);
};
You allocate to arrays of char pointers, list[20], and story[100], but you have loops (indexed by the notorious 'i'), which could easily walk off the end of list or story.
You attempt to open files for filenames argv[2] and argv[3], one as read the other as write, change these lines to the following,
printf("%s %s %s ",argv[1],argv[2],argv[3]); fflush(stdout);
if( !(listfile=fopen(argv[1],"r")) )
{
printf("cannot open %s\n",argv[1]); fflush(stdout);
return(1);
}
print_d(1);
if( !(infile=fopen(argv[2],"r")) )
{
printf("cannot open %s\n",argv[2]); fflush(stdout);
return(2);
}
print_d(2);
if( !(outfh=fopen(argv[3],"w+")) ) //notice the "w+" to create missing file
{
printf("cannot open %s\n",argv[3]); fflush(stdout);
return(3);
}
print_d(3);
Now the files are opening properly, so change the debug print_d arguments to increasing numerical order, so you can spot sequentially which, and since you are using counters, a for(;;) loop works,
int check(char*string)
{
int i;
for(i=0; list[i]!=NULL; i++)
{
if(strcmp(string,list[i])==0){return 1;};
};
return 0;
};
Changing the loops to read both files successfully,
for(i=0; i<20; ++i)
{
if(feof(listfh)!=0) { break; };
list[i]=malloc(sizeof(char [15]));
fscanf(listfh,"%s[^\n]",list[i]);
}
fclose(listfh);
debug(4);
And,
for(i=0; i<20; ++i)
{
if(feof(infh)!=0) { break; };
story[i]=malloc(sizeof(char [25]));
fscanf(infh,"%s",story[i]);
}
fclose(infh);
debug(5);
And now a simple change to the loop to scan the story, to check for censorship (eek!), so we avoid comparing and printing null pointers (another problem you had),
for(i=0; i<100 && (story[i]); ++i)
{
if(check(story[i])==1)
{
fprintf(outfh,"%s","censored"); fflush(outfh);
}
else
{
fprintf(outfh,"%s",story[i]); fflush(outfh);
};
};
But hey, note that you really don't need to read the story into an array, you could read a line at a time, and print combine those two loops, and you can scan arbitrarily large files, without allocating lots of space,
for(i=0; 1; ++i)
{
if(feof(infh)!=0) { break; };
story[0]=malloc(sizeof(char [25]));
fscanf(infh,"%s",story[0]);
if(check(story[0])==1)
{
fprintf(outfh,"%s","censored"); fflush(outfh);
}
else
{
fprintf(outfh,"%s",story[0]); fflush(outfh);
};
}
fclose(infh);
fclose(outfh);
You also need to make sure you only free the lines you allocated,
for(i=0; list[i] && list<20; i++)
{
free(list[i]);
}
This should fix your problems.
Add a usage() function,
void usage(char*progname)
{
printf("need 3 files\n");
printf("%s <restricted> <story> <censorted>\n",progname);
}
And call it,
if( argc < 4 )
{
usage(argv[0]);
return 0;
}

Instead of guessing or asking in forums, just run a debugger. If working on linux, gdb will take you straight to the location of the segmentation fault.
Lets say you app is called "foo":
> gdb foo
# run
And when it crashes:
# bt

Related

Read words from file and put them in array

I want to read the words from a text file into an array.
Why does this code work with a 2D array (a[50][50]) but not with a 1D array (a[50])?
This code prints what I want but it also print some other useless characters. What causes this?
void inputwords(){
int i=0;
char wrd[50];
FILE * fptr;
char fname[20]="txt.file";
fptr=fopen(fname,"w");
if(fptr==NULL) {
printf("error in opening file!");
exit(1);
}
while(wrd!='\0'){
fgets(wrd,sizeof wrd,stdin);
fprintf(fptr,"%s",wrd);
if(wrd[i]=='*' && wrd[i+1]=='*' && wrd[i+2]=='*' && wrd[i+3]=='*' &&
wrd[i+4]=='T' && wrd[i+5]=='E' && wrd[i+6]=='L' && wrd[i+7]=='O' &&
wrd[i+8]=='S') {
break;
}
}
fclose(fptr);
return;
}
void readfile(){
FILE *fptr;
char a[50][50];
int i=0;
char fname[20]="txt.file";
fptr=fopen(fname,"r");
while(fgets(a[i],50,fptr)){
i++;
}
for(i=0;i<50;i++){
printf("%s",a[i]);
}
fclose(fptr);
return;
};
main(){
inputwords();
readfile();
return(0);
}
for(i=0;i<50;i++){
printf("%s",a[i]);
}
This prints out the value of every pointer in a[50]. Once you reach past the char pointers which are actually set to point to something, you're just printing out the value of the pointer itself.
for(int n=0; n<i; n++){
printf("%s",a[n]);
}
Would work.
I just tested the answer of Cowbolt, it works , and also wanted to ask you why do you have the line in inputwords() :
while(wrd!='\0') {
The condition of null character will never happen from user input. It is better to have clear instructions and say :
printf("Enter text (to finish input, type on a line of its own:****TELOS ):");
while (1) {
/*be aware that your final i marks the position of string "****TELOS" */
/* so if you dont want it to output, have the n<i-1 in Cowbolt solution*/

Application crashes with segmentation fault

I know what this error means, but I can't see what I did wrong here. I implemented a function to delete files and directories, and I also want to implement the "-i" functionality for a "rm" command. Command used: rm -i filename.
I get asked if I want to remove it, I type "y", the file I want to remove gets deteleted, but the program crashes afterwards and I don't know why.
int has_args(char * argv[], char arg[])
{
int i = 0;
while(argv[i] != NULL) {
if(strcmp(argv[i], arg) == 0) {
return 1;
}
i++;
}
return 0;
}
void print_error(char *this, char *filename)
{
fprintf(stderr, "%s: Could not delete file: %s;\n%s\n", this, filename, strerror(errno));
}
int cmd_rm(int argc, char *argv[])
{
errno = 0;
if(argc > 1) {
if(has_args(argv, "-r")) {
remove_directory(argv[2]);
}
else if(has_args(argv, "-i")) {
char ans[2];
fprintf(stdout, "Delete file: '%s'?\n", argv[2]);
scanf("%s", ans);
trim(ans);
if((ans[0] == 'Y') || (ans[0] == 'y')) {
if(remove(argv[2])) {
print_error(argv[0], argv[2]);
}
}
}
else {
if(remove(argv[1])) {
print_error(argv[0], argv[1]);
}
}
}
else {
puts("ERROR: ");
print_usage(argv[0]);
}
return 0;
}
Why don't you try scanf("%c",&ch); as the answer if user can input only y or n, however if you like to support yes or no you may write the following code :
scanf("%1,3s",ans); /* Points out that the minimum length is 1 and the maximum length is 3 */
and as others have pointed out in some case cmd_rm() function should return 1 or any other value to indicate success in the deletion of file. However I can't see any success case in your function cmd_rm() and it would be better if you enclose your trim function.
But generally Segmentation faults are because of pointers not pointing to a correct position ( tricky pointers ). Debuggers come handy in finding these pointers.

C struct values garbled

Having the problem with the sender and receiver of the checksum. where I am using the struct to define the variables to be written into the pipe.
struct ANSWER
{
int arr[BUFFER];
int counter;
int ans;
}a;
The mechanism used for checksum is :
int chck(int value[],int count)
{
int i,sum=0,checksum;
for ( i=0; i<count; i++)
{
sum+=value[i];
}
printf("SUM IS :%d \n",sum);
checksum=checksum^sum;
printf("CHECKSUM IS : %d",checksum);
return checksum;
}
The main problem is in writing in the pipe as
write(pipe,a,sizeof(a));
This writes all the contains using struct.
While at receiver side having problem in retrieving the specific values
when I retrieve the values at receiver side I get garbled values in count and ans variables.
read(pipe,a,sizeof(a));
printf("COUNT : \n ",a.counter);
the values turn out to be garbled.
Main area:
void main(int argc, char * argv[])
{
int pipe,i;
pipe = open("devil",O_WRONLY);
if ( pipe == -1 )
{
printf("ERROR NO PIPE FOUND \n");
}
printf("ENTER NUMBER OF ELEMENT : ");
scanf("%d",a.counter);
for ( i=0; i<a.counter; i++)
{
printf("ENTER NUMBER :");
scanf("%d",&a.arr[i]);
}
a.ans=chck(a.arr,a.counter);
printf("CHECKSUM IS : %d \n",a.ans);
write(pipe,&a,sizeof(a));
}
Turning comment to answer, I suggest you add error checking to all your system and standard library calls (except printing to stdout/stderr, that's more clutter than it's worth for almost any app), something like:
int main(int argc, char * argv[])
{
int pipe, i;
pipe = open("devil",O_WRONLY);
if ( pipe == -1 )
{
perror("open pipe");
return EXIT_FAILURE;
}
printf("ENTER NUMBER OF ELEMENT : ");
if (scanf("%d",&a.counter) != 1)
{
fprintf(stderr, "scanf count IO or parse error, or EOF\n");
return EXIT_FAILURE;
}
for ( i=0; i<a.counter; i++)
{
printf("ENTER NUMBER :");
if (scanf("%d",&a.arr[i]) != 1)
{
fprintf(stderr, "scanf number IO or parse error, or EOF\n");
return EXIT_FAILURE;
}
}
a.ans=chck(a.arr,a.counter);
printf("CHECKSUM IS : %d \n",a.ans);
i = write(pipe,&a,sizeof(a));
if (i == -1)
{
perror("write to pipe");
return EXIT_FAILURE;
}
else if (i != sizeof(a))
{
fprintf(stderr, "write to pipe, partial write!\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Notes: For pedantic code, write actually returns type ssize_t. I changed return type of main to int as that is required by standard. Please read manual page / docs of perror to understand what it does and how it prints the actual error message. EXIT_FAILURE and EXIT_SUCCESS are constants for the two standard-defined return values or exit codes, usually 1 and 0 respectively. And in a larger program, you would probably write helper functions or macros for error handling, and split the entire main function into multiple functions, to make the code look less cluttered.
Additionally, you use checksum variable uninitialized in chck function. You should probably initialize it to 0, something like:
int chck(int value[],int count)
{
int i = 0, sum = 0, checksum = 0;
....
In general, you should avoid having uninitialzied variables, it's too easy to forget when first using them, like demonstrated here... Also, turn on compiler warnings, so you will usually get a warning if you still do that accidentally.
One more bug: your first scanf was missing & to get pointer to a.counter (required for scanf, to actually modify the original variable). A good compiler may also be smart enough to warn about passing suspicious arguments to scanf.

Using fscanf for a txt file and ignoring everything but letters

Hello I'm writing a program that needs to read from a text file and only takes in the letters of the whole file. It could be something as
Hello, my name is whatever. I'm thinking of trying to write a program: "name!"
What I need to do is only read in the letters so my output would be:
hellomynameiswhateverimthinkingoftryingtowriteaprogramname
I have something of this sort:
while (fscanf(ifp2, "%c", &file[i]) != EOF) //scans until end of file
{
for (i = 0; i < 10000; i++) //loops a possible 10000, file could possibly be that big
{
//printf("Got inside while loop [%d]\n", i); //this just lets me see the loop
if (fscanf(ifp2, "%c ", &file[i]) == 0) //im trying to see how i can ignore some data
{
fscanf(ifp2, "%c ", &file[i]); //scans in the character
}
}
}
for (i = 0; i < 10000; i++)//prints the array of characters.
{
if(file[i] == NULL)//keeps from printing uninitialized parts of array
{
break;
}
else
{
if (counter % 80 == 0) //makes it print 80 characters per line
{
printf("\n");
}
printf("%c", file[i]);//prints the character
counter++;
}
}
I know I can use fscanf somehow and I know it should be much simpler than this. I just need a *pointer (pun intended) in the right direction!
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char c;
fp = fopen("sample.txt", "r");
if (fp == NULL) {
printf("Couldn't open file for reading.\n");
exit(0);
}
while (fscanf(fp, "%c",&c) != EOF)
{
if (isalpha(c))
printf("%c", c);
}
printf("\n");
return 0;
}
Technially, you can use fscanf to ignore directly (the * suppresses assignment):
fscanf(in, "%*[^A-Za-z]%c", &c);
It's not incredibly useful in this case - intpreted format strings are slow. I would just use fgetc.

I cannot read more than a 49 structs array from a file

I've got this code. When I compile and execute this, no error is displayed, but, since the 50th element until the last one, the values are out of the interval of rand() (which is, i think, from 0 to 32767). It was quite unexpected, because the program continues without showing any writing-error message.
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#define MAX 100
using namespace std;
struct num {
int val;
};
int main() {
FILE *f, *g;
num data[MAX];
f = fopen("file1.txt", "w");
if(f == NULL) {
printf("Error\n");
exit(0);
} else {
for(int i = 0; i < MAX; i++) {
data[i].val = rand();
}
fwrite(data, sizeof(num), MAX, f);
if(ferror(f)) {
exit(0);
}
fclose(f);
}
num data1[MAX];
g = fopen("file1.txt", "r");
if(g == NULL) {
exit(0);
} else {
fread(data1, sizeof(num), MAX, g);
if(ferror(g)) {
printf("Error\n");
exit(0);
}
fclose(g);
for(int i = 0; i < MAX; i++) {
printf("val %d : %d\n", i+1, data1[i].val );
}
}
}
I Think issue with the file opening mode, you have chosen text mode which is system dependent, change it to binary mode and everything would work as expected.
Text mode is depending on the environment where the application runs, some special character conversion may occur in different input/output text according to a system-specific text . Although on same environments no conversions will occur for binary file mode. Binary mode save and read your data without any conversion.
There is no problem with your code.
Add the following at the end of main to check any mismatched values.
for (int i = 0; i < MAX; ++i )
{
if ( data[i].val != data1[i].val )
{
printf("The %d-th value does not match after reading from file.\n", i);
}
}

Resources