v1
This commit is contained in:
commit
ab6da28992
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"signal.h": "c",
|
||||||
|
"readcmd.h": "c",
|
||||||
|
"stdlib.h": "c",
|
||||||
|
"wait.h": "c"
|
||||||
|
}
|
||||||
|
}
|
BIN
.vscode/tasks
vendored
Executable file
BIN
.vscode/tasks
vendored
Executable file
Binary file not shown.
30
.vscode/tasks.json
vendored
Normal file
30
.vscode/tasks.json
vendored
Normal file
|
@ -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"
|
||||||
|
}
|
14
Makefile
Normal file
14
Makefile
Normal file
|
@ -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
|
38
README.md
Executable file
38
README.md
Executable file
|
@ -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
|
85
jobs.c
Normal file
85
jobs.c
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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;
|
||||||
|
}
|
20
jobs.h
Normal file
20
jobs.h
Normal file
|
@ -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
|
102
minishell.c
Normal file
102
minishell.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#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;
|
||||||
|
}
|
BIN
minishell.pdf
Normal file
BIN
minishell.pdf
Normal file
Binary file not shown.
297
readcmd.c
Executable file
297
readcmd.c
Executable file
|
@ -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 <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
40
readcmd.h
Executable file
40
readcmd.h
Executable file
|
@ -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
|
Loading…
Reference in a new issue