Problem with a feature in a shell program - c

This Program works fine with exception for one aspect of smarthistory(). I cannot figure out why when I enter a command number from the smarthistory array why it doesn't execute. After entering the command to execute from the list nothing happens not even the printf statement right after. I am using the gcc compiler.
const int MAX_HISTORY=100;
const int MAX_COMMAND_LENGTH=64;
char history[100][64];
int historyCount = 0;
char smarthistory[100][64];
int smarthistoryCount=0;
void chopnl(char *s) { //strip '\n'
s[strcspn(s, "\n")] = '\0';
}
void printHistory() {
int i;
for (i = 0; i < historyCount; i++)
printf("%d | %s\n", i, history[i]);
}
void printSmartHistory() {
int i;
for (i = 0; i < smarthistoryCount; i++)
printf("%d | %s\n", i, smarthistory[i]);
}
void isPartialMatch(char *commandQuery,char *history, int historyString)
{
int lengthOfQuery=strlen(commandQuery);
if(strncmp( history, commandQuery,lengthOfQuery)==0)
{
memcpy(smarthistory[smarthistoryCount++], history, strlen(history) + 1);
}
else
return;
}
void smartHistory()
{
char commandQuery[MAX_COMMAND_LENGTH];
int commandNumber=0;
int i=0;
printHistory();
printf("enter partial command:> ");
fgets(commandQuery,MAX_COMMAND_LENGTH,stdin);
chopnl(commandQuery);
//printf("%d", strlen(commandQuery));
for(i=0;i<=historyCount;i++)
{
isPartialMatch(commandQuery, history[i], i);
}
printf("SmartHistory Search Results\n");
printSmartHistory();
printf("enter a command number to execute:> ");
scanf("%d", commandNumber);
//chopnl(commandNumber);
printf("command entered >");
handleCommand(smarthistory[commandNumber]);
}
void placeInHistory(char *command) {
// printf("command:> %s |stored in:> %d",command,historyCount );
memcpy(history[historyCount++], command, strlen(command) + 1);
}
int main(int argc, char** argv) {
char command[MAX_COMMAND_LENGTH];
while (1) {
printf("SHELL:>");
fgets(command, MAX_COMMAND_LENGTH, stdin);
chopnl(command);
if (strcmpi(command, "exit") == 0)
return EXIT_SUCCESS;
placeInHistory(command);
handleCommand(command);
}
return (EXIT_SUCCESS);
}
int handleCommand(char *command) {
pid_t pid;
int test=0;
pid = fork();
if (pid > 0) {
wait(&test);
} else if (pid == 0) {
execCommand(command);
exit(0);
} else {
printf("ERROR");
}
}
int execCommand(char *command) {
//system(command);
if (strcmpi(command, "history") == 0)
{
printHistory();
}
else if(strcmpi(command, "smarthistory") == 0)
{
smartHistory();
}
else if (strcmpi(command, "ls") == 0 || (strcmpi(command, "pwd") == 0)) {
char *path[] = {"/bin/", NULL};
strcat(path[0], command);
execve(path[0], path, NULL);
}else{system(command);}
}

Recheck this:
char *path[] = {"/bin/", NULL};
strcat(path[0], command);
path[0] is initialised with const char*, and you cannot use strcat() on that.
Another one:
memcpy(smarthistory[smarthistoryCount++], history, strlen(history) + 1);
shouldn't both, source and destination, be of the same type, char*?
Also, I'd suggest to use char* history[MAX_HLEN]
instead of
char history[x][y].

I'm not sure, but it looks like the handling of "history" and "smarthistory" should be moved from execCommand to handleCommand and should occur INSTEAD OF forking. There may be other bugs.

Problem solved the problem was I needed to add an '&' before commandNumber in the statement: scanf("%d", commandNumber); located in the smartHistory function. What strikes me as odd is I made another version of this where I moved the "history" and "smarthistory" into the handle command function instead of the execCommand function. When I did this the program prints the shell prompt 3 times...if anyone knows why, please let me know. However in the version above just adding the '&' works great.

Related

Recursive inverting

i made this code for my college lesson:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
int palindromoR(int i, int f, char *s)
{
if (f - i <= 0)
return 1;
if (s[i] != s[f]) {
return 0;
} else {
return palindromoR(i+1, f-1, s);
}
}
void palindromo(char *s)
{
int saida = palindromoR(0, strlen(s) - 1, s);
if (saida)
{
printf("eh palindromo\n");
}
else
{
printf("nao eh palindromo\n");
}
}
void inversaR(char *str)
{
static int i=0;
int tam = strlen(str) - i;
char temp;
if (tam +1 == 0)
return;
temp = str[tam];
printf ("%c",temp);
i++;
return inversaR (str);
}
void inversa(char *s)
{
inversaR(s);
printf("\n");
}
unsigned long stirlingR(unsigned long n, unsigned long k)
{
// implemente essa função recursiva
return 0;
}
void stirling(int n, int k)
{
printf("%lu\n", stirlingR(n, k));
}
void padraoR(unsigned n)
{
}
void padrao(unsigned n)
{
padraoR(n);
printf("\n");
}
int main(int argc, char *argv[])
{
char file_name[MAX], aux[MAX];
FILE *entrada;
int t, a, b;
scanf("%s", file_name);
entrada = fopen(file_name, "r");
if (entrada == NULL)
{
printf("Nao encontrei o arquivo!");
exit(EXIT_FAILURE);
}
fscanf(entrada, "%d", &t);
if (t < 1 || t > 4)
{
printf("Parametros incorretos.\n");
printf("Ex:\n");
printf("tp01_recursao 1 [para testar palindromo]\n");
printf("tp01_recursao 2 [para testar inversa]\n");
printf("tp01_recursao 3 [para testar Stirling]\n");
printf("tp01_recursao 4 [para testar padrao]\n");
}
if (t == 1)
{
printf("\nTestando palindromo()\n\n");
fscanf(entrada, "%s", aux);
while (aux[0] != '.')
{
palindromo(aux);
fscanf(entrada, "%s", aux);
}
}
else if (t == 2)
{
printf("\nTestando inversa()\n\n");
fscanf(entrada, "%s", aux);
while (aux[0] != '.')
{
inversa(aux);
fscanf(entrada, "%s", aux);
}
}
else if (t == 3)
{
printf("\nTestando Stirling()\n\n");
fscanf(entrada, "%d %d", &a, &b);
while (a != -1)
{
stirling(a, b);
fscanf(entrada, "%d %d", &a, &b);
}
}
else if (t == 4)
{
printf("\nTestando padrao()\n\n");
fscanf(entrada, "%d", &a);
while (a != -1)
{
padrao(a);
fscanf(entrada, "%d", &a);
}
}
return 0;
}
My function inversaR seems to work when i try like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void inversaR(char *str)
{
static int i=0;
int tam = strlen(str) - i;
char temp;
if (tam +1 == 0)
return;
temp = str[tam];
printf ("%c",temp);
i++;
return inversaR (str);
}
void inversa(char *s)
{
inversaR(s);
printf("\n");
}
int main (){
char teste[100] = "alucard";
inversa(teste);
return 0;
}
the code above gives me the answer "dracula" as expected, but when trying with the first code it cuts the strings in 2 characters. The archive it's reading contains the following strings:
2
ab
gato
minerva
alucard
.
I tried to chance it using the function strrev() and it seems to work just fine, otherwise the same problem kept blowing my mind.
I tried out your code and saw the quirky chopped output you referred to. Doing some debugging of the code led me to the issue with your static variable, "i" in the recursive "inversaR" function. After each call with a different string, the value was left with an arbitrary value from the previous recursive call set which was giving either a partial string or no string at all.
With that it was apparent that when the final character had been printed and the return back up the function call stack was occurring, this variable needed to be reset. Following is a refactored version of the function.
void inversaR(char *str)
{
static int i=0;
int tam = strlen(str) - i;
char temp;
//printf("tam: %d\n", tam);
if (tam +1 == 0)
{
i = 0; /* This needs to be reset when returning up the recursive call stack */
return;
}
temp = str[tam];
printf ("%c",temp);
i++;
return inversaR (str);
}
Also, just to clarify to anyone testing this program, I added a "printf" statement prior to the prompt for a file name so that it is clear what is wanted for input.
printf("File name: "); /* Added to alert the user to enter a file name */
scanf("%s", file_name);
entrada = fopen(file_name, "r");
With those two bits of code refactored, and having created a text file with the sample string set, the following terminal output was created.
#Vera:~/C_Programs/Console/Dracula/bin/Release$ ./Dracula
File name: Test.txt
Testando inversa()
ba
otag
avrenim
dracula
All of the strings appear to have been properly reversed. Go ahead and try out these program tweaks and see if they meet the spirit of your project.

Issue with libreadline when completing an upper case directory

this is a piece of a shell that I'm creating. I'm having some trouble using libreadline, because when the shell gets loaded and I try to cd in a directory using autocompletion (so pressing TAB), after I press enter I access the directory but I get some strange output before another prompt is printed. I noticed that this happens only when the name of the directory starts with an upper case letter.
Example: "user:: ~ % cd Github" <-- written pressing tab to autocomplete Github
Next prompt is: "8b�/�user :: Github %"
I really cannot understand why, this is something really strange for me.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <readline/history.h>
#include <readline/readline.h>
#include "flush.h"
#include "env.h"
#include "fget.h"
char * flush_builtins[] =
{
"cd",
"help",
"exit",
"ver",
"fget"
};
int flush_num_builtins() {
return sizeof(flush_builtins) / sizeof(char *);
}
int (*flush_func[]) (char **) =
{
&flush_cd,
&help,
&exit_flush,
&ver,
&fget
};
static int flush_startp(char **args)
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "flush: command not found\n");
}
exit(1);
}
else if (pid < 0)
{
fprintf(stderr, "flush: command not found\n");
}
else
{
do
{
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
static int flush_exec(char **args)
{
int i;
if (args[0] == NULL)
{
return 1;
}
for (i = 0; i < flush_num_builtins(); i++)
{
if (strcmp(args[0], flush_builtins[i]) == 0) {
return (*flush_func[i])(args);
}
}
return flush_startp(args);
}
static char * flush_read(void)
{
fflush(stdout);
char *line_read = malloc(sizeof(char) * LINE_BUF);
char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);
strcat(prompt, get_user());
strcat(prompt, " :: ");
if (strcmp(current, get_home()) == 0)
{
strcat(prompt, "~");
}
else
{
strcat(prompt, get_cwd());
}
strcat(prompt, " % ");
line_read = readline(prompt);
if (line_read && *line_read)
{
add_history(line_read);
}
return line_read;
free(prompt);
free(line_read);
free(current);
}
static char **flush_getargs(char * line)
{
int bufsize = TOK_BUF;
int i = 0;
char **tokens = malloc(bufsize * sizeof(char *));
char **token;
if (!tokens)
{
fprintf(stderr, "allocation error\n");
exit(1);
}
token = strtok(line, DELIM);
while (token != NULL)
{
tokens[i] = token;
i++;
token = strtok(NULL, DELIM);
}
tokens[i] = NULL;
return tokens;
}
static void flush_loop(void)
{
char *line;
char **args;
int status;
do
{
line = flush_read();
args = flush_getargs(line);
status = flush_exec(args);
free(line);
free(args);
} while (status);
}
static void handler(int num)
{
signal(SIGINT, handler);
flush_loop();
fflush(stdout);
}
int main()
{
init();
signal(SIGINT, handler);
flush_loop();
return 0;
}
You can not use strcat with a non \0 terminated string:
char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);
strcat(prompt, get_user());
Use strcpy instead of strcat, or calloc instead of malloc

How to implement history function?

I am new to C programming and currently learning this into a course. I'm facing an issues while trying to practice the below history function.
I'm able to display the shell commands. However, when I type history, the past shell commands are not getting saved into the history buffer.
Can anyone help me to find where I went wrong?
Here is my code:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define BUFSIZE 20
#define MAX_WORD_IN_LINE 20
int tokenize(char *str, char **args)
{
int i, argc = 0;
char *token;
token = strtok(str," \t\n");
for(i=0; token!=NULL;i++)
{
args[i] = token;
printf("args[%d] = %s\n", i, args[i]);
token = strtok(NULL, " \t\n");
argc++;
}
return argc;
}
void display_strings(char **p)
{
if (p == NULL) return;
while(*p != NULL){
printf("%s\n",*p);
p++;
}
}
int history(char *hist[], int current){
int i = current;
int hist_num = 1;
do {
if (hist[i]) {
printf("%4d %s\n", hist_num, hist[i]);
hist_num++;
}
i = (i + 1) % BUFSIZE;
} while (i != current);
return 0;
}
int main(void){
char *args[MAX_WORD_IN_LINE];
char buffer[BUFSIZE];
char *hist[BUFSIZE];
int i,current=0;
pid_t pid;
int argc;
for(i=0;i<BUFSIZE;i++)
hist[i]= NULL;
while(1) {
memset(args,0,MAX_WORD_IN_LINE);
printf("osh> ");
fgets(buffer, BUFSIZE, stdin);
argc = tokenize(buffer, args);
//display_strings(args);
// skip on empty command
if (argc == 0) continue;
if (strcmp(args[0],"quit") == 0) break;
else if (strcmp(args[0], "hello") == 0) printf("Hello there. How are you?\n");
else if (strcmp(args[0],"history")==0) history(hist,current);
else {
pid = fork();
if (pid == 0) {
hist[current]=strdup(args[0]);
current++;
execvp(args[0], args);
return 0;
}
You need to make a copy of the string that args[0] points to when you save it in hist. Currently, you're just assigning the pointer to the current args[0], and it will be overwritten by the next command. When you print the history, you'll just get the last command repeatedly. So use:
hist[current] = strdup(args[0]);

IO redirection and buffer issues, fflush and c

for my class we are to implement a shell with output redirection. I have the output redirection working, except my first command is always corrupted see:
$ echo this doesn't work
H<#?4echo
No such file or directory
$ echo this does work
this does work
but every command afterwards seems fine. What technique do I use to find the bug that is causing this problem?
I think it has something to do with not fflushing properly. I sprinkled it around my code (which was stupid) to see if it would help during the loop but it did not. I've also tried printing out my OrderedIds list which is just a list of commands to check if I could find H<#?4 anywhere, but even when I initialized it, it did not work.
Thanks for your help.
#define LENGTH 1000
#define MAXCMD 11
#define MAX_STR_LEN 20
void init(char *temp);
void clean(char **orderedIds);
void init_pid(int *temp);
void reap(int *temp,int ret_status);
void jobs(int *pid_list, char **orderedIds);
int ioRedir(char **orderedIds);
void reap(int *temp,int ret_status){//chainsaws all zombies
int a;
for (a=0; a<LENGTH; a++ ){
waitpid(temp[a],&ret_status,WNOHANG) == temp[a];
}
}
void init(char *temp){//Function to initialize/reset the cmd array
int i;
for(i=0; i<LENGTH; i++){
temp[i] = 0;
}
}
void init_pid(int *temp){//Function to initialize/reset the pid list
int i;
for(i=0; i<LENGTH; i++){
temp[i] = -777;
}
}
void clean(char **orderedIds){//garbage collection
int i;
for(i=0; i<MAXCMD; i++){
free(orderedIds[i]);
}
free(orderedIds);
}
void jobs(int *pid_list, char **orderedIds){//function to check for background proccesses
printf("Jobs:\n");
int y;
for(y=0; y<LENGTH; y++){
if(kill(pid_list[y], 0) == 0){
printf("%d\n", pid_list[y]);
}
}
clean(orderedIds);
printf("$ ");
}
int ioRedir(char **orderedIds){
int i;
for ( i = 0; i<MAXCMD; i++){
if(orderedIds[i] == NULL){
return -1;
}
if(strcmp(orderedIds[i],">")==0){
return (i+1);
}
}
}
int main (int argc, char *argv[], char *envp[])
{
char temp[LENGTH];
char * tok;
char c = '\0';
int saved_stdout;
int pid_list[LENGTH];
int ret_status;
int numFile;
int pid_counter = 0;
int outputfd = -1;
char outputFile[MAX_STR_LEN];
pid_t pid;
printf("$ ");
int i, j, y, background= 0;
init_pid(pid_list);
while(c !=EOF) { //while not ^D // Source: LinuxGazzette Ramankutty
outputfd = -1;
fflush(0);
c = getchar();
if(c=='\n'){ //entered command
reap(pid_list, ret_status);
char **orderedIds = malloc(MAXCMD * sizeof(char*));
for (i=0; i<MAXCMD; i++){
orderedIds[i] = malloc(MAXCMD * sizeof(char*));
}
int k=0;
tok = strtok(temp, " \n\t\r");
while (tok !=NULL){
strcpy(orderedIds[k], tok);
k++;
tok = strtok (NULL, " \n\t\r");
}
orderedIds[k] = NULL; //END with NULL
init(temp); //initialize the array
if(orderedIds[0] ==NULL){
printf("\n$ ");
continue;
}
numFile = ioRedir(orderedIds);
if(strcmp(orderedIds[0],"exit")==0){// if exit
printf("now exiting...\n");
break;
}
if(strcmp(orderedIds[k-1], "&")==0){//if background
orderedIds[k-1] = NULL;
background = 1;
}else background = 0;
if(strcmp(orderedIds[0], "jobs") == 0){//if jobs command
jobs(pid_list, orderedIds);
continue;
}
if(strcmp(orderedIds[0], "cd") == 0){ //if change directory command
chdir(orderedIds[1]);
printf("$ ");
continue;
}
pid = fork();
if (pid!=0 && background == 1)
{
//go to end of list in pid and put it in
pid_list[pid_counter] = pid;
pid_counter++;
printf("To the background: %d\n", pid);
} else if (pid==0 && background == 1) {
fclose(stdin); //close child's stdin
fopen("/dev/null", "r"); //open a new stdin that is always empty.
if(execvp(orderedIds[0], orderedIds)){
printf("%s\n", orderedIds[0]);
puts(strerror(errno));
exit(127);
}
}
if (pid != 0 && !background){
//printf("Waiting for child (%d)\n", pid);
fflush(0);
pid = wait(&ret_status);
} else if (pid == 0 && !background) {
if(numFile > 0){
strncpy(outputFile, orderedIds[numFile], strlen(orderedIds[numFile]));
numFile = 0;
//open the output file
outputfd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRGRP | S_IROTH);
if (outputfd < 0) {
exit(EXIT_FAILURE);
}
//close STDOUT
if(close(STDOUT_FILENO) < 0 ){
perror("close(2) file: STDOUT_FILENO");
close(outputfd);
exit(EXIT_FAILURE);
}
//use dup to rerout the output
if(saved_stdout = dup(outputfd) != STDOUT_FILENO){
perror("dup(2)");
close(outputfd);
exit(EXIT_FAILURE);
}
close(outputfd);
}
if (execvp(orderedIds[0], orderedIds)){
printf("%s\n", orderedIds[0]);
puts(strerror(errno));
exit(127);
}
}
dup2(saved_stdout,outputfd);
clean(orderedIds);
fflush(0);
printf("$ ");
} else {
strncat(temp, &c, 1);
}
}
fflush(0);
return 0;
}
The reason for the garbage is that you never initialized temp to an empty string at the beginning of main(). You call init(temp) after processing each command.
There are lots of other problems in your code:
orderedIds[i] = malloc(MAXCMD * sizeof(char*));
Since orderedIds[i] is an array of char, not char*, you should multiply the size by sizeof(char). Also, it's not clear why you're using MAXCMD as the size -- on the previous line this was the maximum number of words on a line, not the number of characters in a word.
strcpy(orderedIds[k], tok);
You should use strncpy() to ensure that you don't copy more than the size of orderedIds[k].
Another option would be not to preallocate all the orderedIds[i] in the first place. Instead of using strcpy(), use strdup() and assign this to orderedIds[k]; if you do this, you have to remember to free() all these strings.
A third option is not to copy the strings at all. Just assign the pointers returned by strtok() to orderedIds[k]. But in this case you mustn't call init(tmp) until after you've forked.
strncpy(outputFile, orderedIds[numFile], strlen(orderedIds[numFile]));
The limit should be the size of outputFile, not the length of orderedIds[numFile]. strncpy() will never copy more than the length of the source, you need to tell it the maximum size of the destination to prevent a buffer overflow.
outputfd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRGRP | S_IROTH);
if (outputfd < 0) {
exit(EXIT_FAILURE);
}
You should call perror() to report the reason that open() failed.
puts(strerror(errno));
Call perror(), like you do elsewhere.

Create a file or go to line n of that file

I've been working on this C programming assignment and I just can't seem to find out why it is not behaving the way I expect it to be behaving. The program is supposed to run, and there are 3 possible options for commands 0, -n, and n, where n is a positive integer.
When I execute the program, and type 0 as the command (which should create a file if one has not already been created) it just loops back to asking me to enter a command.
The commands -n and n go to the line specified by n and seeks to it. n prints line n, whereas -n prints all lines from n onward until it can no longer read from the text file.
Would greatly appreciate it if somebody could give me a hint or two and steer me in the right direction.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define BUFSIZE 256
#define LINESIZE 1024
int write(FILE *fp);
int grade_validation(int grade);
int id_validation(char studentid[]);
int display(FILE *fp, int cmd);
int display_all(FILE *fp, int cmd);
int main(int argc, char *argv[]) {
if(argc != 2) {
perror("invalid number of args");
return 1;
} else {
FILE *fp;
if((fp = fopen(argv[1], "wb+")) == 0) {
perror("fopen");
return 1;
}
write(fp);
fclose(fp);
}
return 0;
}
int write(FILE* fp) {
int grade;
char studentid[BUFSIZE];
char input[BUFSIZE];
int cmd;
while(1) {
printf("Input 0 to append, n to view, or -n to view all records starting from n.\n");
if(!fgets(input, LINESIZE, stdin)) {
clearerr(stdin);
return 0;
}
if((sscanf(input, "%d", &cmd) == 1)) {
if(cmd == 0) {
if((id_validation(studentid)) != 1 || (grade_validation(&grade) == -1)) {
continue;
}
fprintf(fp, "%s %3d ", studentid, grade);
} else if(cmd > 0) {
display(fp, cmd);
} else if(cmd < 0) {
display_all(fp, cmd);
}
}
}
}
int grade_validation(int grade) {
char input[BUFSIZE];
FILE *fp;
if(grade >= 0 && grade <= 100) {
return 1;
}
if(sscanf(input, "%d", &grade) == 1) {
if((grade_validation(grade)) == 1) {
if(fprintf(fp, "%3d", grade) == 0) {
return 0;
}
printf("Grade recorded successfully.\n");
return 1;
}
}
return 0;
}
int id_validation(char studentid[]) {
char input[BUFSIZE];
FILE *fp;
size_t i = 0;
if(strlen(studentid) == 9) {
for(i = 0; i < 9; i++) {
if(isdigit(studentid[i])) {
return 1;
}
}
}
if(sscanf(input, "%s", studentid) == 1) {
if((id_validation(studentid)) == 1) {
if(fprintf(fp, "%s", studentid) == 0) {
return 0;
}
printf("Student ID recorded successfully.\n");
return 1;
}
}
return 0;
}
int display(FILE *fp, int cmd) {
char studentid[BUFSIZE];
int grade;
if(!fgets(cmd, BUFSIZE, fp)) {
clearerr(stdin);
return 0;
}
fseek(fp, cmd, SEEK_SET);
fprintf(stderr, "%s %3d", studentid, grade);
}
int display_all(FILE *fp, int cmd) {
char studentid[BUFSIZE];
int grade;
if(!fgets(cmd, BUFSIZE, fp)) {
clearerr(stdin);
return 0;
}
fseek(fp, cmd, SEEK_SET);
while((sscanf(studentid, grade, "%s %3d", cmd)) != EOF) {
fprintf(stderr, "%s %3d", studentid, grade);
}
}
You're trying to validate the uninitialized studentid and grade.
Also, you pass &grade to grade_validation, which expects and integer.
This would cause a warning, which should make you figure out something is wrong. Always compile with warnings enabled, and treated as errors (in gcc, -Wall -Werror).

Resources