/* minish2.c - esimerkki minimalistisesta shellistä * * skriptin nimen voi antaa argumenttina, muuten lukee stdiniä * ymmärtää seuraavanlaista komentokieltä: * (1) välilyönnit rivin alussa ohitetaan * (2) # rivin alussa on kommenttimerkki * (3) & rivin lopussa tarkoittaa tausta-ajoa * (4) yksi sisäinen komento, 'exit' (mahd. argumenttina status) * (5) etsii ulkoisia komentoja PATHista (muttei osaa muuttaa PATHia) */ #include #include #include #include #include #include #define MAXLINE 300 int main(int argc, char **argv) { char line[MAXLINE+1]; /* suoritettava komentorivi */ char *args[MAXLINE]; /* osoittimet argumenttien alkuun */ char *p, **arg; /* osoittimia em. taulukoihin */ int bg; /* tausta-ajolippu */ int pid; /* lapsiprosessin PID */ /* yritetään avata argumentti tiedostona stdinin paikalle */ if (argc>=2 && !freopen(argv[1], "r", stdin)) { perror(argv[1]); exit(errno); } /* luetaan rivejä kunnes tulee EOF */ while (fgets(line, MAXLINE, stdin)) { /* siivotaan mahdollisesti päättyneet taustaprosessit pois */ waitpid(-1, NULL, WNOHANG); /* tarkistetaan että saatiin \n */ if ((p = strchr(line, '\n'))) { *p=0; /* poistetaan se */ } else { /* ei saatu, rivi oli liian pitkä */ fprintf(stderr, "line too long\n"); exit(1); } /* poistetaan rivin lopussa mahdollisesti oleva & */ /* ja talletetaan tieto sen olemassaolosta bg:hen */ /* huom. &:n jäljessä ei saa olla välilyöntejä... */ if ((bg = (*--p == '&'))) *p=0; /* ohitetaan alkutyhjät */ p = line; while (*p == ' ') ++p; /* jos seuraava merkki on # rivi tulkitaan kommentiksi */ if (*p == '#') continue; /* p osoittaa nyt ensimmäisen sanan alkuun, */ /* talletaan osoitin siihen args[0]:aan */ *(arg=args) = p; /* etsitään välilyöntejä */ while ((p = strchr(p, ' '))) { /* nollataan kaikki peräkkäiset välilyönnit */ while (*p == ' ') *p++ = 0; /* jos perässä on ei-välilyönti, tallennetaan */ /* osoitin siihen args-taulukon seuraavaan paikkaan */ if (*p) *++arg=p; } /* args-taulukon loppuun tarvitaan NULL execv:tä varten */ *++arg=NULL; /* jos komento on "exit" lopetetaan */ if (!strcmp(line,"exit")) { exit(args[1] ? atoi(args[1]) : 0); } /* käynnistetään lapsiprosessi */ switch ((pid=fork())) { case 0: /* nyt ollaan lapsiprosessissa */ /* yritetään suorittaa annettu komento */ execvp(args[0], args); /* ei palaa jos onnistuu */ perror(args[0]); exit(errno); case -1: /* muisti lopussa tms */ perror("fork() failed"); break; default: /* odotetaan lapsiprosessin päättymistä, paitsi */ /* jos se haluttiin jättää taustalle */ if (!bg) waitpid(pid, NULL, 0); } } exit(0); } /******************************LOPPU******************************/