1

I have the following code:

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

void  passe(char *ligne, char **argv)
{
     while (*ligne != '\0')
     {
          while (*ligne == ' ' || *ligne == '\t' || *ligne == '\n')
               *ligne++ = '\0';
          *argv++ = ligne;
          while (*ligne != '\0' && *ligne != ' ' &&  *ligne != '\t' && *ligne != '\n') 
               ligne++;   
     }
     *argv = '\0';
}

void  executer(char **argv)
{
     pid_t  pid;
     int    status;

     if ((pid=fork()) == 0)
     {
          if (execvp(*argv, argv) < 0) 
          {
               printf("*** ERREUR: exec echoué\n");
               exit(1);
          }
     }
     wait(&status);
}

int  main(void)
{
     char  ligne[1024];
     char  *argv[64];

     while (1)
     {
          printf("Shell -> ");
          scanf("%s",ligne);
          printf("\n");
          passe(ligne, argv);
          if (strcmp(argv[0], "exit") == 0)
               exit(0);
          executer(argv);
     }
     return 0;
}

The programme works fine, however when I parse something like "ps -f" it retunrs "execvp echoué", as the code removes the spaces. What should I exactly do to make it accept comands with options such as ps -f?

1 Answer 1

2

You should use fgets() instead of scanf(). Because fgets does not delete white spaces from your input. scanf function only consumes input until the whitespace. For example if you enter "ps -f" scanf consumes this input as "ps". If you write ps directly to your shell terminal you can face with same exception that is what your programme exception. You can use fgets as stream reader that can be file stream or standard input stream(stdin). I have written code that solves your problem. You can check how you can use.

/* ----------------------------------------------------------------- */
/* PROGRAM  shell.c                                                  */
/*    This program reads in an input line, parses the input line     */
/* into tokens, and use execvp() to execute the command.             */
/* ----------------------------------------------------------------- */

#include  <stdio.h>
#include  <sys/types.h>

/* ----------------------------------------------------------------- */
/* FUNCTION  parse:                                                  */
/*    This function takes an input line and parse it into tokens.    */
/* It first replaces all white spaces with zeros until it hits a     */
/* non-white space character which indicates the beginning of an     */
/* argument.  It saves the address to argv[], and then skips all     */
/* non-white spaces which constitute the argument.                   */
/* ----------------------------------------------------------------- */

void  parse(char *line, char **argv)
{
     while (*line != '\0') {       /* if not the end of line ....... */ 
          while (*line == ' ' || *line == '\t' || *line == '\n')
               *line++ = '\0';     /* replace white spaces with 0    */
          *argv++ = line;          /* save the argument position     */
          while (*line != '\0' && *line != ' ' && 
                 *line != '\t' && *line != '\n') 
               line++;             /* skip the argument until ...    */
     }
     *argv = '\0';                 /* mark the end of argument list  */
}

/* ----------------------------------------------------------------- */
/* FUNCTION execute:                                                 */
/*    This function receives a commend line argument list with the   */
/* first one being a file name followed by its arguments.  Then,     */
/* this function forks a child process to execute the command using  */
/* system call execvp().                                             */
/* ----------------------------------------------------------------- */

void  execute(char **argv)
{
     pid_t  pid;
     int    status;

     if ((pid = fork()) < 0) {     /* fork a child process           */
          printf("*** ERROR: forking child process failed\n");
          exit(1);
     }
     else if (pid == 0) {          /* for the child process:         */
          if (execvp(*argv, argv) < 0) {     /* execute the command  */
               printf("*** ERROR: exec failed\n");
               exit(1);
          }
     }
     else {                                  /* for the parent:      */
          while (wait(&status) != pid)       /* wait for completion  */
               ;
     }
}

/* ----------------------------------------------------------------- */
/*                  The main program starts here                     */
/* ----------------------------------------------------------------- */

void  main(void)
{
     char  line[1024];             /* the input line                 */
     char  *argv[64];              /* the command line argument      */

     while (1) {                   /* repeat until done ....         */
          printf("Shell -> ");     /*   display a prompt             */
          fgets(line,1024,stdin);  /*   read in the command line     */
          int i = strlen(line)-1;
          if( line[ i ] == '\n') 
              line[i] = '\0';
          parse(line, argv);       /*   parse the line               */
          if (strcmp(argv[0], "exit") == 0)  /* is it an "exit"?     */
               exit(0);            /*   exit if it is                */
          execute(argv);           /* otherwise, execute the command */
     }
}

output

Sign up to request clarification or add additional context in comments.

7 Comments

How do I exactly use fgets? I beleive it needs parsing a file?
Using fgets() is extremely bad practice -- it provides no bounds checking, is thus open to serious security bugs -- and most companies won't allow any code that uses it to pass code review for that reason.
@CharlesDuffy I think you should be wrong. Because scanf does not have any bound check. You can use fgets like fgets(argv, 4, stdin). Second argument checks bounds. You can not enter more than 4 character now.
@MelikşahŞimşek, I didn't say that scanf was good practice either. And yes, adding an argument adding bounds checking resolves the concern.
@CharlesDuffy My example that before last edit have bound checking parameter also. I don't understand why you comment bad and vote down?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.