I'm trying to write a program of a basic shell that exits at EOF and handles SIGINT without quitting. It was working within one functionless c file but upon creating functions I ran into an error where EOF and SIGNINT would cause seg faults. I'm unsure what's causing this and hoping that someone will spot something I did not. Below is the function I think causing the issue, its the first thing done after the Shell reads a command (or where I'm applying signals).
int check_redirect(char *line, int flag)
{
int n = 0;
if (line == NULL) return (flag);
else
{
do
{
if (line[n] == '>') flag = 1;
n++;
}while (line[n] != '\n');
}
return (flag);
}
This is where the function is called:
char buffer[15];
time_t now = time(NULL);
strftime(buffer, 15, "[%d/%m %H:%M]", localtime(&now) );
fprintf(stdout, "%s # ", buffer);
signal(SIGINT, SIG_IGN);
read = getline (&line, &len, stdin);
signal(SIGINT, SIG_DFL);
flag = check_redirect(line, flag);
Hopefully this is clear enough. This is at the beginning of a loop, which is looking for input to execute as a command, having printed out the prompt (date and time #). The commands work mostly but act as if they are lost occasionally, which I think may be related to this signal processing error.
Thanks in advance.
Seg fault can occur from an infinite loop
You have the potential for an infinite loop here:
... }while (line[n] != '\n');
Suggest checking for more than just the \n character.
You could use strstr(line, "\n"); to verify newline exists before entering loop, for example.
Change your code to
int check_redirect(char *line, int flag, int len)
{
int n = 0;
if (line != NULL)
{
do
{
if (line[n] == '>')
{
flag = 1;
}
n++;
}
while ((line[n] != '\n') && (n<len))
}
return (flag);
}
call it with:
flag = check_redirect(line, flag, read);
Related
Currently, I have a parent process piping information to children and reading out of their STDOUT through fgetc. I iterate through rounds and store the file pointer from the first round using fdopen and call it again in the next round.
The issue is, in the first round the read_line results in a perfectly fine read, but in the second round the fgetc hits a segfault.
Error Message:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7791330 in __GI___uflow (fp=0x6041b0) at genops.c:381
381 return *(unsigned char *) fp->_IO_read_ptr++;
Reading Function: (I know it's not the best)
To Clarify. It works the first time. Why won't it work the second?
Many other questions seem to just have a different error.
char* read_line(FILE* file,int currentP, Players *players) {
char* result = malloc(sizeof(char) * 80);
int position = 0;
int next = 0;
for (int i = 0; i < players->players[currentP].position; i++) {
next = fgetc(file);
}
while (1) {
next = fgetc(file);
//players->players[currentP].position++;
if (next == '!') {
return "!";
}
if (next == EOF || next == '\n') {
result[position] = '\0';
return result;
} else {
result[position++] = (char)next;
}
}
}
I made a program that I then used to create a file from its output, now I want to make one of several programs to run redirecting that file (or piping the output of the other programs to it).
I used the following code as a test for the first program
int main (int argc, char* argv[])
{
long long int n = 0;
char str[100];
while (str != NULL)
{
fscanf(stdin,"%s\0", str);
printf("%lld\t%s\n", n, str);
n++;
}
return 0;
}
The program executes correctly until the last line of the redirected file or piped output, which then keeps repearing infinitely until I stop the execution with ctrl-c (Windows).
I don't know why this happens, I tried flushing stdin, stdout and everything that I had think of and no luck.
What I am doing wrong or missing ?
Thanks in advance.
char str[100];
while (str != NULL)
str is treated as a pointer to the first character in the array, so its value never changes, which means the loop will never terminate.
while (str != NULL)
{
fscanf(stdin,"%s\0", str);
printf("%lld\t%s\n", n, str);
n++;
}
replaced by
while (scanf("%s", str) != EOF)
{
printf("%lld\t%s\n", n, str);
n++;
}
Solved the problem.
Its just a program where i am trying to read the number of occurrences of the word passed as an argument in the file which is also passed as next argument.
Code looks like below :
#include <stdio.h>
extern void exit(int);
void main(int argc, char *argv[]) {
char C, buf[10];
int count = 0, i = 0;
FILE *fp;
if ( argc != 3 ) {
printf("Please provide proper args\n");
exit(0);
}
fp = fopen(argv[2], "r");
if( fp == NULL ) {
printf("File couldn't be opened\n");
exit(1);
}
else {
do {
while ( (C = fgetc(fp)) && ( C != ' ') ) {
//if ( C == EOF ) break;
buf[i] = C;
i++;
}
buf[i] = '\0';
i = 0;
if (strcmp(argv[1], buf) == 0) count++;
} while (C != EOF );
}
close(fp);
printf("Total \"%s\" occurances is %d\n", argv[1], count);
}
GDB :
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a925fd in getc () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.18-12.fc20.x86_64
Now it will not segfault if i remove the comment before the "if" statement after the first while. The only line in this program is commented.
But if that is the case then we have definitely crossed the getc part and entered into the while. I was thinking that the problem could be while doing the strcmp. But why do i see the error in getc in gdb.
With the check for end of file outcommented you actually have an infinite loop, where fgetc continues to return EOF but you never check for it. This leads to undefined behavior because you will then write well beyond the bounds of the buf array. What probably happens is that you then overwrite the FILE pointer fp, casing the next call to fgetc to operate on an invalid FILE pointer.
Also as noted in another comment, fgets returns an int. You need to know that (char) EOF != (int) EOF.
I am new to C and I am trying to create a simple C shell that will allow the user to perform various functions like chdir, cd, exit, mkdir.
I've posted my code below. Can anyone look through it and see what I am doing wrong? I am not sure if I am using fork and execcv correctly. Thanks!
include stdio.h
include stdlib.h
include unistd.h
include <string.h>
include sys/types.h
main() {
//char *user;
//if ((user = getlogin()) == NULL)
// perror("__getlogin1() error");
//else printf("__getlogin1() returned %s\n", user);
int j, status;
int pid, c_pid;
int i = 0;
char *tmp, **ap;
char instring[80]; // store one line of input
char *argv[10]; // store parameters in the format for execv()
promptstart:
printf("Please enter a commcand:\n");
// read a char at a time and put it in instring[]
// put a '\0' at the end
instring[i] = getc(stdin); // stdin is the keyboard
while (instring[i] != '\n') {
i++;
instring[i] = getc(stdin);
}
instring[i] = '\0'; // replace '\n' with '\0'
tmp = instring;
i = 0;
argv[i] = strsep(&tmp, " \t"); // put first word int argv[0]
while ((i < 10) && (argv[i] != '\0')) {
i++;
argv[i] = strsep(&tmp, " \t");
}
// print out the command and options.
i = 0;
while (argv[i] != '\0') {
printf("your entered: %s\n", argv[i++]);
}
//PLACE ERROR HERE
if ((c_pid = fork()) == 0) {
for (j = 0; j < 10; j++)
printf("child (%d) prints %d\n", getpid(), j);
exit(0);
} else if (c_pid > 0) {
c_pid = wait(&status);
printf("child %d exited with status %d\n", c_pid, status);
} else {
execvp(argv[0], argv);
}
goto promptstart;
}
At least IMO, you're putting far too much into main. I'd start with something like:
int main() {
char input[128];
do {
fgets(stdin, input, sizeof(input));
dispatch(input);
} while (strcmp(input, "exit"));
return 0;
}
Then dispatch will look for internal commands, and only do an exec when/if it's given a command it doesn't recognize. To keep things simple to start with, you might consider using popen to execute external commands, and leave switching to a "raw" fork/exec for later, when the limitations of popen start to cause you problems.
For shell builtins (man bash), you probably don't want to fork/exec. I would save fork/exec for running programs that are in your PATH (an environment variable that your shell will have to manage). The shell itself should interface with the filesystem through commands like chdir (man 2 chdir).
Consider using a nice string tokenizer (or just fallback to strtok) for parsing the commandline, and as another comment suggests, abstract that into a function so that your main loop is lean.
EDIT:
complete code with main is here http://codepad.org/79aLzj2H
and once again this is were the weird behavious is happening
for (i = 0; i<tab_size; i++)
{
//CORRECT OUTPUT
printf("%s\n", tableau[i].capitale);
printf("%s\n", tableau[i].pays);
printf("%s\n", tableau[i].commentaire);
//WRONG OUTPUT
//printf("%s --- %s --- %s |\n", tableau[i].capitale, tableau[i].pays, tableau[i].commentaire);
}
I have an array of the following strcuture
struct T_info
{
char capitale[255];
char pays[255];
char commentaire[255];
};
struct T_info *tableau;
This is how the array is populated
int advance(FILE *f)
{
char c;
c = getc(f);
if(c == '\n')
return 0;
while(c != EOF && (c == ' ' || c == '\t'))
{
c = getc(f);
}
return fseek(f, -1, SEEK_CUR);
}
int get_word(FILE *f, char * buffer)
{
char c;
int count = 0;
int space = 0;
while((c = getc(f)) != EOF)
{
if (c == '\n')
{
buffer[count] = '\0';
return -2;
}
if ((c == ' ' || c == '\t') && space < 1)
{
buffer[count] = c;
count ++;
space++;
}
else
{
if (c != ' ' && c != '\t')
{
buffer[count] = c;
count ++;
space = 0;
}
else /* more than one space*/
{
advance(f);
break;
}
}
}
buffer[count] = '\0';
if(c == EOF)
return -1;
return count;
}
void fill_table(FILE *f,struct T_info *tab)
{
int line = 0, column = 0;
fseek(f, 0, SEEK_SET);
char buffer[MAX_LINE];
char c;
int res;
int i = 0;
while((res = get_word(f, buffer)) != -999)
{
switch(column)
{
case 0:
strcpy(tab[line].capitale, buffer);
column++;
break;
case 1:
strcpy(tab[line].pays, buffer);
column++;
break;
default:
strcpy(tab[line].commentaire, buffer);
column++;
break;
}
/*if I printf each one alone here, everything works ok*/
//last word in line
if (res == -2)
{
if (column == 2)
{
strcpy(tab[line].commentaire, " ");
}
//wrong output here
printf("%s -- %s -- %s\n", tab[line].capitale, tab[line].pays, tab[line].commentaire);
column = 0;
line++;
continue;
}
column = column % 3;
if (column == 0)
{
line++;
}
/*EOF reached*/
if(res == -1)
return;
}
return ;
}
Edit :
trying this
printf("%s -- ", tab[line].capitale);
printf("%s --", tab[line].pays);
printf("%s --\n", tab[line].commentaire);
gives me as result
-- --abi -- Emirats arabes unis
I expect to get
Abu Dhabi -- Emirats arabes unis --
Am I missing something?
Does printf have side effects?
Well, it prints to the screen. That's a side effect. Other than that: no.
is printf changing its parameters
No
I get wrong resutts [...] what is going on?
If by wrong results you mean that the output does not appear when it should, this is probably just a line buffering issue (your second version does not print newline which may cause the output to not be flushed).
It's highly unlikely that printf is your problem. What is far, far more likely is that you're corrupting memory and your strange results from printf are just a symptom.
There are several places I see in your code which might result in reading or writing past the end of an array. It's hard to say which of them might be causing you problems without seeing your input, but here are a few that I noticed:
get_lines_count won't count the last line if it doesn't end in a newline, but your other methods will process that line
advance will skip over a newline if it is preceded by spaces, which will cause your column-based processing to get off, and could result in some of your strings being uninitialized
get_word doesn't do any bounds checks on buffer
There may be others, those were just the ones that popped out at me.
I tested your code, adding the missing parts (MAX_LINE constant, main function and a sample datafile with three columns separated by 2+ whitespace), and the code works as expected.
Perhaps the code you posted is still not complete (fill_table() looks for a -999 magic number from get_word(), but get_word() never returns that), your main function is missing, so we don't know if you are properly allocating memory, etc.
Unrelated but important: it is not recommended (and also not portable) to do relative movements with fseek in text files. You probably want to use ungetc instead in this case. If you really want to move the file pointer while reading a text stream, you should use fgetpos and fsetpos.
Your approach for getting help is very wrong. You assumed that printf had side effects without even understanding your code. The problem is clearly not in printf, but you held information unnecessarily. Your code is not complete. You should create a reduced testcase that compiles and displays your problem clearly, and include it in full in your question. Don't blame random library functions if you don't understand what is really wrong with your program. The problem can be anywhere.
From your comments, i am assuming if you use these printf statements,
printf("%s\n", tableau[i].capitale);
printf("%s", tableau[i].pays);
printf("%s\n", tableau[i].commentaire);
then everything works fine...
So try replacing your single printf statement with this. (Line no. 173 in http://codepad.org/79aLzj2H)
printf("%s\n %s %s /n", tableau[i].capitale, tableau[i].pays, tableau[i].commentaire);