Unix System Programming

Matty2d

Weaksauce
Joined
Mar 30, 2003
Messages
83
I have made a simple shell (right not it doesn't do everything i want it to, but it interprets the dir dos command in unix...and it will do the same for copy and a few more soon) but, n e wayz, i just want to know why after i execute a command i have to hit enter again to get the prompt. I have no idea why, could someone take a look @ my code, it's short and simple.

Code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[])
{

int status;
pid_t  pid;
char command[BUFSIZ];

//Convert Unix ls to Dos dir
const char *s1 = "dir";
const char *s2 = "ls";


while(1){
printf("Matt-shell> ");
if (fgets(command, sizeof(command), stdin) == NULL) {
putchar('\n');
exit(0);
}

command[strlen(command)-1] = NULL;


if((pid = fork()) == 0){ 
  
   if(strcmp(command,s1) == 0)
   {
     execlp(s2,s2,0); // Executes Dos Interpreted Commands
    
   }
  system(command); // Executes everything else


while (wait(&status) != pid ) 
     continue;
putchar('\n');

}
}

}


Thanks Matt.D
 
I think the return key is being read in the with the command... if you could read in only the command and leave the return keystroke in the buffer that would solve the problem, I think.
 
Code:
ameoba@girl:~/sh$ ./mattsh 
Matt-shell> dir
Matt-shell> 1  2  3  4  5  mattsh  sh.c

Matt-shell>

The problem is that the shell line is printing before the output of the command. It's even more obvious if you do a command like " sleep 5; echo foo". Why is this happening?


....because you have the wait() inside the if(fork...) bit; the child process, not the parent, is waiting. You'd see this if you had your code properly formatted.
 
reformat with that handy indent command
used flags "-i4 -sc -bli0 -bl -cbi0 -nut -npsl"
Code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main (int argc, char *argv[])
{

    int status;
    pid_t pid;
    char command[BUFSIZ];

//Convert Unix ls to Dos dir
    const char *s1 = "dir";
    const char *s2 = "ls";


    while (1)
    {
        printf ("Matt-shell> ");
        if (fgets (command, sizeof (command), stdin) == NULL)
        {
            putchar ('\n');
            exit (0);
        }

        command[strlen (command) - 1] = NULL;


        if ((pid = fork ()) == 0)
        {

            if (strcmp (command, s1) == 0)
            {
                execlp (s2, s2, 0);     // Executes Dos Interpreted Commands

            }
            system (command);   // Executes everything else


            [color=red]while (wait (&status) != pid)
                continue;[/color]
            putchar ('\n');

        }
    }

}

The part highlighted in red is what ameoba was talking about
 
Wow. Is this really even necessary? Why not just use the alias directive within your .profile?
 
rael said:
Wow. Is this really even necessary? Why not just use the alias directive within your .profile?
I think he is trying to learn how to actually code his own shell.
 
I have jazzed up the code and it works now, there is just one more problem, i got dir to work fine because it is a single command. When i have to type something like this: copy filename location , in the program is has to be interpruted as cp filename location in order for it to run in the system call system() (and for unix to actually execute it). I have tried string tokenizer but it is not working, i have also tried to read from argv[] array. (i'm thinking the only way is to parse it) Is there any other method into in doing that.

what i need is:
copy filename location --> cp filename location
delete filename --> rm filename


Thanks Matty.D
 
Parse it and call exec[...]() yourself, instead of using
system() to do all the work. Writing a shell is a fantastic
way to learn about unix's inner workings - I wrote one when
I was in college, then a better one when I got out, which
I actually used as my main shell for several years.
Parsing is really really important. It's a very good thing
to learn about.
 
command[strlen (command) - 1] = NULL;


This is a pet peeve of mine, so bear with me. This line is competely unnecessary. More than that, by having that line there, you illustrate that you have no idea how strings are actually represented in C. Or what the strlen(3) function actually does.

You're probably thinking to yourself "This guy has no idea what he's talking about. C strings have to be NULL terminated." You're correct on the latter half. C strings DO have to be NULL terminated. That is the only way to tell when you've reached the end of one. However, I'd like you to consider how strlen() could possibly know how long a string actually is without knowing that it will end with a NULL?

If you don't believe that your collection of characters has a null at the end of it, then I highly suggest you don't run any of the string functions (strlen, strcpy, strcat, etc etc...) on them. That would result in a crash or bizarre behaviour.

If this is from a university class and the professor told you to do that, then slap him with a trout.
 
ameoba said:
I think he's trying to strip a newline...
the newline char happens before the null terminator. If he wants to strip a new line, I am sure there are other ways of going about it, but I dont know what they are.
 
Oh look at that... My bad :). I've seen so many people try to add the \0 at the end of things unnecessarily that I completely missed that.

Sorry about that.
 
Back
Top