user2965601
user2965601

Reputation:

C - A simple shell on linux - Some trouble with commands

I wrote this simple shell so far. But I got some trouble with my shell. For example, when I try to open a pdf file via the command "evince pdffile.pdf", the actual pdfile does not get opened. The pdf viewer runs but the actual file with the whole content never appears. Or another example is the command "ls -l". I don't get the files and folders listed as it should be, but "ls" is working. Another example is gedit, and so on. Also, I should mention. I am not using "system()", because system() would do everything and I would not have something to do. Instead, I am using "execvp()". Here is the code. I hope, you may find the problem, because I have no clue what the problem is causing.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"

void exec_cmd (char *buf);

int main() {    
   char line[MAX_LENGTH];
   char * cmd;
   char curDir[100];

   while (1) {  
       getcwd(curDir, 100);
       printf("%s@%s$ ", getlogin(), curDir);
       if (!fgets(line, MAX_LENGTH, stdin))
           break;

       if ((cmd = strtok(line, DELIMS))) {
           errno = 0;
           if (strcmp(cmd, "cd") == 0) {
              char *arg = strtok(0, DELIMS);

              if (!arg) 
                 fprintf(stderr, "cd: argument is missing.\n");
              else chdir(arg);

           } else if (strcmp(cmd, "exit") == 0) {
                 exit(0);

           } else exec_cmd(line);

        if (errno) perror("Error. Command failure");
        }
   }
return 0;
}

void exec_cmd (char *buf) {
    int status = 0;
    char *argv[MAX_LENGTH];
    int j=0;
    pid_t pid;
    argv[j++] = strtok (buf, DELIMS);
    while (j<MAX_LENGTH && (argv[j++]=strtok(NULL,DELIMS))!=NULL); // EDIT: " " replaced by DELIMS 

    pid = fork();
    if(pid < 0) {
       printf("Error occured");
       exit(-1);
    } else if(pid == 0) {
         execvp(argv[0],argv);
    } else if( pid > 0) {
         wait(&status);
    }
}   

Upvotes: 0

Views: 489

Answers (2)

paxdiablo
paxdiablo

Reputation: 881793

Check the arguments (eg, print them out, each enclosed in []) before you call fork/exec, there's a good chance they're not what you think.

While your first call to strtok uses your full delimiter set, subsequent calls do not. They instead just use a space. That means that the final argument will probably have the newline left on the string by fgets. I'd be using the same delimiter set in the subsequent calls. In other words:

while (j<MAX_LENGTH && (argv[j++]=strtok(NULL,DELIMS))!=NULL);

Having entered your code and done that debugging, I find that the string passed to the function only ever has one word in it. It turns out that's because of the strtok that happened in main to check for cd/exit. That left the nul character at the end of the first word, an effect inherent in the way strtok works.

Probably the quickest fix is to make a copy of the string before the initial strtok in main, then pass that to the function. In other words, use strdup (and, later, free). Now glibc has a strdup but, if you're in an environment that doesn't (it's POSIX rather than ISO), see here.

Upvotes: 1

n. m. could be an AI
n. m. could be an AI

Reputation: 120001

The bug is in main().

You are using strtok to find the first word of the line. But strtok modifies the line, making it actually contain just that word (a NUL terminator is written to the string right after it).

You need to make a copy of the line, or use strtok_s, or do something else to avoid modifying the line.

Upvotes: 1

Related Questions