commit ab6da28992f841637998c9e4044e1cc1b6f07f80 Author: Laureηt Date: Tue Apr 20 18:12:44 2021 +0200 v1 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f798eb3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "gcc - Build and debug active file", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "C/C++: gcc build active file", + "miDebuggerPath": "/usr/bin/gdb" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fcac5ac --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "signal.h": "c", + "readcmd.h": "c", + "stdlib.h": "c", + "wait.h": "c" + } +} \ No newline at end of file diff --git a/.vscode/tasks b/.vscode/tasks new file mode 100755 index 0000000..41ca94f Binary files /dev/null and b/.vscode/tasks differ diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4554d97 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build active file", + "command": "/usr/bin/gcc", + "args": [ + "-Wall", + "-g", + "jobs.c", + "readcmd.c", + "minishell.c", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9b3e77 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +CC = gcc +LDFLAGS = -Wall + +all: minishell + +minishell: readcmd.c minishell.c + $(CC) $(LDFLAGS) $^ -o $@ + +clean: + rm *.o minishell + +.PHONY: clean all + +readcmd.o: readcmd.h diff --git a/README.md b/README.md new file mode 100755 index 0000000..489d8b9 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +Introduction +------------ +Le code fourni a pour but de vous décharger du travail d'analyse d'une ligne de commande, +avant son interprétation par le shell. Il propose une fonction `readcmd()` qui fournit +le résultat de cette analyse. + +Le source est fourni non pas à titre d'exemple (il n'est pas d'une clarté éblouissante), +mais à titre de documentation (et de spécification :)) ; il n'est ni + nécessaire, ni conseillé de le modifier pour réaliser le travail demandé : l'analyse + réalisée est suffisante pour la syntaxe des commandes que votre shell doit interpréter. + + Ce code est a priori tout à fait fiable, mais n'hésitez cependant pas à nous signaler + ce qui vous apparaîtrait comme des anomalies, ou des lacunes dans la documentation. + +La structure `cmdline` +---------------------- +L'appel à readcmd() retourne une structure cmdline, qui contient le résultat de l'analyse +de la ligne de commande pour le shell. +Cette structure contient notamment : + +- l'indication du fait que la commande doit être lancée en tâche de fond ou non +- les redirections éventuelles +- la décomposition de la ligne de commande en commandes élémentaires, et la décomposition + de chaque commande en mots. Le champ `seq` référence le résultat de cette décomposition, + qui est vu comme un tableau à deux dimensions [commandes élémentaires, mots de la commande] + + **Pour plus de détails, voir le fichier d'en-tête `readcmd.h`.** + +**Exemples :** + +- "ls -l" : seq[0][0] = "ls", seq[0][1] = "-l", seq[0][2] = NULL, seq[1] = NULL, backgrounded = NULL, in = NULL, out = NULL +- "ls -l > toto" : seq[0][0] = "ls", seq[0][1] = "-l", seq[0][2] = NULL, + seq[1] = NULL, backgrounded = NULL, in = NULL, out => "toto" +- "ls | grep toto | wc -l" : seq[0][0] = "ls", seq[0][1] = NULL, +seq[1][0] = "grep", seq[1][1] = "toto", seq[1][2] = NULL, +seq[2][0] = "wc", seq[0][1] = "-l", seq[0][2] = NULL, +seq[3] = NULL, backgrounded = NULL, in = NULL, out = NULL +- "sleep 100 &" : seq[0][0] = "sleep", seq[0][1] = "20", backgrounded != NULL, in = NULL, out = NULL \ No newline at end of file diff --git a/jobs b/jobs new file mode 100755 index 0000000..fbb8adf Binary files /dev/null and b/jobs differ diff --git a/jobs.c b/jobs.c new file mode 100644 index 0000000..2ea0dd3 --- /dev/null +++ b/jobs.c @@ -0,0 +1,85 @@ +#include +#include +#include "jobs.h" + +void ajouter(list* l_ptr, int pid, int id) { + + cell* new_cell = malloc(sizeof(*new_cell)); + + new_cell->pid = pid; + new_cell->id = id; + + if (*l_ptr == NULL) { + new_cell->next = NULL; + } else { + new_cell->next = *l_ptr; + } + + *l_ptr = new_cell; + + printf("[%d] %d\n", id, pid); + +} + +void supprimer(list* l_ptr, int pid) { + + cell* cursor = *l_ptr; + + if (cursor->pid == pid) { + cell* cursor2free = cursor; + *l_ptr = cursor->next; + free(cursor2free); + } else { + while (cursor->next != NULL) { + if (cursor->next->pid == pid) { + break; + } else { + cursor = cursor->next; + } + } + cell* cursor_next = cursor->next->next; + free(cursor->next); + cursor->next = cursor_next; + } + + + +} + +void afficher(list* l_ptr) { + + cell* cursor = *l_ptr; + + while (cursor != NULL) { + printf("[%d] %d\n", cursor->id, cursor->pid); + cursor = cursor->next; + } + +} + +void initialiser(list* l_ptr) { + *l_ptr = NULL; +} + +void liberer(list* l_ptr) { + free(*l_ptr); + *l_ptr = NULL; +} + +int est_vide(list l_ptr) { + return l_ptr == NULL; +} + +int contiens(list* l_ptr, int pid) { + + cell* cursor = *l_ptr; + + while (cursor != NULL) { + if (cursor->pid == pid) { + return 1; + } + cursor = cursor->next; + } + + return 0; +} \ No newline at end of file diff --git a/jobs.h b/jobs.h new file mode 100644 index 0000000..347c910 --- /dev/null +++ b/jobs.h @@ -0,0 +1,20 @@ +#ifndef __JOBS_H +#define __JOBS_H + +typedef struct cell cell; +struct cell { + int pid; + int id; + cell* next; +}; + +typedef cell* list; + +void ajouter(cell** list, int pid, int id); +void supprimer(cell** list, int pid); +void afficher(cell** list); +void initialiser(list* list); +void liberer(list* list); +int contiens(list* l_ptr, int pid); + +#endif \ No newline at end of file diff --git a/minishell b/minishell new file mode 100755 index 0000000..65982dd Binary files /dev/null and b/minishell differ diff --git a/minishell.c b/minishell.c new file mode 100644 index 0000000..bbfd81d --- /dev/null +++ b/minishell.c @@ -0,0 +1,102 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include "readcmd.h" +#include "jobs.h" + +int prompting = 0; +int job_id = 1; +list jobs; + +void handler_print(int signal_num, siginfo_t* info) { + if (contiens(&jobs, info->si_pid)) { + if (prompting) { printf("\n"); } + printf("[%d] --> (%d) %s\n", info->si_pid, signal_num, strsignal(signal_num)); + supprimer(&jobs, info->si_pid); + job_id--; + } +} + +int main(int argc, char *argv[]) { + + extern int errno; + + initialiser(&jobs); + + struct sigaction action; + action.sa_flags = SA_SIGINFO; //| SA_RESTART; + action.sa_handler = handler_print; + sigemptyset(&action.sa_mask); + sigaction(SIGCHLD, &action, NULL); + + + char initcd[256], currentcd[256]; + getcwd(initcd, 256); + + while (1) { + getcwd(currentcd, 256); + printf("%s >>> ", currentcd); + prompting = 1; + struct cmdline* cmd = readcmd(); + prompting = 0; + + if (cmd == NULL || cmd->seq[0] == NULL) { // ligne vide, on skip + continue; + } else if (!strcmp(cmd->seq[0][0], "exit")) { // on quitte le shell + break; + } else if (!strcmp(cmd->seq[0][0], "cd")) { // cd + if (cmd->seq[0][1] == NULL) { // vide + chdir(initcd); + } else { // avec un path + chdir(cmd->seq[0][1]); + } + continue; + } else if (!strcmp(cmd->seq[0][0], "jobs")) { + afficher(&jobs); + continue; + } + + pid_t pidFils = fork(); + + if (pidFils == -1) { + fprintf(stderr, "ERROR: forking failed, %s\n", strerror(errno)); + exit(errno); + } + + if (pidFils == 0) { // fils + execvp(cmd->seq[0][0], cmd->seq[0]); + exit(errno); // si execlp échoue on exit avec une erreur + + } else { // père + if (cmd->backgrounded) { + ajouter(&jobs, pidFils, job_id++); + } else { + jmp_buf env; + setjmp(env); + int codeTerm; + pid_t idFils = waitpid(pidFils, &codeTerm, 0); // on attend la fin de l'exec du fils + + if (idFils == -1){ + + // todo : peut mieux faire ? + if (errno == 4) { + longjmp(env, 1); + } + + fprintf(stderr, "ERROR: waiting for %d failed, (%d) %s\n", codeTerm, errno, strerror(errno)); + exit(errno); + } + + } + } + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/minishell.pdf b/minishell.pdf new file mode 100644 index 0000000..6309f4a Binary files /dev/null and b/minishell.pdf differ diff --git a/readcmd.c b/readcmd.c new file mode 100755 index 0000000..a9e8926 --- /dev/null +++ b/readcmd.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2002, Simon Nieuviarts + */ + +/* + * Backgrounding added. [PM] Ajout d'une rustine nécessaire : lignes 153 et 293 commentées + */ + +#include +#include +#include +#include +#include +#include +#include "readcmd.h" + + +static void memory_error(void) +{ + errno = ENOMEM; + perror(0); + exit(1); +} + + +static void *xmalloc(size_t size) +{ + void *p = malloc(size); + if (!p) memory_error(); + return p; +} + + +static void *xrealloc(void *ptr, size_t size) +{ + void *p = realloc(ptr, size); + if (!p) memory_error(); + return p; +} + + +/* Read a line from standard input and put it in a char[] */ +static char *readline(void) +{ + size_t buf_len = 16; + char *buf = xmalloc(buf_len * sizeof(char)); + + if (fgets(buf, buf_len, stdin) == NULL) { + free(buf); + return NULL; + } + + do { + size_t l = strlen(buf); + if ((l > 0) && (buf[l-1] == '\n')) { + l--; + buf[l] = 0; + return buf; + } + if (buf_len >= (INT_MAX / 2)) memory_error(); + buf_len *= 2; + buf = xrealloc(buf, buf_len * sizeof(char)); + if (fgets(buf + l, buf_len - l, stdin) == NULL) return buf; + } while (1); +} + + +/* Split the string in words, according to the simple shell grammar. */ +static char **split_in_words(char *line) +{ + char *cur = line; + char **tab = 0; + size_t l = 0; + char c; + + while ((c = *cur) != 0) { + char *w = 0; + char *start; + switch (c) { + case ' ': + case '\t': + /* Ignore any whitespace */ + cur++; + break; + case '<': + w = "<"; + cur++; + break; + case '>': + w = ">"; + cur++; + break; + case '|': + w = "|"; + cur++; + break; + case '&': + w = "&"; + cur++; + break; + default: + /* Another word */ + start = cur; + while (c) { + c = *++cur; + switch (c) { + case 0: + case ' ': + case '\t': + case '<': + case '>': + case '|': + case '&': + c = 0; + break; + default: ; + } + } + w = xmalloc((cur - start + 1) * sizeof(char)); + strncpy(w, start, cur - start); + w[cur - start] = 0; + } + if (w) { + tab = xrealloc(tab, (l + 1) * sizeof(char *)); + tab[l++] = w; + } + } + tab = xrealloc(tab, (l + 1) * sizeof(char *)); + tab[l++] = 0; + return tab; +} + + +static void freeseq(char ***seq) +{ + int i, j; + + for (i=0; seq[i]!=0; i++) { + char **cmd = seq[i]; + + for (j=0; cmd[j]!=0; j++) free(cmd[j]); + free(cmd); + } + free(seq); +} + + +/* Free the fields of the structure but not the structure itself */ +static void freecmd(struct cmdline *s) +{ + if (s->in) free(s->in); + if (s->out) free(s->out); +// if (s->backgrounded) free(s->backgrounded); + if (s->seq) freeseq(s->seq); +} + + +struct cmdline *readcmd(void) +{ + static struct cmdline *static_cmdline = 0; + struct cmdline *s = static_cmdline; + char *line; + char **words; + int i; + char *w; + char **cmd; + char ***seq; + size_t cmd_len, seq_len; + + line = readline(); + if (line == NULL) { + if (s) { + freecmd(s); + free(s); + } + return static_cmdline = 0; + } + + cmd = xmalloc(sizeof(char *)); + cmd[0] = 0; + cmd_len = 0; + seq = xmalloc(sizeof(char **)); + seq[0] = 0; + seq_len = 0; + + words = split_in_words(line); + free(line); + + if (!s) + static_cmdline = s = xmalloc(sizeof(struct cmdline)); + else + freecmd(s); + s->err = 0; + s->in = 0; + s->out = 0; + s->backgrounded = 0; + s->seq = 0; + + i = 0; + while ((w = words[i++]) != 0) { + switch (w[0]) { + case '&': + if(s->backgrounded){ + s->err = "error on &"; + goto error; + } + s->backgrounded = &w[0]; + break; + case '<': + /* Tricky : the word can only be "<" */ + if (s->in) { + s->err = "only one input file supported"; + goto error; + } + if (words[i] == 0) { + s->err = "filename missing for input redirection"; + goto error; + } + s->in = words[i++]; + break; + case '>': + /* Tricky : the word can only be ">" */ + if (s->out) { + s->err = "only one output file supported"; + goto error; + } + if (words[i] == 0) { + s->err = "filename missing for output redirection"; + goto error; + } + s->out = words[i++]; + break; + case '|': + /* Tricky : the word can only be "|" */ + if (cmd_len == 0) { + s->err = "misplaced pipe"; + goto error; + } + + seq = xrealloc(seq, (seq_len + 2) * sizeof(char **)); + seq[seq_len++] = cmd; + seq[seq_len] = 0; + + cmd = xmalloc(sizeof(char *)); + cmd[0] = 0; + cmd_len = 0; + break; + default: + cmd = xrealloc(cmd, (cmd_len + 2) * sizeof(char *)); + cmd[cmd_len++] = w; + cmd[cmd_len] = 0; + } + } + + if (cmd_len != 0) { + seq = xrealloc(seq, (seq_len + 2) * sizeof(char **)); + seq[seq_len++] = cmd; + seq[seq_len] = 0; + } else if (seq_len != 0) { + s->err = "misplaced pipe"; + i--; + goto error; + } else + free(cmd); + free(words); + s->seq = seq; + return s; +error: + while ((w = words[i++]) != 0) { + switch (w[0]) { + case '<': + case '>': + case '|': + case '&': + break; + default: + free(w); + } + } + free(words); + freeseq(seq); + for (i=0; cmd[i]!=0; i++) free(cmd[i]); + free(cmd); + if (s->in) { + free(s->in); + s->in = 0; + } + if (s->out) { + free(s->out); + s->out = 0; + } + if (s->backgrounded) { +// free(s->backgrounded); + s->out = 0; + } + return s; +} diff --git a/readcmd.h b/readcmd.h new file mode 100755 index 0000000..75b5bbd --- /dev/null +++ b/readcmd.h @@ -0,0 +1,40 @@ +#ifndef __READCMD_H +#define __READCMD_H + +/* Lit une ligne de commande depuis l'entrée standard. + * Remarque : + * Dans le cas où suffisamment de mémoire ne peut être allouée pour le résultat, + * affiche un message d'erreur et appelle exit(). + */ + +struct cmdline *readcmd(void); +/* Structure retournée par readcmd() + * Remarques utiles : + * - readcmd() peut retourner null, dans le cas où le processus en attente sur readcmd reçoit un signal + * - en cas d'appels successifs, + * readcmd() réutilise la mémoire allouée à la structure cmdline qu'elle retourne + */ +struct cmdline { + char *err; /* Si non null : message d'erreur à afficher. + * Dans ce cas, les autres champs sont nuls. */ + char *in; /* Si non null : nom du fichier vers lequel l'entrée doit être redirigée. */ + char *out; /* Si non null : nom du fichier vers lequel la sortie doit être redirigée. */ + char *backgrounded; /* Si non null : commande en tâche de fond */ + char ***seq; /* Une ligne de commande est une suite de commandes liées par des tubes + * Ainsi, + * - une commande est un tableau de chaînes de caractères (char **). + * Chaque élément de ce tableau est en effet une chaîne (char *) + * correspondant à un argument de la commande. + * Le dernier pointeur du tableau est suivi d'un pointeur null, qui + * marque la fin du tableau. + * - seq est un tableau de commandes (char ***), chaque commande de ce + * tableau correspondant à une commande élémentaire du pipeline. + * Le dernier élément (char **) du tableau est ici aussi suivi d'un pointeur null. + * - Dans le cas particulier où une ligne vide est saisie, le pointeur référencé par + * seq vaut directement NULL : puisque le tableau n'a aucun élément, le marqueur + * de fin de tableau se trouve référencé par seq, ce que l'on peut écrire + * (*seq == NULL) + * (ou (seq[0] == NULL), mais cela peut prêter à confusion, seq[0] n'existant pas) + */ +}; +#endif diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..f8fc522 --- /dev/null +++ b/test.sh @@ -0,0 +1,2 @@ +sleep 5 +echo bonjourr